浏览代码

Add stroke generation to SVGLoader

yomboprime 6 年之前
父节点
当前提交
a4b1b6df12

+ 1 - 0
examples/files.js

@@ -44,6 +44,7 @@ var files = {
 		"webgl_geometry_terrain_raycast",
 		"webgl_geometry_text",
 		"webgl_geometry_text_shapes",
+		"webgl_geometry_text_stroke",
 		"webgl_hdr",
 		"webgl_helpers",
 		"webgl_interactive_buffergeometry",

+ 869 - 30
examples/js/loaders/SVGLoader.js

@@ -56,37 +56,37 @@ THREE.SVGLoader.prototype = {
 
 				case 'path':
 					style = parseStyle( node, style );
-					if ( node.hasAttribute( 'd' ) && isVisible( style ) ) path = parsePathNode( node, style );
+					if ( node.hasAttribute( 'd' ) ) path = parsePathNode( node, style );
 					break;
 
 				case 'rect':
 					style = parseStyle( node, style );
-					if ( isVisible( style ) ) path = parseRectNode( node, style );
+					path = parseRectNode( node, style );
 					break;
 
 				case 'polygon':
 					style = parseStyle( node, style );
-					if ( isVisible( style ) ) path = parsePolygonNode( node, style );
+					path = parsePolygonNode( node, style );
 					break;
 
 				case 'polyline':
 					style = parseStyle( node, style );
-					if ( isVisible( style ) ) path = parsePolylineNode( node, style );
+					path = parsePolylineNode( node, style );
 					break;
 
 				case 'circle':
 					style = parseStyle( node, style );
-					if ( isVisible( style ) ) path = parseCircleNode( node, style );
+					path = parseCircleNode( node, style );
 					break;
 
 				case 'ellipse':
 					style = parseStyle( node, style );
-					if ( isVisible( style ) ) path = parseEllipseNode( node, style );
+					path = parseEllipseNode( node, style );
 					break;
 
 				case 'line':
 					style = parseStyle( node, style );
-					if ( isVisible( style ) ) path = parseLineNode( node, style );
+					path = parseLineNode( node, style );
 					break;
 
 				default:
@@ -96,9 +96,16 @@ THREE.SVGLoader.prototype = {
 
 			if ( path ) {
 
+				if ( style.fill !== undefined && style.fill !== 'none' ) {
+
+					path.color.setStyle( style.fill );
+
+				}
+
 				transformPath( path, currentTransform );
 
 				paths.push( path );
+
 				path.userData = { node: node, style: style };
 
 			}
@@ -122,7 +129,6 @@ THREE.SVGLoader.prototype = {
 		function parsePathNode( node, style ) {
 
 			var path = new THREE.ShapePath();
-			path.color.setStyle( style.fill );
 
 			var point = new THREE.Vector2();
 			var control = new THREE.Vector2();
@@ -545,7 +551,6 @@ THREE.SVGLoader.prototype = {
 			var h = parseFloat( node.getAttribute( 'height' ) );
 
 			var path = new THREE.ShapePath();
-			path.color.setStyle( style.fill );
 			path.moveTo( x + 2 * rx, y );
 			path.lineTo( x + w - 2 * rx, y );
 			if ( rx !== 0 || ry !== 0 ) path.bezierCurveTo( x + w, y, x + w, y, x + w, y + 2 * ry );
@@ -591,7 +596,6 @@ THREE.SVGLoader.prototype = {
 			var regex = /(-?[\d\.?]+)[,|\s](-?[\d\.?]+)/g;
 
 			var path = new THREE.ShapePath();
-			path.color.setStyle( style.fill );
 
 			var index = 0;
 
@@ -623,7 +627,6 @@ THREE.SVGLoader.prototype = {
 			var regex = /(-?[\d\.?]+)[,|\s](-?[\d\.?]+)/g;
 
 			var path = new THREE.ShapePath();
-			path.color.setStyle( style.fill );
 
 			var index = 0;
 
@@ -645,7 +648,6 @@ THREE.SVGLoader.prototype = {
 			subpath.absarc( x, y, r, 0, Math.PI * 2 );
 
 			var path = new THREE.ShapePath();
-			path.color.setStyle( style.fill );
 			path.subPaths.push( subpath );
 
 			return path;
@@ -663,7 +665,6 @@ THREE.SVGLoader.prototype = {
 			subpath.absellipse( x, y, rx, ry, 0, Math.PI * 2 );
 
 			var path = new THREE.ShapePath();
-			path.color.setStyle( style.fill );
 			path.subPaths.push( subpath );
 
 			return path;
@@ -692,19 +693,37 @@ THREE.SVGLoader.prototype = {
 
 			style = Object.assign( {}, style ); // clone style
 
-			if ( node.hasAttribute( 'fill' ) ) style.fill = node.getAttribute( 'fill' );
-			if ( node.style.fill !== '' ) style.fill = node.style.fill;
+			function addStyle( svgName, jsName, adjustFunction ) {
 
-			if ( node.hasAttribute( 'fill-opacity' ) ) style.fillOpacity = node.getAttribute( 'fill-opacity' );
-			if ( node.style.fillOpacity !== '' ) style.fillOpacity = node.style.fillOpacity;
+				if ( adjustFunction === undefined ) adjustFunction = function copy( v ) { return v; };
 
-			return style;
+				if ( node.hasAttribute( svgName ) ) style[ jsName ] = adjustFunction( node.getAttribute( svgName ) );
+				if ( node.style[ svgName ] !== '' ) style[ jsName ] = adjustFunction( node.style[ svgName ] );
 
-		}
+			}
+
+			function clamp( v ) {
+
+				return Math.max( 0, Math.min( 1, v ) );
+
+			}
 
-		function isVisible( style ) {
+			function positive( v ) {
 
-			return style.fill !== 'none' && style.fill !== 'transparent';
+				return Math.max( 0, v );
+
+			}
+
+			addStyle( 'fill', 'fill' );
+			addStyle( 'fill-opacity', 'fillOpacity', clamp );
+			addStyle( 'stroke', 'stroke' );
+			addStyle( 'stroke-opacity', 'strokeOpacity', clamp );
+			addStyle( 'stroke-width', 'strokeWidth', positive );
+			addStyle( 'stroke-linejoin', 'strokeLineJoin' );
+			addStyle( 'stroke-linecap', 'strokeLineCap' );
+			addStyle( 'stroke-miterlimit', 'strokeMiterLimit', positive );
+
+			return style;
 
 		}
 
@@ -754,7 +773,7 @@ THREE.SVGLoader.prototype = {
 				return null;
 			}
 
-			var transform = parseTransformNode( node );
+			var transform = parseNodeTransform( node );
 
 			if ( transform ) {
 
@@ -771,13 +790,13 @@ THREE.SVGLoader.prototype = {
 
 		}
 
-		function parseTransformNode( node ) {
+		function parseNodeTransform( node ) {
 
 			var transform = new THREE.Matrix3();
 			var currentTransform = tempTransform0;
 			var transformsTexts = node.getAttribute( 'transform' ).split( ' ' );
 
-			for ( var tIndex = transformsTexts.length - 1; tIndex >= 0; tIndex-- ) {
+			for ( var tIndex = transformsTexts.length - 1; tIndex >= 0; tIndex -- ) {
 
 				var transformText = transformsTexts[ tIndex ];
 				var openParPos = transformText.indexOf( "(" );
@@ -924,9 +943,6 @@ THREE.SVGLoader.prototype = {
 
 			var isRotated = isTransformRotated( m );
 
-			var tempV2 = new THREE.Vector2();
-			var tempV3 = new THREE.Vector3();
-
 			var subPaths = path.subPaths;
 
 			for ( var i = 0, n = subPaths.length; i < n; i++ ) {
@@ -1004,9 +1020,13 @@ THREE.SVGLoader.prototype = {
 		var tempTransform1 = new THREE.Matrix3();
 		var tempTransform2 = new THREE.Matrix3();
 		var tempTransform3 = new THREE.Matrix3();
+		var tempV2 = new THREE.Vector2();
+		var tempV3 = new THREE.Vector3();
 
 		var currentTransform = new THREE.Matrix3();
 
+		var scope = this;
+
 		console.time( 'THREE.SVGLoader: DOMParser' );
 
 		var xml = new DOMParser().parseFromString( text, 'image/svg+xml' ); // application/xml
@@ -1015,8 +1035,16 @@ THREE.SVGLoader.prototype = {
 
 		console.time( 'THREE.SVGLoader: Parse' );
 
-		parseNode( xml.documentElement, { fill: '#000' } );
-		
+		parseNode( xml.documentElement, {
+			fill: '#000',
+			fillOpacity: 1,
+			strokeOpacity: 1,
+			strokeWidth: 1,
+			strokeLineJoin: 'miter',
+			strokeLineCap: 'butt',
+			strokeMiterLimit: 4
+		} );
+
 		var data = { paths: paths, xml: xml.documentElement };
 
 		// console.log( paths );
@@ -1024,9 +1052,820 @@ THREE.SVGLoader.prototype = {
 
 		console.timeEnd( 'THREE.SVGLoader: Parse' );
 
-
 		return data;
 
 	}
 
 };
+
+THREE.SVGLoader.getStrokeStyle = function ( width, color, opacity, lineJoin, lineCap,  miterLimit ) {
+
+	// Param width: Stroke width
+	// Param color: As returned by THREE.Color.getStyle()
+	// Param opacity: 0 (transparent) to 1 (opaque)
+	// Param lineJoin: One of "round", "bevel", "miter" or "miter-limit"
+	// Param lineCap: One of "round", "square" or "butt"
+	// Param miterLimit: Maximum join length, in multiples of the "width" parameter (join is truncated if it exceeds that distance)
+	// Returns style object
+
+	width = width !== undefined ? width : 1;
+	color = color !== undefined ? color : '#000';
+	opacity = opacity !== undefined ? opacity : 1;
+	lineJoin = lineJoin !== undefined ? lineJoin : 'miter';
+	lineCap = lineCap !== undefined ? lineCap : 'butt';
+	miterLimit = miterLimit !== undefined ? miterLimit : 4;
+
+	return {
+		strokeColor: color,
+		strokeWidth: width,
+		strokeLineJoin: lineJoin,
+		strokeLineCap: lineCap,
+		strokeMiterLimit: miterLimit
+	};
+
+};
+
+THREE.SVGLoader.pointsToStroke = function ( points, style, arcDivisions, minDistance ) {
+
+	// Generates a stroke with some witdh around the given path.
+	// The path can be open or closed (last point equals to first point)
+	// Param points: Array of Vector2D (the path). Minimum 2 points.
+	// Param style: Object with SVG properties as returned by SVGLoader.getStrokeStyle(), or SVGLoader.parse() in the path.userData.style object
+	// Params arcDivisions: Arc divisions for round joins and endcaps. (Optional)
+	// Param minDistance: Points closer to this distance will be merged. (Optional)
+	// Returns BufferGeometry with stroke triangles (In plane z = 0). UV coordinates are generated ('u' along path. 'v' across it, from left to right)
+
+	var vertices = [];
+	var normals = [];
+	var uvs = [];
+
+	if ( THREE.SVGLoader.pointsToStrokeWithBuffers( points, style, arcDivisions, minDistance, vertices, normals, uvs ) === 0 ) {
+
+		return null;
+
+	}
+
+	var geometry = new THREE.BufferGeometry();
+	geometry.addAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) );
+	geometry.addAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ) );
+	geometry.addAttribute( 'uv', new THREE.Float32BufferAttribute( uvs, 2 ) );
+
+	return geometry;
+
+};
+
+THREE.SVGLoader.pointsToStrokeWithBuffers = function () {
+
+	var tempV2_1 = new THREE.Vector2();
+	var tempV2_2 = new THREE.Vector2();
+	var tempV2_3 = new THREE.Vector2();
+	var tempV2_4 = new THREE.Vector2();
+	var tempV2_5 = new THREE.Vector2();
+	var tempV2_6 = new THREE.Vector2();
+	var tempV2_7 = new THREE.Vector2();
+	var tempV3_1 = new THREE.Vector3();
+	var lastPointL = new THREE.Vector2();
+	var lastPointR = new THREE.Vector2();
+	var point0L = new THREE.Vector2();
+	var point0R = new THREE.Vector2();
+	var currentPointL = new THREE.Vector2();
+	var currentPointR = new THREE.Vector2();
+	var nextPointL = new THREE.Vector2();
+	var nextPointR = new THREE.Vector2();
+	var innerPoint = new THREE.Vector2();
+	var outerPoint = new THREE.Vector2();
+	var tempTransform0 = new THREE.Matrix3();
+	var tempTransform1 = new THREE.Matrix3();
+	var tempTransform2 = new THREE.Matrix3();
+
+	return function ( points, style, arcDivisions, minDistance, vertices, normals, uvs, vertexOffset ) {
+
+		// This function can be called to update existing arrays or buffers.
+		// Accepts same parameters as pointsToStroke, plus the buffers and optional offset.
+		// Param vertexOffset: Offset vertices to start writing in the buffers (3 elements/vertex for vertices and normals, and 2 elements/vertex for uvs)
+		// Returns number of written vertices / normals / uvs
+		// if 'vertices' parameter is undefined no triangles will be generated, but the returned vertices count will still be valid (useful to preallocate the buffers)
+
+		arcLengthDivisions = arcDivisions !== undefined ? arcDivisions : 12;
+		minDistance = minDistance !== undefined ? minDistance : 0.001;
+		vertexOffset = vertexOffset !== undefined ? vertexOffset : 0;
+
+		// First ensure there are no duplicated points
+		points = removeDuplicatedPoints( points );
+
+		var numPoints = points.length;
+
+		if ( numPoints < 2 ) return 0;
+
+		var isClosed = points[ 0 ].equals( points[ numPoints - 1 ] );
+
+		var currentPoint;
+		var previousPoint = points[ 0 ];
+		var nextPoint;
+
+		var strokeWidth2 = style.strokeWidth / 2;
+
+		var deltaU = 1 / ( numPoints - 1 );
+		var u0 = 0;
+
+		var innerSideModified;
+		var joinIsOnLeftSide;
+		var isMiter;
+		var initialJoinIsOnLeftSide = false;
+
+		var numVertices = 0;
+		var currentCoordinate = vertexOffset * 3;
+		var currentCoordinateUV = vertexOffset * 2;
+
+		// Get initial left and right stroke points
+		getNormal( points[ 0 ], points[ 1 ], tempV2_1 ).multiplyScalar( strokeWidth2 );
+		lastPointL.copy( points[ 0 ] ).sub( tempV2_1 );
+		lastPointR.copy( points[ 0 ] ).add( tempV2_1 );
+		point0L.copy( lastPointL );
+		point0R.copy( lastPointR );
+
+		for ( var iPoint = 1; iPoint < numPoints; iPoint ++ ) {
+
+			currentPoint = points[ iPoint ];
+
+			// Get next point
+			if ( iPoint === numPoints - 1 ) {
+
+				if ( isClosed ) {
+
+					// Skip duplicated initial point
+					nextPoint = points[ 1 ];
+
+				}
+				else nextPoint = undefined;
+
+			}
+			else {
+
+				nextPoint = points[ iPoint + 1 ];
+
+			}
+
+			// Normal of previous segment in tempV2_1
+			var normal1 = tempV2_1;
+			getNormal( previousPoint, currentPoint, normal1 );
+
+			tempV2_3.copy( normal1 ).multiplyScalar( strokeWidth2 );
+			currentPointL.copy( currentPoint ).sub( tempV2_3 );
+			currentPointR.copy( currentPoint ).add( tempV2_3 );
+
+			var u1 = u0 + deltaU;
+
+			innerSideModified = false;
+
+			if ( nextPoint !== undefined ) {
+
+				// Normal of next segment in tempV2_2
+				getNormal( currentPoint, nextPoint, tempV2_2 );
+
+				tempV2_3.copy( tempV2_2 ).multiplyScalar( strokeWidth2 );
+				nextPointL.copy( currentPoint ).sub( tempV2_3 );
+				nextPointR.copy( currentPoint ).add( tempV2_3 );
+
+				joinIsOnLeftSide = true;
+				tempV2_3.subVectors( nextPoint, previousPoint );
+				if ( normal1.dot( tempV2_3 ) < 0 ) {
+
+					joinIsOnLeftSide = false;
+
+				}
+				if ( iPoint === 1 ) initialJoinIsOnLeftSide = joinIsOnLeftSide;
+
+				tempV2_3.subVectors( nextPoint, currentPoint )
+				var maxInnerDistance = tempV2_3.normalize();
+				var dot = Math.abs( normal1.dot( tempV2_3 ) );
+
+				// If path is straight, don't create join
+				if ( dot !== 0 ) {
+
+					// Compute inner and outer segment intersections
+					var miterSide = strokeWidth2 / dot;
+					tempV2_3.multiplyScalar( - miterSide );
+					tempV2_4.subVectors( currentPoint, previousPoint );
+					tempV2_5.copy( tempV2_4 ).setLength( miterSide ).add( tempV2_3 );
+					innerPoint.copy( tempV2_5 ).negate();
+					var miterLength2 = tempV2_5.length();
+					var segmentLengthPrev = tempV2_4.length();
+					tempV2_4.divideScalar( segmentLengthPrev );
+					tempV2_6.subVectors( nextPoint, currentPoint );
+					var segmentLengthNext = tempV2_6.length();
+					tempV2_6.divideScalar( segmentLengthNext );
+					// Check that previous and next segments doesn't overlap with the innerPoint of intersection
+					if ( tempV2_4.dot( innerPoint ) < segmentLengthPrev && tempV2_6.dot( innerPoint ) < segmentLengthNext ) {
+
+						innerSideModified = true;
+
+					}
+					outerPoint.copy( tempV2_5 ).add( currentPoint );
+					innerPoint.add( currentPoint );
+
+					isMiter = false;
+
+					if ( innerSideModified ) {
+
+						if ( joinIsOnLeftSide ) {
+
+							nextPointR.copy( innerPoint );
+							currentPointR.copy( innerPoint );
+
+						}
+						else {
+
+							nextPointL.copy( innerPoint );
+							currentPointL.copy( innerPoint );
+
+						}
+
+					}
+					else {
+
+						// The segment triangles are generated here if there was overlapping
+
+						makeSegmentTriangles();
+
+					}
+
+					switch ( style.strokeLineJoin ) {
+
+						case 'bevel':
+
+							makeSegmentWithBevelJoin( joinIsOnLeftSide, innerSideModified, u1 );
+
+							break;
+
+						case 'round':
+
+							// Segment triangles
+
+							createSegmentTrianglesWithMiddleSection( joinIsOnLeftSide, innerSideModified );
+
+							// Join triangles
+
+							if ( joinIsOnLeftSide ) {
+
+								makeCircularSector( currentPoint, currentPointL, nextPointL, u1, 0 );
+
+							}
+							else {
+
+								makeCircularSector( currentPoint, nextPointR, currentPointR, u1, 1 );
+
+							}
+
+							break;
+
+						case 'miter':
+						case 'miter-clip':
+						default:
+
+							var miterFraction = ( strokeWidth2 * style.strokeMiterLimit ) / miterLength2;
+
+							if ( miterFraction < 1 ) {
+
+								// The join miter length exceeds the miter limit
+
+								if ( style.strokeLineJoin !== 'miter-clip' ) {
+
+									makeSegmentWithBevelJoin( joinIsOnLeftSide, innerSideModified, u1 );
+									break;
+
+								}
+								else {
+
+									// Segment triangles
+
+									createSegmentTrianglesWithMiddleSection( joinIsOnLeftSide, innerSideModified );
+
+									// Miter-clip join triangles
+
+									if ( joinIsOnLeftSide ) {
+
+										tempV2_6.subVectors( outerPoint, currentPointL ).multiplyScalar( miterFraction ).add( currentPointL );
+										tempV2_7.subVectors( outerPoint, nextPointL ).multiplyScalar( miterFraction ).add( nextPointL );
+
+										addVertex( currentPointL, u1, 0 );
+										addVertex( tempV2_6, u1, 0 );
+										addVertex( currentPoint, u1, 0.5 );
+
+										addVertex( currentPoint, u1, 0.5 );
+										addVertex( tempV2_6, u1, 0 );
+										addVertex( tempV2_7, u1, 0 );
+
+										addVertex( currentPoint, u1, 0.5 );
+										addVertex( tempV2_7, u1, 0 );
+										addVertex( nextPointL, u1, 0 );
+
+									}
+									else {
+
+										tempV2_6.subVectors( outerPoint, currentPointR ).multiplyScalar( miterFraction ).add( currentPointR );
+										tempV2_7.subVectors( outerPoint, nextPointR ).multiplyScalar( miterFraction ).add( nextPointR );
+
+										addVertex( currentPointR, u1, 1 );
+										addVertex( tempV2_6, u1, 1 );
+										addVertex( currentPoint, u1, 0.5 );
+
+										addVertex( currentPoint, u1, 0.5 );
+										addVertex( tempV2_6, u1, 1 );
+										addVertex( tempV2_7, u1, 1 );
+
+										addVertex( currentPoint, u1, 0.5 );
+										addVertex( tempV2_7, u1, 1 );
+										addVertex( nextPointR, u1, 1 );
+
+									}
+
+								}
+
+							}
+							else {
+
+								// Miter join segment triangles
+
+								if ( innerSideModified ) {
+
+									// Optimized segment + join triangles
+
+									if ( joinIsOnLeftSide ) {
+
+										addVertex( lastPointR, u0, 1 );
+										addVertex( lastPointL, u0, 0 );
+										addVertex( outerPoint, u1, 0 );
+
+										addVertex( lastPointR, u0, 1 );
+										addVertex( outerPoint, u1, 0 );
+										addVertex( innerPoint, u1, 1 );
+
+									}
+									else {
+
+										addVertex( lastPointR, u0, 1 );
+										addVertex( lastPointL, u0, 0 );
+										addVertex( outerPoint, u1, 1 );
+
+										addVertex( lastPointL, u0, 0 );
+										addVertex( innerPoint, u1, 0 );
+										addVertex( outerPoint, u1, 1 );
+
+									}
+
+
+									if ( joinIsOnLeftSide ) {
+
+										nextPointL.copy( outerPoint );
+
+									}
+									else {
+
+										nextPointR.copy( outerPoint );
+
+									}
+
+
+								}
+								else {
+
+									// Add extra miter join triangles
+
+									if ( joinIsOnLeftSide ) {
+
+										addVertex( currentPointL, u1, 0 );
+										addVertex( outerPoint, u1, 0 );
+										addVertex( currentPoint, u1, 0.5 );
+
+										addVertex( currentPoint, u1, 0.5 );
+										addVertex( outerPoint, u1, 0 );
+										addVertex( nextPointL, u1, 0 );
+
+									}
+									else {
+
+										addVertex( currentPointR, u1, 1 );
+										addVertex( outerPoint, u1, 1 );
+										addVertex( currentPoint, u1, 0.5 );
+
+										addVertex( currentPoint, u1, 0.5 );
+										addVertex( outerPoint, u1, 1 );
+										addVertex( nextPointR, u1, 1 );
+
+									}
+
+								}
+
+								isMiter = true;
+
+							}
+
+							break;
+
+					}
+
+				}
+				else {
+
+					// The segment triangles are generated here when two consecutive points are collinear
+
+					makeSegmentTriangles();
+
+				}
+
+			}
+			else {
+
+				// The segment triangles are generated here if it is the ending segment
+
+				makeSegmentTriangles();
+
+			}
+
+			if ( ! isClosed && iPoint === numPoints - 1 ) {
+
+				// Start line endcap
+				addCapGeometry( points[ 0 ], point0L, point0R, joinIsOnLeftSide, true, u0 );
+
+			}
+
+			// Increment loop variables
+
+			u0 = u1;
+
+			previousPoint = currentPoint;
+
+			lastPointL.copy( nextPointL );
+			lastPointR.copy( nextPointR );
+
+		}
+
+		if ( ! isClosed ) {
+
+			// Ending line endcap
+			addCapGeometry( currentPoint, currentPointL, currentPointR, joinIsOnLeftSide, false, u1 );
+
+		}
+		else if ( innerSideModified && vertices ) {
+
+			// Modify path first segment vertices to adjust to the segments inner and outer intersections
+
+			var lastOuter = outerPoint;
+			var lastInner = innerPoint;
+			if ( initialJoinIsOnLeftSide !== joinIsOnLeftSide) {
+				lastOuter = innerPoint;
+				lastInner = outerPoint;
+			}
+
+			if ( joinIsOnLeftSide ) {
+
+				lastInner.toArray( vertices, 0 * 3 );
+				lastInner.toArray( vertices, 3 * 3 );
+
+				if ( isMiter ) {
+
+					lastOuter.toArray( vertices, 1 * 3 );
+				}
+
+			}
+			else {
+
+				lastInner.toArray( vertices, 1 * 3 );
+				lastInner.toArray( vertices, 3 * 3 );
+
+				if ( isMiter ) {
+
+					lastOuter.toArray( vertices, 0 * 3 );
+				}
+
+			}
+
+		}
+
+		return numVertices;
+
+		// -- End of algorithm
+
+		// -- Functions
+
+		function getNormal( p1, p2, result ) {
+
+			result.subVectors( p2, p1 );
+			return result.set( - result.y, result.x ).normalize();
+
+		}
+
+		function addVertex( position, u, v ) {
+
+			if ( vertices ) {
+
+				vertices[ currentCoordinate ] = position.x;
+				vertices[ currentCoordinate + 1 ] = position.y;
+				vertices[ currentCoordinate + 2 ] = 0;
+
+				normals[ currentCoordinate ] = 0;
+				normals[ currentCoordinate + 1 ] = 0;
+				normals[ currentCoordinate + 2 ] = 1;
+
+				uvs[ currentCoordinateUV ] = u;
+				uvs[ currentCoordinateUV + 1 ] = v;
+
+				currentCoordinate += 3;
+				currentCoordinateUV += 2;
+
+			}
+
+			numVertices += 3;
+
+		}
+
+		function makeCircularSector( center, p1, p2, u, v ) {
+
+			// param p1, p2: Points in the circle arc.
+			// p1 and p2 are in clockwise direction.
+
+			tempV2_1.copy( p1 ).sub( center ).normalize();
+			tempV2_2.copy( p2 ).sub( center ).normalize();
+
+			var angle = Math.PI;
+			var dot = tempV2_1.dot( tempV2_2 );
+			if ( Math.abs( dot ) < 1 ) angle = Math.abs( Math.acos( dot ) );
+
+			angle /= arcLengthDivisions;
+
+			tempV2_3.copy( p1 );
+
+			for ( var i = 0, il = arcLengthDivisions - 1; i < il; i++ ) {
+
+				tempV2_4.copy( tempV2_3 ).rotateAround( center, angle );
+
+				addVertex( tempV2_3, u, v );
+				addVertex( tempV2_4, u, v );
+				addVertex( center, u, 0.5 );
+
+				tempV2_3.copy( tempV2_4 );
+			}
+
+			addVertex( tempV2_4, u, v );
+			addVertex( p2, u, v );
+			addVertex( center, u, 0.5 );
+
+		}
+
+		function makeSegmentTriangles() {
+
+			addVertex( lastPointR, u0, 1 );
+			addVertex( lastPointL, u0, 0 );
+			addVertex( currentPointL, u1, 0 );
+
+			addVertex( lastPointR, u0, 1 );
+			addVertex( currentPointL, u1, 1 );
+			addVertex( currentPointR, u1, 0 );
+
+		}
+
+		function makeSegmentWithBevelJoin( joinIsOnLeftSide, innerSideModified, u ) {
+
+			if ( innerSideModified ) {
+
+				// Optimized segment + bevel triangles
+
+				if ( joinIsOnLeftSide ) {
+
+					// Path segments triangles
+
+					addVertex( lastPointR, u0, 1 );
+					addVertex( lastPointL, u0, 0 );
+					addVertex( currentPointL, u1, 0 );
+
+					addVertex( lastPointR, u0, 1 );
+					addVertex( currentPointL, u1, 0 );
+					addVertex( innerPoint, u1, 1 );
+
+					// Bevel join triangle
+
+					addVertex( currentPointL, u, 0 );
+					addVertex( nextPointL, u, 0 );
+					addVertex( innerPoint, u, 0.5 );
+
+				}
+				else {
+
+					// Path segments triangles
+
+					addVertex( lastPointR, u0, 1 );
+					addVertex( lastPointL, u0, 0 );
+					addVertex( currentPointR, u1, 1 );
+
+					addVertex( lastPointL, u0, 0 );
+					addVertex( innerPoint, u1, 0 );
+					addVertex( currentPointR, u1, 1 );
+
+					// Bevel join triangle
+
+					addVertex( currentPointR, u, 1 );
+					addVertex( nextPointR, u, 0 );
+					addVertex( innerPoint, u, 0.5 );
+
+				}
+
+			}
+			else {
+
+				// Bevel join triangle. The segment triangles are done in the main loop
+
+				if ( joinIsOnLeftSide ) {
+
+					addVertex( currentPointL, u, 0 );
+					addVertex( nextPointL, u, 0 );
+					addVertex( currentPoint, u, 0.5 );
+
+				}
+				else {
+
+					addVertex( currentPointR, u, 1 );
+					addVertex( nextPointR, u, 0 );
+					addVertex( currentPoint, u, 0.5 );
+
+				}
+
+			}
+
+		}
+
+		function createSegmentTrianglesWithMiddleSection( joinIsOnLeftSide, innerSideModified ) {
+
+			if ( innerSideModified ) {
+
+				if ( joinIsOnLeftSide ) {
+
+					addVertex( lastPointR, u0, 1 );
+					addVertex( lastPointL, u0, 0 );
+					addVertex( currentPointL, u1, 0 );
+
+					addVertex( lastPointR, u0, 1 );
+					addVertex( currentPointL, u1, 0 );
+					addVertex( innerPoint, u1, 1 );
+
+					addVertex( currentPointL, u0, 0 );
+					addVertex( currentPoint, u1, 0.5 );
+					addVertex( innerPoint, u1, 1 );
+
+					addVertex( currentPoint, u1, 0.5 );
+					addVertex( nextPointL, u0, 0 );
+					addVertex( innerPoint, u1, 1 );
+
+				}
+				else {
+
+					addVertex( lastPointR, u0, 1 );
+					addVertex( lastPointL, u0, 0 );
+					addVertex( currentPointR, u1, 1 );
+
+					addVertex( lastPointL, u0, 0 );
+					addVertex( innerPoint, u1, 0 );
+					addVertex( currentPointR, u1, 1 );
+
+					addVertex( currentPointR, u0, 1 );
+					addVertex( innerPoint, u1, 0 );
+					addVertex( currentPoint, u1, 0.5 );
+
+					addVertex( currentPoint, u1, 0.5 );
+					addVertex( innerPoint, u1, 0 );
+					addVertex( nextPointR, u0, 1 );
+
+				}
+
+			}
+
+		}
+
+		function addCapGeometry( center, p1, p2, joinIsOnLeftSide, start, u ) {
+
+			// param center: End point of the path
+			// param p1, p2: Left and right cap points
+
+			switch ( style.strokeLineCap ) {
+
+				case 'round':
+
+					if ( start ) {
+
+						makeCircularSector( center, p2, p1, u, 0.5 );
+
+					}
+					else {
+
+						makeCircularSector( center, p1, p2, u, 0.5 );
+
+					}
+
+					break;
+
+				case 'square':
+
+					if ( start ) {
+
+						tempV2_1.subVectors( p1, center );
+						tempV2_2.set( tempV2_1.y, - tempV2_1.x );
+
+						tempV2_3.addVectors( tempV2_1, tempV2_2 ).add( center );
+						tempV2_4.subVectors( tempV2_2, tempV2_1 ).add( center );
+
+						// Modify already existing vertices
+						if ( joinIsOnLeftSide ) {
+
+							tempV2_3.toArray( vertices, 1 * 3 );
+							tempV2_4.toArray( vertices, 0 * 3 );
+							tempV2_4.toArray( vertices, 3 * 3 );
+
+						}
+						else {
+
+							tempV2_3.toArray( vertices, 1 * 3 );
+							tempV2_3.toArray( vertices, 3 * 3 );
+							tempV2_4.toArray( vertices, 0 * 3 );
+
+						}
+
+					}
+					else {
+
+						tempV2_1.subVectors( p2, center );
+						tempV2_2.set( tempV2_1.y, - tempV2_1.x );
+
+						tempV2_3.addVectors( tempV2_1, tempV2_2 ).add( center );
+						tempV2_4.subVectors( tempV2_2, tempV2_1 ).add( center );
+
+						var vl = vertices.length;
+
+						// Modify already existing vertices
+						if ( joinIsOnLeftSide ) {
+
+							tempV2_3.toArray( vertices, vl - 1 * 3 );
+							tempV2_4.toArray( vertices, vl - 2 * 3 );
+							tempV2_4.toArray( vertices, vl - 4 * 3 );
+
+						}
+						else {
+
+							tempV2_3.toArray( vertices, vl - 2 * 3 );
+							tempV2_4.toArray( vertices, vl - 1 * 3 );
+							tempV2_4.toArray( vertices, vl - 4 * 3 );
+
+						}
+
+					}
+
+					break;
+
+				case 'butt':
+				default:
+
+					// Nothing to do here
+					break;
+
+			}
+
+		}
+
+		function removeDuplicatedPoints( points ) {
+
+			// Creates a new array if necessary with duplicated points removed.
+			// This does not remove duplicated initial and ending points of a closed path.
+
+			var dupPoints = false;
+			for ( var i = 1, n = points.length - 1; i < n; i ++ ) {
+
+				if ( points[ i ].distanceTo( points[ i + 1 ] ) < minDistance ) {
+
+					dupPoints = true;
+					break;
+
+				}
+
+			}
+
+			if ( ! dupPoints ) return points;
+
+			var newPoints = [];
+			newPoints.push( points[ 0 ] );
+
+			for ( var i = 1, n = points.length - 1; i < n; i ++ ) {
+
+				if ( points[ i ].distanceTo( points[ i + 1 ] ) >= minDistance ) {
+
+					newPoints.push( points[ i ] );
+
+				}
+			}
+
+			newPoints.push( points[ points.length - 1 ] );
+
+			return newPoints;
+
+		}
+	};
+
+}();

+ 428 - 0
examples/models/svg/lineJoinsAndCaps.svg

@@ -0,0 +1,428 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="210mm"
+   height="297mm"
+   viewBox="0 0 210 297"
+   version="1.1"
+   id="svg8"
+   inkscape:version="0.92.3 (2405546, 2018-03-11)"
+   sodipodi:docname="lineJoinsAndCaps.svg">
+  <defs
+     id="defs2" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.35"
+     inkscape:cx="1163.5114"
+     inkscape:cy="194.79646"
+     inkscape:document-units="mm"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1920"
+     inkscape:window-height="1015"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
+     inkscape:window-maximized="1" />
+  <metadata
+     id="metadata5">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Capa 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <path
+       style="fill:#56bef1;fill-opacity:1;stroke:#000000;stroke-width:15.12240791;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:2.0999999;stroke-dasharray:none;stroke-opacity:1"
+       d="M 11.645714,137.94889 32.107527,33.104515 156.65766,13.79107 159.03004,161.95271 94.975687,86.630309 Z"
+       id="path815"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccccc" />
+    <path
+       sodipodi:nodetypes="cccccc"
+       inkscape:connector-curvature="0"
+       id="path824"
+       d="M 216.55882,139.23495 237.02062,34.39057 361.57076,15.077128 363.94313,163.23877 299.88879,87.916364 Z"
+       style="fill:#56bef1;fill-opacity:1;stroke:#000000;stroke-width:15.12106323;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:2.0999999;stroke-dasharray:none;stroke-opacity:1" />
+    <path
+       style="fill:#56bef1;fill-opacity:1;stroke:#000000;stroke-width:15.12106323;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2.0999999;stroke-dasharray:none;stroke-opacity:1"
+       d="M 421.54128,139.9001 442.00309,35.055731 566.55324,15.74229 568.92561,163.90393 504.87126,88.581525 Z"
+       id="path826"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccccc" />
+    <path
+       inkscape:connector-curvature="0"
+       id="path830"
+       d="m 26.903642,382.81518 107.927638,29.30033 0.66958,-68.73008"
+       style="fill:none;stroke:#000000;stroke-width:16.19490242;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       sodipodi:nodetypes="ccc" />
+    <path
+       sodipodi:nodetypes="ccc"
+       style="fill:none;stroke:#000000;stroke-width:16.195;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="m 231.9558,382.81518 107.92806,29.30033 0.66958,-68.73008"
+       id="path4868"
+       inkscape:connector-curvature="0" />
+    <path
+       inkscape:connector-curvature="0"
+       id="path4870"
+       d="m 437.00814,382.81518 107.92806,29.30033 0.66958,-68.73008"
+       style="fill:none;stroke:#000000;stroke-width:16.19499969;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       sodipodi:nodetypes="ccc" />
+    <g
+       aria-label="Square endcap"
+       transform="matrix(0.76997327,0,0,0.76997327,398.16964,271.71599)"
+       style="font-style:normal;font-weight:normal;font-size:18.66666603px;line-height:0;font-family:sans-serif;letter-spacing:0px;word-spacing:4.94999981px;fill:#000000;fill-opacity:1;stroke:none"
+       id="flowRoot4583">
+      <path
+         d="m 24.595875,251.66342 q 0.992,0 1.632,-0.192 0.672,-0.224 1.056,-0.576 0.384,-0.384 0.512,-0.832 0.16,-0.48 0.16,-0.96 0,-0.608 -0.352,-1.088 -0.352,-0.48 -0.896,-0.832 -0.544,-0.384 -1.216,-0.672 -0.672,-0.32 -1.344,-0.576 -0.896,-0.32 -1.856,-0.736 -0.96,-0.416 -1.76,-1.056 -0.768,-0.64 -1.28,-1.568 -0.512,-0.96 -0.512,-2.368 0,-2.784 1.792,-4.352 1.824,-1.568 4.992,-1.568 1.824,0 3.168,0.416 1.376,0.416 2.304,0.928 l -1.152,3.04 q -0.8,-0.448 -1.792,-0.768 -0.96,-0.32 -2.24,-0.32 -3.136,0 -3.136,2.272 0,0.576 0.32,1.024 0.32,0.416 0.8,0.768 0.512,0.32 1.12,0.576 0.64,0.256 1.248,0.48 0.928,0.352 1.92,0.8 1.024,0.416 1.856,1.152 0.864,0.704 1.408,1.792 0.544,1.056 0.544,2.688 0,2.784 -1.824,4.32 -1.792,1.504 -5.472,1.504 -2.464,0 -3.936,-0.512 -1.472,-0.544 -2.208,-0.992 l 1.152,-3.2 q 0.864,0.512 2.048,0.96 1.216,0.448 2.944,0.448 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4642"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 38.163875,247.15142 q 0,2.08 0.672,3.232 0.704,1.12 2.112,1.12 0.672,0 1.248,-0.16 0.608,-0.16 1.056,-0.416 v -8.256 q -0.288,-0.032 -0.8,-0.064 -0.48,-0.064 -1.024,-0.064 -1.632,0 -2.464,1.312 -0.8,1.28 -0.8,3.296 z m -4,-0.128 q 0,-1.728 0.48,-3.168 0.48,-1.472 1.376,-2.496 0.928,-1.024 2.272,-1.6 1.376,-0.576 3.104,-0.576 0.736,0 1.536,0.064 0.832,0.064 1.6,0.192 0.768,0.096 1.44,0.256 0.704,0.128 1.216,0.288 v 19.84 h -3.936 v -5.696 q -0.736,0.352 -1.568,0.544 -0.8,0.224 -1.536,0.224 -1.472,0 -2.592,-0.544 -1.12,-0.576 -1.888,-1.6 -0.736,-1.024 -1.12,-2.464 -0.384,-1.472 -0.384,-3.264 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4644"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 63.187875,254.03142 q -1.088,0.288 -2.656,0.576 -1.536,0.288 -3.36,0.288 -1.856,0 -3.04,-0.512 -1.184,-0.512 -1.888,-1.44 -0.672,-0.96 -0.928,-2.24 -0.256,-1.28 -0.256,-2.816 v -8.384 h 3.936 v 7.872 q 0,2.08 0.48,3.104 0.512,0.992 1.952,0.992 0.896,0 1.792,-0.16 v -11.808 h 3.968 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4646"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 73.203875,251.72742 q 0.64,0 1.248,0 0.608,-0.032 1.056,-0.096 v -3.456 q -0.384,-0.064 -0.928,-0.096 -0.544,-0.064 -0.992,-0.064 -0.608,0 -1.184,0.096 -0.576,0.064 -1.024,0.256 -0.416,0.192 -0.672,0.544 -0.256,0.352 -0.256,0.928 0,1.024 0.768,1.472 0.768,0.416 1.984,0.416 z m -0.352,-12.608 q 1.856,0 3.072,0.448 1.248,0.416 1.984,1.216 0.736,0.768 1.056,1.92 0.32,1.12 0.32,2.528 v 8.96 q -0.864,0.192 -2.56,0.448 -1.696,0.256 -3.808,0.256 -1.44,0 -2.624,-0.256 -1.152,-0.256 -1.984,-0.832 -0.832,-0.608 -1.28,-1.536 -0.448,-0.96 -0.448,-2.336 0,-1.312 0.512,-2.208 0.512,-0.896 1.376,-1.44 0.864,-0.544 1.984,-0.768 1.12,-0.256 2.336,-0.256 1.536,0 2.72,0.256 v -0.48 q 0,-1.12 -0.704,-1.856 -0.704,-0.768 -2.432,-0.768 -1.12,0 -2.176,0.16 -1.024,0.16 -1.664,0.384 l -0.544,-3.168 q 0.736,-0.256 2.08,-0.448 1.344,-0.224 2.784,-0.224 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4648"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 95.155875,243.50342 q -0.384,-0.096 -0.928,-0.192 -0.512,-0.096 -1.056,-0.16 -0.544,-0.096 -1.056,-0.128 -0.512,-0.032 -0.864,-0.032 -0.832,0 -1.632,0.096 -0.8,0.064 -1.632,0.288 v 11.168 h -3.968 v -14.048 q 1.568,-0.576 3.2,-0.928 1.664,-0.352 3.872,-0.352 0.32,0 0.896,0.032 0.608,0.032 1.28,0.128 0.672,0.064 1.344,0.192 0.704,0.096 1.248,0.288 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4650"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 98.163875,247.15142 q 0,-1.984 0.608,-3.488 0.608,-1.504 1.600005,-2.496 0.992,-1.024 2.27199,-1.536 1.28,-0.512 2.62401,-0.512 3.328,0 5.056,1.984 1.72799,1.952 1.72799,5.728 0,0.384 -0.032,0.8 0,0.416 -0.032,0.672 h -9.76001 q 0,1.472 1.21601,2.336 1.216,0.832 3.136,0.832 1.184,0 2.23999,-0.256 1.08801,-0.256 1.824,-0.512 l 0.54401,3.36 q -1.024,0.352 -2.176,0.576 -1.152,0.256 -2.592,0.256 -1.92,0 -3.456,-0.48 -1.50401,-0.512 -2.592,-1.472 -1.056005,-0.992 -1.632005,-2.432 -0.576,-1.44 -0.576,-3.36 z m 10.080005,-1.568 q 0,-0.608 -0.16,-1.152 -0.16001,-0.576 -0.512,-1.024 -0.352,-0.448 -0.896,-0.704 -0.544,-0.288 -1.34401,-0.288 -0.768,0 -1.34399,0.256 -0.544,0.256 -0.928,0.704 -0.352,0.448 -0.57601,1.024 -0.19199,0.576 -0.256,1.184 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4652"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 135.11387,247.15142 q 0,-1.984 0.608,-3.488 0.608,-1.504 1.6,-2.496 0.992,-1.024 2.272,-1.536 1.28,-0.512 2.624,-0.512 3.328,0 5.056,1.984 1.728,1.952 1.728,5.728 0,0.384 -0.032,0.8 0,0.416 -0.032,0.672 h -9.76 q 0,1.472 1.216,2.336 1.216,0.832 3.136,0.832 1.184,0 2.24,-0.256 1.088,-0.256 1.824,-0.512 l 0.544,3.36 q -1.024,0.352 -2.176,0.576 -1.152,0.256 -2.592,0.256 -1.92,0 -3.456,-0.48 -1.504,-0.512 -2.592,-1.472 -1.056,-0.992 -1.632,-2.432 -0.576,-1.44 -0.576,-3.36 z m 10.08,-1.568 q 0,-0.608 -0.16,-1.152 -0.16,-0.576 -0.512,-1.024 -0.352,-0.448 -0.896,-0.704 -0.544,-0.288 -1.344,-0.288 -0.768,0 -1.344,0.256 -0.544,0.256 -0.928,0.704 -0.352,0.448 -0.576,1.024 -0.192,0.576 -0.256,1.184 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4654"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 152.13787,239.98342 q 1.056,-0.288 2.592,-0.544 1.568,-0.256 3.392,-0.256 1.792,0 2.976,0.512 1.184,0.48 1.856,1.408 0.704,0.896 0.992,2.176 0.288,1.248 0.288,2.784 v 8.48 h -3.936 v -7.968 q 0,-2.112 -0.48,-3.04 -0.48,-0.928 -1.952,-0.928 -0.448,0 -0.896,0.032 -0.416,0.032 -0.896,0.096 v 11.808 h -3.936 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4656"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 171.01787,246.92742 q 0,2.016 0.736,3.296 0.768,1.28 2.432,1.28 0.48,0 0.896,-0.032 0.416,-0.032 0.864,-0.096 v -8.192 q -0.448,-0.256 -1.024,-0.416 -0.576,-0.192 -1.216,-0.192 -1.408,0 -2.048,1.12 -0.64,1.12 -0.64,3.232 z m 8.864,7.168 q -1.088,0.352 -2.656,0.576 -1.568,0.224 -3.072,0.224 -3.52,0 -5.344,-2.08 -1.792,-2.08 -1.792,-5.696 0,-3.68 1.504,-5.792 1.536,-2.144 4.512,-2.144 0.8,0 1.568,0.192 0.768,0.16 1.344,0.48 v -6.848 l 3.936,-0.672 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4658"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 183.52987,247.02342 q 0,-1.632 0.512,-3.072 0.512,-1.44 1.568,-2.496 1.056,-1.088 2.656,-1.696 1.632,-0.64 3.872,-0.64 1.344,0 2.432,0.192 1.088,0.192 2.208,0.64 l -0.864,3.168 q -0.64,-0.224 -1.44,-0.384 -0.768,-0.192 -1.984,-0.192 -1.408,0 -2.368,0.352 -0.928,0.32 -1.504,0.928 -0.576,0.576 -0.832,1.408 -0.256,0.832 -0.256,1.792 0,2.08 1.152,3.264 1.184,1.184 4,1.184 0.928,0 1.92,-0.128 1.024,-0.128 1.856,-0.416 l 0.576,3.232 q -0.832,0.32 -2.016,0.512 -1.184,0.224 -2.784,0.224 -2.304,0 -3.968,-0.608 -1.632,-0.608 -2.688,-1.664 -1.056,-1.056 -1.568,-2.496 -0.48,-1.44 -0.48,-3.104 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4660"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 206.15387,251.72742 q 0.64,0 1.248,0 0.608,-0.032 1.056,-0.096 v -3.456 q -0.384,-0.064 -0.928,-0.096 -0.544,-0.064 -0.992,-0.064 -0.608,0 -1.184,0.096 -0.576,0.064 -1.024,0.256 -0.416,0.192 -0.672,0.544 -0.256,0.352 -0.256,0.928 0,1.024 0.768,1.472 0.768,0.416 1.984,0.416 z m -0.352,-12.608 q 1.856,0 3.072,0.448 1.248,0.416 1.984,1.216 0.736,0.768 1.056,1.92 0.32,1.12 0.32,2.528 v 8.96 q -0.864,0.192 -2.56,0.448 -1.696,0.256 -3.808,0.256 -1.44,0 -2.624,-0.256 -1.152,-0.256 -1.984,-0.832 -0.832,-0.608 -1.28,-1.536 -0.448,-0.96 -0.448,-2.336 0,-1.312 0.512,-2.208 0.512,-0.896 1.376,-1.44 0.864,-0.544 1.984,-0.768 1.12,-0.256 2.336,-0.256 1.536,0 2.72,0.256 v -0.48 q 0,-1.12 -0.704,-1.856 -0.704,-0.768 -2.432,-0.768 -1.12,0 -2.176,0.16 -1.024,0.16 -1.664,0.384 l -0.544,-3.168 q 0.736,-0.256 2.08,-0.448 1.344,-0.224 2.784,-0.224 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4662"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 225.12987,247.15142 q 0,-2.016 -0.8,-3.296 -0.8,-1.312 -2.432,-1.312 -0.448,0 -0.96,0.064 -0.512,0.032 -0.864,0.064 v 8.256 q 0.384,0.256 1.024,0.416 0.64,0.16 1.28,0.16 1.408,0 2.08,-1.12 0.672,-1.152 0.672,-3.232 z m 4,-0.128 q 0,1.728 -0.384,3.2 -0.384,1.44 -1.12,2.496 -0.736,1.024 -1.856,1.6 -1.12,0.576 -2.592,0.576 -1.504,0 -3.104,-0.736 v 5.664 h -3.936 v -19.84 q 1.088,-0.352 2.656,-0.576 1.6,-0.224 3.136,-0.224 3.488,0 5.344,2.112 1.856,2.112 1.856,5.728 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4664"
+         inkscape:connector-curvature="0" />
+    </g>
+    <g
+       aria-label="Butt endcap"
+       transform="matrix(0.76997327,0,0,0.76997327,207.21552,271.71599)"
+       style="font-style:normal;font-weight:normal;font-size:18.66666603px;line-height:0;font-family:sans-serif;letter-spacing:0px;word-spacing:4.94999981px;fill:#000000;fill-opacity:1;stroke:none"
+       id="flowRoot4575">
+      <path
+         d="m 32.179875,248.55942 q 0,1.792 -0.608,3.008 -0.608,1.184 -1.664,1.92 -1.024,0.704 -2.464,1.024 -1.408,0.288 -3.04,0.288 -1.312,0 -2.72,-0.16 -1.408,-0.128 -2.784,-0.416 v -19.232 q 1.12,-0.192 2.464,-0.32 1.344,-0.16 2.656,-0.16 2.24,0 3.648,0.48 1.408,0.448 2.208,1.216 0.8,0.736 1.088,1.664 0.288,0.928 0.288,1.856 0,1.408 -0.704,2.496 -0.672,1.088 -1.824,1.728 2.016,0.736 2.72,1.984 0.736,1.248 0.736,2.624 z m -9.344,-2.688 v 5.376 q 0.416,0.064 0.896,0.096 0.512,0.032 0.992,0.032 0.672,0 1.312,-0.096 0.64,-0.128 1.12,-0.416 0.512,-0.32 0.832,-0.864 0.32,-0.576 0.32,-1.44 0,-1.44 -0.928,-2.048 -0.896,-0.64 -2.432,-0.64 z m 1.536,-3.264 q 1.504,0 2.272,-0.64 0.768,-0.672 0.768,-1.824 0,-0.704 -0.224,-1.12 -0.224,-0.448 -0.608,-0.672 -0.384,-0.256 -0.896,-0.32 -0.512,-0.096 -1.056,-0.096 -0.448,0 -0.928,0.032 -0.48,0.032 -0.864,0.096 v 4.544 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4667"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 47.187875,254.03142 q -1.088,0.288 -2.656,0.576 -1.536,0.288 -3.36,0.288 -1.856,0 -3.04,-0.512 -1.184,-0.512 -1.888,-1.44 -0.672,-0.96 -0.928,-2.24 -0.256,-1.28 -0.256,-2.816 v -8.384 h 3.936 v 7.872 q 0,2.08 0.48,3.104 0.512,0.992 1.952,0.992 0.896,0 1.792,-0.16 v -11.808 h 3.968 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4669"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 53.747875,242.76742 h -3.424 v -3.264 h 3.424 v -3.776 l 3.936,-0.64 v 4.416 h 6.304 v 3.264 h -6.304 v 6.08 q 0,0.832 0.16,1.344 0.16,0.512 0.448,0.8 0.288,0.288 0.704,0.384 0.416,0.096 0.928,0.096 0.544,0 0.992,-0.032 0.48,-0.032 0.896,-0.096 0.448,-0.096 0.896,-0.256 0.48,-0.16 1.024,-0.416 l 0.544,3.392 q -1.088,0.448 -2.368,0.64 -1.248,0.192 -2.432,0.192 -1.376,0 -2.432,-0.224 -1.056,-0.224 -1.792,-0.864 -0.736,-0.64 -1.12,-1.792 -0.384,-1.184 -0.384,-3.072 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4671"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 69.747875,242.76742 h -3.424 v -3.264 h 3.424 v -3.776 l 3.936,-0.64 v 4.416 h 6.304 v 3.264 h -6.304 v 6.08 q 0,0.832 0.16,1.344 0.16,0.512 0.448,0.8 0.288,0.288 0.704,0.384 0.416,0.096 0.928,0.096 0.544,0 0.992,-0.032 0.48,-0.032 0.896,-0.096 0.448,-0.096 0.896,-0.256 0.48,-0.16 1.024,-0.416 l 0.544,3.392 q -1.088,0.448 -2.368,0.64 -1.248,0.192 -2.432,0.192 -1.376,0 -2.432,-0.224 -1.056,-0.224 -1.792,-0.864 -0.736,-0.64 -1.12,-1.792 -0.384,-1.184 -0.384,-3.072 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4673"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 103.11387,247.15142 q 0,-1.984 0.608,-3.488 0.608,-1.504 1.6,-2.496 0.992,-1.024 2.272,-1.536 1.28,-0.512 2.624,-0.512 3.328,0 5.056,1.984 1.728,1.952 1.728,5.728 0,0.384 -0.032,0.8 0,0.416 -0.032,0.672 h -9.76 q 0,1.472 1.216,2.336 1.216,0.832 3.136,0.832 1.184,0 2.24,-0.256 1.088,-0.256 1.824,-0.512 l 0.544,3.36 q -1.024,0.352 -2.176,0.576 -1.152,0.256 -2.592,0.256 -1.92,0 -3.456,-0.48 -1.504,-0.512 -2.592,-1.472 -1.056,-0.992 -1.632,-2.432 -0.576,-1.44 -0.576,-3.36 z m 10.08,-1.568 q 0,-0.608 -0.16,-1.152 -0.16,-0.576 -0.512,-1.024 -0.352,-0.448 -0.896,-0.704 -0.544,-0.288 -1.344,-0.288 -0.768,0 -1.344,0.256 -0.544,0.256 -0.928,0.704 -0.352,0.448 -0.576,1.024 -0.192,0.576 -0.256,1.184 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4675"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 120.13787,239.98342 q 1.056,-0.288 2.592,-0.544 1.568,-0.256 3.392,-0.256 1.792,0 2.976,0.512 1.184,0.48 1.856,1.408 0.704,0.896 0.992,2.176 0.288,1.248 0.288,2.784 v 8.48 h -3.936 v -7.968 q 0,-2.112 -0.48,-3.04 -0.48,-0.928 -1.952,-0.928 -0.448,0 -0.896,0.032 -0.416,0.032 -0.896,0.096 v 11.808 h -3.936 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4677"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 139.01787,246.92742 q 0,2.016 0.736,3.296 0.768,1.28 2.432,1.28 0.48,0 0.896,-0.032 0.416,-0.032 0.864,-0.096 v -8.192 q -0.448,-0.256 -1.024,-0.416 -0.576,-0.192 -1.216,-0.192 -1.408,0 -2.048,1.12 -0.64,1.12 -0.64,3.232 z m 8.864,7.168 q -1.088,0.352 -2.656,0.576 -1.568,0.224 -3.072,0.224 -3.52,0 -5.344,-2.08 -1.792,-2.08 -1.792,-5.696 0,-3.68 1.504,-5.792 1.536,-2.144 4.512,-2.144 0.8,0 1.568,0.192 0.768,0.16 1.344,0.48 v -6.848 l 3.936,-0.672 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4679"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 151.52987,247.02342 q 0,-1.632 0.512,-3.072 0.512,-1.44 1.568,-2.496 1.056,-1.088 2.656,-1.696 1.632,-0.64 3.872,-0.64 1.344,0 2.432,0.192 1.088,0.192 2.208,0.64 l -0.864,3.168 q -0.64,-0.224 -1.44,-0.384 -0.768,-0.192 -1.984,-0.192 -1.408,0 -2.368,0.352 -0.928,0.32 -1.504,0.928 -0.576,0.576 -0.832,1.408 -0.256,0.832 -0.256,1.792 0,2.08 1.152,3.264 1.184,1.184 4,1.184 0.928,0 1.92,-0.128 1.024,-0.128 1.856,-0.416 l 0.576,3.232 q -0.832,0.32 -2.016,0.512 -1.184,0.224 -2.784,0.224 -2.304,0 -3.968,-0.608 -1.632,-0.608 -2.688,-1.664 -1.056,-1.056 -1.568,-2.496 -0.48,-1.44 -0.48,-3.104 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4681"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 174.15387,251.72742 q 0.64,0 1.248,0 0.608,-0.032 1.056,-0.096 v -3.456 q -0.384,-0.064 -0.928,-0.096 -0.544,-0.064 -0.992,-0.064 -0.608,0 -1.184,0.096 -0.576,0.064 -1.024,0.256 -0.416,0.192 -0.672,0.544 -0.256,0.352 -0.256,0.928 0,1.024 0.768,1.472 0.768,0.416 1.984,0.416 z m -0.352,-12.608 q 1.856,0 3.072,0.448 1.248,0.416 1.984,1.216 0.736,0.768 1.056,1.92 0.32,1.12 0.32,2.528 v 8.96 q -0.864,0.192 -2.56,0.448 -1.696,0.256 -3.808,0.256 -1.44,0 -2.624,-0.256 -1.152,-0.256 -1.984,-0.832 -0.832,-0.608 -1.28,-1.536 -0.448,-0.96 -0.448,-2.336 0,-1.312 0.512,-2.208 0.512,-0.896 1.376,-1.44 0.864,-0.544 1.984,-0.768 1.12,-0.256 2.336,-0.256 1.536,0 2.72,0.256 v -0.48 q 0,-1.12 -0.704,-1.856 -0.704,-0.768 -2.432,-0.768 -1.12,0 -2.176,0.16 -1.024,0.16 -1.664,0.384 l -0.544,-3.168 q 0.736,-0.256 2.08,-0.448 1.344,-0.224 2.784,-0.224 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4683"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 193.12987,247.15142 q 0,-2.016 -0.8,-3.296 -0.8,-1.312 -2.432,-1.312 -0.448,0 -0.96,0.064 -0.512,0.032 -0.864,0.064 v 8.256 q 0.384,0.256 1.024,0.416 0.64,0.16 1.28,0.16 1.408,0 2.08,-1.12 0.672,-1.152 0.672,-3.232 z m 4,-0.128 q 0,1.728 -0.384,3.2 -0.384,1.44 -1.12,2.496 -0.736,1.024 -1.856,1.6 -1.12,0.576 -2.592,0.576 -1.504,0 -3.104,-0.736 v 5.664 h -3.936 v -19.84 q 1.088,-0.352 2.656,-0.576 1.6,-0.224 3.136,-0.224 3.488,0 5.344,2.112 1.856,2.112 1.856,5.728 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4685"
+         inkscape:connector-curvature="0" />
+    </g>
+    <g
+       aria-label="Round endcap"
+       transform="matrix(0.76997327,0,0,0.76997327,-2.2173295,271.71599)"
+       style="font-style:normal;font-weight:normal;font-size:18.66666603px;line-height:0;font-family:sans-serif;letter-spacing:0px;word-spacing:4.94999981px;fill:#000000;fill-opacity:1;stroke:none"
+       id="flowRoot4567">
+      <path
+         d="m 24.019875,234.51142 q 1.696,0 3.072,0.384 1.376,0.384 2.368,1.184 0.992,0.8 1.504,2.048 0.544,1.216 0.544,2.88 0,1.728 -0.704,3.136 -0.704,1.408 -2.336,2.144 0.544,0.832 1.152,1.856 0.608,0.992 1.184,2.112 0.576,1.088 1.088,2.208 0.544,1.088 0.928,2.08 h -4.128 q -0.736,-1.92 -1.728,-3.68 -0.96,-1.792 -2.08,-3.616 h -2.048 v 7.296 h -3.936 v -19.52 q 0.576,-0.128 1.248,-0.224 0.704,-0.128 1.408,-0.16 0.704,-0.064 1.344,-0.096 0.64,-0.032 1.12,-0.032 z m 3.488,6.464 q 0,-1.568 -0.96,-2.304 -0.96,-0.736 -2.528,-0.736 -0.224,0 -0.544,0.032 -0.32,0 -0.64,0.064 v 5.952 h 0.864 q 2.016,0 2.912,-0.768 0.896,-0.8 0.896,-2.24 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4688"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 48.275875,246.99142 q 0,1.792 -0.512,3.264 -0.512,1.44 -1.44,2.496 -0.928,1.056 -2.24,1.632 -1.28,0.576 -2.88,0.576 -1.6,0 -2.912,-0.576 -1.28,-0.576 -2.24,-1.632 -0.928,-1.056 -1.44,-2.496 -0.512,-1.472 -0.512,-3.264 0,-1.76 0.512,-3.2 0.544,-1.472 1.472,-2.496 0.96,-1.056 2.272,-1.6 1.312,-0.576 2.848,-0.576 1.568,0 2.848,0.576 1.312,0.544 2.24,1.6 0.96,1.024 1.472,2.496 0.512,1.44 0.512,3.2 z m -10.176,0.032 q 0,2.016 0.704,3.264 0.736,1.248 2.336,1.248 1.536,0 2.304,-1.248 0.8,-1.248 0.8,-3.264 0,-2.016 -0.736,-3.232 -0.704,-1.248 -2.304,-1.248 -1.536,0 -2.336,1.248 -0.768,1.216 -0.768,3.232 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4690"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 63.187875,254.03142 q -1.088,0.288 -2.656,0.576 -1.536,0.288 -3.36,0.288 -1.856,0 -3.04,-0.512 -1.184,-0.512 -1.888,-1.44 -0.672,-0.96 -0.928,-2.24 -0.256,-1.28 -0.256,-2.816 v -8.384 h 3.936 v 7.872 q 0,2.08 0.48,3.104 0.512,0.992 1.952,0.992 0.896,0 1.792,-0.16 v -11.808 h 3.968 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4692"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 67.187875,239.98342 q 1.056,-0.288 2.592,-0.544 1.568,-0.256 3.392,-0.256 1.792,0 2.976,0.512 1.184,0.48 1.856,1.408 0.704,0.896 0.992,2.176 0.288,1.248 0.288,2.784 v 8.48 h -3.936 v -7.968 q 0,-2.112 -0.48,-3.04 -0.48,-0.928 -1.952,-0.928 -0.448,0 -0.896,0.032 -0.416,0.032 -0.896,0.096 v 11.808 h -3.936 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4694"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 86.067875,246.92742 q 0,2.016 0.736,3.296 0.768,1.28 2.432,1.28 0.48,0 0.896,-0.032 0.416,-0.032 0.864,-0.096 v -8.192 q -0.448,-0.256 -1.024,-0.416 -0.576,-0.192 -1.216,-0.192 -1.408,0 -2.048,1.12 -0.64,1.12 -0.64,3.232 z m 8.864,7.168 q -1.088,0.352 -2.656,0.576 -1.568,0.224 -3.072,0.224 -3.52,0 -5.344,-2.08 -1.792,-2.08 -1.792,-5.696 0,-3.68 1.504,-5.792 1.536,-2.144 4.512,-2.144 0.8,0 1.568,0.192 0.768,0.16 1.344,0.48 v -6.848 l 3.936,-0.672 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4696"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 119.11387,247.15142 q 0,-1.984 0.608,-3.488 0.608,-1.504 1.6,-2.496 0.992,-1.024 2.272,-1.536 1.28,-0.512 2.624,-0.512 3.328,0 5.056,1.984 1.728,1.952 1.728,5.728 0,0.384 -0.032,0.8 0,0.416 -0.032,0.672 h -9.76 q 0,1.472 1.216,2.336 1.216,0.832 3.136,0.832 1.184,0 2.24,-0.256 1.088,-0.256 1.824,-0.512 l 0.544,3.36 q -1.024,0.352 -2.176,0.576 -1.152,0.256 -2.592,0.256 -1.92,0 -3.456,-0.48 -1.504,-0.512 -2.592,-1.472 -1.056,-0.992 -1.632,-2.432 -0.576,-1.44 -0.576,-3.36 z m 10.08,-1.568 q 0,-0.608 -0.16,-1.152 -0.16,-0.576 -0.512,-1.024 -0.352,-0.448 -0.896,-0.704 -0.544,-0.288 -1.344,-0.288 -0.768,0 -1.344,0.256 -0.544,0.256 -0.928,0.704 -0.352,0.448 -0.576,1.024 -0.192,0.576 -0.256,1.184 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4698"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 136.13787,239.98342 q 1.056,-0.288 2.592,-0.544 1.568,-0.256 3.392,-0.256 1.792,0 2.976,0.512 1.184,0.48 1.856,1.408 0.704,0.896 0.992,2.176 0.288,1.248 0.288,2.784 v 8.48 h -3.936 v -7.968 q 0,-2.112 -0.48,-3.04 -0.48,-0.928 -1.952,-0.928 -0.448,0 -0.896,0.032 -0.416,0.032 -0.896,0.096 v 11.808 h -3.936 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4700"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 155.01787,246.92742 q 0,2.016 0.736,3.296 0.768,1.28 2.432,1.28 0.48,0 0.896,-0.032 0.416,-0.032 0.864,-0.096 v -8.192 q -0.448,-0.256 -1.024,-0.416 -0.576,-0.192 -1.216,-0.192 -1.408,0 -2.048,1.12 -0.64,1.12 -0.64,3.232 z m 8.864,7.168 q -1.088,0.352 -2.656,0.576 -1.568,0.224 -3.072,0.224 -3.52,0 -5.344,-2.08 -1.792,-2.08 -1.792,-5.696 0,-3.68 1.504,-5.792 1.536,-2.144 4.512,-2.144 0.8,0 1.568,0.192 0.768,0.16 1.344,0.48 v -6.848 l 3.936,-0.672 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4702"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 167.52987,247.02342 q 0,-1.632 0.512,-3.072 0.512,-1.44 1.568,-2.496 1.056,-1.088 2.656,-1.696 1.632,-0.64 3.872,-0.64 1.344,0 2.432,0.192 1.088,0.192 2.208,0.64 l -0.864,3.168 q -0.64,-0.224 -1.44,-0.384 -0.768,-0.192 -1.984,-0.192 -1.408,0 -2.368,0.352 -0.928,0.32 -1.504,0.928 -0.576,0.576 -0.832,1.408 -0.256,0.832 -0.256,1.792 0,2.08 1.152,3.264 1.184,1.184 4,1.184 0.928,0 1.92,-0.128 1.024,-0.128 1.856,-0.416 l 0.576,3.232 q -0.832,0.32 -2.016,0.512 -1.184,0.224 -2.784,0.224 -2.304,0 -3.968,-0.608 -1.632,-0.608 -2.688,-1.664 -1.056,-1.056 -1.568,-2.496 -0.48,-1.44 -0.48,-3.104 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4704"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 190.15387,251.72742 q 0.64,0 1.248,0 0.608,-0.032 1.056,-0.096 v -3.456 q -0.384,-0.064 -0.928,-0.096 -0.544,-0.064 -0.992,-0.064 -0.608,0 -1.184,0.096 -0.576,0.064 -1.024,0.256 -0.416,0.192 -0.672,0.544 -0.256,0.352 -0.256,0.928 0,1.024 0.768,1.472 0.768,0.416 1.984,0.416 z m -0.352,-12.608 q 1.856,0 3.072,0.448 1.248,0.416 1.984,1.216 0.736,0.768 1.056,1.92 0.32,1.12 0.32,2.528 v 8.96 q -0.864,0.192 -2.56,0.448 -1.696,0.256 -3.808,0.256 -1.44,0 -2.624,-0.256 -1.152,-0.256 -1.984,-0.832 -0.832,-0.608 -1.28,-1.536 -0.448,-0.96 -0.448,-2.336 0,-1.312 0.512,-2.208 0.512,-0.896 1.376,-1.44 0.864,-0.544 1.984,-0.768 1.12,-0.256 2.336,-0.256 1.536,0 2.72,0.256 v -0.48 q 0,-1.12 -0.704,-1.856 -0.704,-0.768 -2.432,-0.768 -1.12,0 -2.176,0.16 -1.024,0.16 -1.664,0.384 l -0.544,-3.168 q 0.736,-0.256 2.08,-0.448 1.344,-0.224 2.784,-0.224 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4706"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 209.12987,247.15142 q 0,-2.016 -0.8,-3.296 -0.8,-1.312 -2.432,-1.312 -0.448,0 -0.96,0.064 -0.512,0.032 -0.864,0.064 v 8.256 q 0.384,0.256 1.024,0.416 0.64,0.16 1.28,0.16 1.408,0 2.08,-1.12 0.672,-1.152 0.672,-3.232 z m 4,-0.128 q 0,1.728 -0.384,3.2 -0.384,1.44 -1.12,2.496 -0.736,1.024 -1.856,1.6 -1.12,0.576 -2.592,0.576 -1.504,0 -3.104,-0.736 v 5.664 h -3.936 v -19.84 q 1.088,-0.352 2.656,-0.576 1.6,-0.224 3.136,-0.224 3.488,0 5.344,2.112 1.856,2.112 1.856,5.728 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4708"
+         inkscape:connector-curvature="0" />
+    </g>
+    <g
+       aria-label="Miter join"
+       transform="matrix(0.76997327,0,0,0.76997327,421.26899,14.544752)"
+       style="font-style:normal;font-weight:normal;font-size:18.66666603px;line-height:0;font-family:sans-serif;letter-spacing:0px;word-spacing:4.94999981px;fill:#000000;fill-opacity:1;stroke:none"
+       id="flowRoot4559">
+      <path
+         d="m 22.323875,234.73542 q 0.224,0.672 0.544,1.728 0.352,1.056 0.736,2.272 0.384,1.216 0.768,2.464 0.384,1.248 0.736,2.304 0.384,-1.184 0.8,-2.432 0.416,-1.28 0.8,-2.432 0.384,-1.184 0.704,-2.208 0.32,-1.024 0.576,-1.696 h 3.264 q 0.224,2.592 0.416,5.056 0.224,2.464 0.352,4.896 0.16,2.4 0.256,4.864 0.096,2.432 0.16,4.992 h -3.616 l 0.192,-15.392 -2.432,8.384 h -2.88 l -2.336,-8.384 0.16,15.392 h -3.616 q 0.096,-2.432 0.192,-5.056 0.128,-2.624 0.256,-5.216 0.128,-2.592 0.288,-5.024 0.192,-2.432 0.416,-4.512 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4585"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 42.259875,235.18342 q 0,1.152 -0.736,1.824 -0.736,0.672 -1.76,0.672 -1.024,0 -1.76,-0.672 -0.704,-0.672 -0.704,-1.824 0,-1.184 0.704,-1.856 0.736,-0.672 1.76,-0.672 1.024,0 1.76,0.672 0.736,0.672 0.736,1.856 z m 5.856,18.72 q -1.216,0.576 -2.304,0.768 -1.056,0.224 -1.952,0.224 -1.536,0 -2.592,-0.448 -1.024,-0.448 -1.664,-1.28 -0.608,-0.864 -0.864,-2.08 -0.256,-1.216 -0.256,-2.784 v -5.536 h -4.16 v -3.264 h 8.096 v 9.312 q 0,1.344 0.48,2.016 0.512,0.64 1.664,0.64 0.544,0 1.312,-0.128 0.768,-0.16 1.728,-0.608 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4587"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 53.747875,242.76742 h -3.424 v -3.264 h 3.424 v -3.776 l 3.936,-0.64 v 4.416 h 6.304 v 3.264 h -6.304 v 6.08 q 0,0.832 0.16,1.344 0.16,0.512 0.448,0.8 0.288,0.288 0.704,0.384 0.416,0.096 0.928,0.096 0.544,0 0.992,-0.032 0.48,-0.032 0.896,-0.096 0.448,-0.096 0.896,-0.256 0.48,-0.16 1.024,-0.416 l 0.544,3.392 q -1.088,0.448 -2.368,0.64 -1.248,0.192 -2.432,0.192 -1.376,0 -2.432,-0.224 -1.056,-0.224 -1.792,-0.864 -0.736,-0.64 -1.12,-1.792 -0.384,-1.184 -0.384,-3.072 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4589"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 66.163875,247.15142 q 0,-1.984 0.608,-3.488 0.608,-1.504 1.6,-2.496 0.992,-1.024 2.272,-1.536 1.28,-0.512 2.624,-0.512 3.328,0 5.056,1.984 1.728,1.952 1.728,5.728 0,0.384 -0.032,0.8 0,0.416 -0.032,0.672 h -9.76 q 0,1.472 1.216,2.336 1.216,0.832 3.136,0.832 1.184,0 2.24,-0.256 1.088,-0.256 1.824,-0.512 l 0.544,3.36 q -1.024,0.352 -2.176,0.576 -1.152,0.256 -2.592,0.256 -1.92,0 -3.456,-0.48 -1.504,-0.512 -2.592,-1.472 -1.056,-0.992 -1.632,-2.432 -0.576,-1.44 -0.576,-3.36 z m 10.08,-1.568 q 0,-0.608 -0.16,-1.152 -0.16,-0.576 -0.512,-1.024 -0.352,-0.448 -0.896,-0.704 -0.544,-0.288 -1.344,-0.288 -0.768,0 -1.344,0.256 -0.544,0.256 -0.928,0.704 -0.352,0.448 -0.576,1.024 -0.192,0.576 -0.256,1.184 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4591"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 95.155875,243.50342 q -0.384,-0.096 -0.928,-0.192 -0.512,-0.096 -1.056,-0.16 -0.544,-0.096 -1.056,-0.128 -0.512,-0.032 -0.864,-0.032 -0.832,0 -1.632,0.096 -0.8,0.064 -1.632,0.288 v 11.168 h -3.968 v -14.048 q 1.568,-0.576 3.2,-0.928 1.664,-0.352 3.872,-0.352 0.32,0 0.896,0.032 0.608,0.032 1.28,0.128 0.672,0.064 1.344,0.192 0.704,0.096 1.248,0.288 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4593"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 130.92187,253.55142 q 0,1.824 -0.448,3.04 -0.448,1.248 -1.248,1.984 -0.768,0.768 -1.856,1.088 -1.056,0.32 -2.304,0.32 -1.536,0 -2.848,-0.384 -1.312,-0.384 -2.528,-0.992 l 1.216,-3.328 q 0.8,0.448 1.92,0.832 1.152,0.416 2.08,0.416 0.992,0 1.536,-0.608 0.544,-0.576 0.544,-2.176 v -10.976 h -6.08 v -3.264 h 10.016 z m -0.48,-18.368 q 0,1.152 -0.736,1.824 -0.736,0.672 -1.76,0.672 -1.024,0 -1.76,-0.672 -0.704,-0.672 -0.704,-1.824 0,-1.184 0.704,-1.856 0.736,-0.672 1.76,-0.672 1.024,0 1.76,0.672 0.736,0.672 0.736,1.856 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4595"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 149.22587,246.99142 q 0,1.792 -0.512,3.264 -0.512,1.44 -1.44,2.496 -0.928,1.056 -2.24,1.632 -1.28,0.576 -2.88,0.576 -1.6,0 -2.912,-0.576 -1.28,-0.576 -2.24,-1.632 -0.928,-1.056 -1.44,-2.496 -0.512,-1.472 -0.512,-3.264 0,-1.76 0.512,-3.2 0.544,-1.472 1.472,-2.496 0.96,-1.056 2.272,-1.6 1.312,-0.576 2.848,-0.576 1.568,0 2.848,0.576 1.312,0.544 2.24,1.6 0.96,1.024 1.472,2.496 0.512,1.44 0.512,3.2 z m -10.176,0.032 q 0,2.016 0.704,3.264 0.736,1.248 2.336,1.248 1.536,0 2.304,-1.248 0.8,-1.248 0.8,-3.264 0,-2.016 -0.736,-3.232 -0.704,-1.248 -2.304,-1.248 -1.536,0 -2.336,1.248 -0.768,1.216 -0.768,3.232 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4597"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 159.20987,235.18342 q 0,1.152 -0.736,1.824 -0.736,0.672 -1.76,0.672 -1.024,0 -1.76,-0.672 -0.704,-0.672 -0.704,-1.824 0,-1.184 0.704,-1.856 0.736,-0.672 1.76,-0.672 1.024,0 1.76,0.672 0.736,0.672 0.736,1.856 z m 5.856,18.72 q -1.216,0.576 -2.304,0.768 -1.056,0.224 -1.952,0.224 -1.536,0 -2.592,-0.448 -1.024,-0.448 -1.664,-1.28 -0.608,-0.864 -0.864,-2.08 -0.256,-1.216 -0.256,-2.784 v -5.536 h -4.16 v -3.264 h 8.096 v 9.312 q 0,1.344 0.48,2.016 0.512,0.64 1.664,0.64 0.544,0 1.312,-0.128 0.768,-0.16 1.728,-0.608 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4599"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 168.13787,239.98342 q 1.056,-0.288 2.592,-0.544 1.568,-0.256 3.392,-0.256 1.792,0 2.976,0.512 1.184,0.48 1.856,1.408 0.704,0.896 0.992,2.176 0.288,1.248 0.288,2.784 v 8.48 h -3.936 v -7.968 q 0,-2.112 -0.48,-3.04 -0.48,-0.928 -1.952,-0.928 -0.448,0 -0.896,0.032 -0.416,0.032 -0.896,0.096 v 11.808 h -3.936 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4601"
+         inkscape:connector-curvature="0" />
+    </g>
+    <g
+       aria-label="Bevel join"
+       transform="matrix(0.76997327,0,0,0.76997327,219.5351,14.544752)"
+       style="font-style:normal;font-weight:normal;font-size:18.66666603px;line-height:0;font-family:sans-serif;letter-spacing:0px;word-spacing:4.94999981px;fill:#000000;fill-opacity:1;stroke:none"
+       id="flowRoot4551">
+      <path
+         d="m 32.179875,248.55942 q 0,1.792 -0.608,3.008 -0.608,1.184 -1.664,1.92 -1.024,0.704 -2.464,1.024 -1.408,0.288 -3.04,0.288 -1.312,0 -2.72,-0.16 -1.408,-0.128 -2.784,-0.416 v -19.232 q 1.12,-0.192 2.464,-0.32 1.344,-0.16 2.656,-0.16 2.24,0 3.648,0.48 1.408,0.448 2.208,1.216 0.8,0.736 1.088,1.664 0.288,0.928 0.288,1.856 0,1.408 -0.704,2.496 -0.672,1.088 -1.824,1.728 2.016,0.736 2.72,1.984 0.736,1.248 0.736,2.624 z m -9.344,-2.688 v 5.376 q 0.416,0.064 0.896,0.096 0.512,0.032 0.992,0.032 0.672,0 1.312,-0.096 0.64,-0.128 1.12,-0.416 0.512,-0.32 0.832,-0.864 0.32,-0.576 0.32,-1.44 0,-1.44 -0.928,-2.048 -0.896,-0.64 -2.432,-0.64 z m 1.536,-3.264 q 1.504,0 2.272,-0.64 0.768,-0.672 0.768,-1.824 0,-0.704 -0.224,-1.12 -0.224,-0.448 -0.608,-0.672 -0.384,-0.256 -0.896,-0.32 -0.512,-0.096 -1.056,-0.096 -0.448,0 -0.928,0.032 -0.48,0.032 -0.864,0.096 v 4.544 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4604"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 34.163875,247.15142 q 0,-1.984 0.608,-3.488 0.608,-1.504 1.6,-2.496 0.992,-1.024 2.272,-1.536 1.28,-0.512 2.624,-0.512 3.328,0 5.056,1.984 1.728,1.952 1.728,5.728 0,0.384 -0.032,0.8 0,0.416 -0.032,0.672 h -9.76 q 0,1.472 1.216,2.336 1.216,0.832 3.136,0.832 1.184,0 2.24,-0.256 1.088,-0.256 1.824,-0.512 l 0.544,3.36 q -1.024,0.352 -2.176,0.576 -1.152,0.256 -2.592,0.256 -1.92,0 -3.456,-0.48 -1.504,-0.512 -2.592,-1.472 -1.056,-0.992 -1.632,-2.432 -0.576,-1.44 -0.576,-3.36 z m 10.08,-1.568 q 0,-0.608 -0.16,-1.152 -0.16,-0.576 -0.512,-1.024 -0.352,-0.448 -0.896,-0.704 -0.544,-0.288 -1.344,-0.288 -0.768,0 -1.344,0.256 -0.544,0.256 -0.928,0.704 -0.352,0.448 -0.576,1.024 -0.192,0.576 -0.256,1.184 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4606"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 55.379875,254.54342 q -1.44,-2.848 -2.944,-6.656 -1.472,-3.84 -2.816,-8.384 h 4.128 q 0.288,1.216 0.672,2.592 0.416,1.376 0.864,2.784 0.448,1.376 0.896,2.688 0.48,1.312 0.896,2.4 0.416,-1.088 0.928,-2.4 0.512,-1.312 0.992,-2.688 0.512,-1.408 0.96,-2.784 0.48,-1.376 0.768,-2.592 h 4 q -1.344,4.544 -2.976,8.384 -1.632,3.808 -3.072,6.656 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4608"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 66.163875,247.15142 q 0,-1.984 0.608,-3.488 0.608,-1.504 1.6,-2.496 0.992,-1.024 2.272,-1.536 1.28,-0.512 2.624,-0.512 3.328,0 5.056,1.984 1.728,1.952 1.728,5.728 0,0.384 -0.032,0.8 0,0.416 -0.032,0.672 h -9.76 q 0,1.472 1.216,2.336 1.216,0.832 3.136,0.832 1.184,0 2.24,-0.256 1.088,-0.256 1.824,-0.512 l 0.544,3.36 q -1.024,0.352 -2.176,0.576 -1.152,0.256 -2.592,0.256 -1.92,0 -3.456,-0.48 -1.504,-0.512 -2.592,-1.472 -1.056,-0.992 -1.632,-2.432 -0.576,-1.44 -0.576,-3.36 z m 10.08,-1.568 q 0,-0.608 -0.16,-1.152 -0.16,-0.576 -0.512,-1.024 -0.352,-0.448 -0.896,-0.704 -0.544,-0.288 -1.344,-0.288 -0.768,0 -1.344,0.256 -0.544,0.256 -0.928,0.704 -0.352,0.448 -0.576,1.024 -0.192,0.576 -0.256,1.184 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4610"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 96.115875,253.90342 q -0.096,0.064 -0.448,0.224 -0.32,0.16 -0.864,0.352 -0.544,0.16 -1.344,0.288 -0.768,0.128 -1.76,0.128 -2.72,0 -3.968,-1.6 -1.248,-1.632 -1.248,-4.736 v -12.768 h -4.16 v -3.264 h 8.096 v 16.32 q 0,1.536 0.608,2.08 0.608,0.544 1.536,0.544 1.184,0 1.984,-0.32 0.8,-0.32 1.056,-0.416 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4612"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 130.92187,253.55142 q 0,1.824 -0.448,3.04 -0.448,1.248 -1.248,1.984 -0.768,0.768 -1.856,1.088 -1.056,0.32 -2.304,0.32 -1.536,0 -2.848,-0.384 -1.312,-0.384 -2.528,-0.992 l 1.216,-3.328 q 0.8,0.448 1.92,0.832 1.152,0.416 2.08,0.416 0.992,0 1.536,-0.608 0.544,-0.576 0.544,-2.176 v -10.976 h -6.08 v -3.264 h 10.016 z m -0.48,-18.368 q 0,1.152 -0.736,1.824 -0.736,0.672 -1.76,0.672 -1.024,0 -1.76,-0.672 -0.704,-0.672 -0.704,-1.824 0,-1.184 0.704,-1.856 0.736,-0.672 1.76,-0.672 1.024,0 1.76,0.672 0.736,0.672 0.736,1.856 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4614"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 149.22587,246.99142 q 0,1.792 -0.512,3.264 -0.512,1.44 -1.44,2.496 -0.928,1.056 -2.24,1.632 -1.28,0.576 -2.88,0.576 -1.6,0 -2.912,-0.576 -1.28,-0.576 -2.24,-1.632 -0.928,-1.056 -1.44,-2.496 -0.512,-1.472 -0.512,-3.264 0,-1.76 0.512,-3.2 0.544,-1.472 1.472,-2.496 0.96,-1.056 2.272,-1.6 1.312,-0.576 2.848,-0.576 1.568,0 2.848,0.576 1.312,0.544 2.24,1.6 0.96,1.024 1.472,2.496 0.512,1.44 0.512,3.2 z m -10.176,0.032 q 0,2.016 0.704,3.264 0.736,1.248 2.336,1.248 1.536,0 2.304,-1.248 0.8,-1.248 0.8,-3.264 0,-2.016 -0.736,-3.232 -0.704,-1.248 -2.304,-1.248 -1.536,0 -2.336,1.248 -0.768,1.216 -0.768,3.232 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4616"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 159.20987,235.18342 q 0,1.152 -0.736,1.824 -0.736,0.672 -1.76,0.672 -1.024,0 -1.76,-0.672 -0.704,-0.672 -0.704,-1.824 0,-1.184 0.704,-1.856 0.736,-0.672 1.76,-0.672 1.024,0 1.76,0.672 0.736,0.672 0.736,1.856 z m 5.856,18.72 q -1.216,0.576 -2.304,0.768 -1.056,0.224 -1.952,0.224 -1.536,0 -2.592,-0.448 -1.024,-0.448 -1.664,-1.28 -0.608,-0.864 -0.864,-2.08 -0.256,-1.216 -0.256,-2.784 v -5.536 h -4.16 v -3.264 h 8.096 v 9.312 q 0,1.344 0.48,2.016 0.512,0.64 1.664,0.64 0.544,0 1.312,-0.128 0.768,-0.16 1.728,-0.608 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4618"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 168.13787,239.98342 q 1.056,-0.288 2.592,-0.544 1.568,-0.256 3.392,-0.256 1.792,0 2.976,0.512 1.184,0.48 1.856,1.408 0.704,0.896 0.992,2.176 0.288,1.248 0.288,2.784 v 8.48 h -3.936 v -7.968 q 0,-2.112 -0.48,-3.04 -0.48,-0.928 -1.952,-0.928 -0.448,0 -0.896,0.032 -0.416,0.032 -0.896,0.096 v 11.808 h -3.936 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4620"
+         inkscape:connector-curvature="0" />
+    </g>
+    <g
+       aria-label="Round join"
+       transform="matrix(0.76997327,0,0,0.76997327,11.64219,14.544752)"
+       style="font-style:normal;font-weight:normal;font-size:18.66666603px;line-height:0;font-family:sans-serif;letter-spacing:0px;word-spacing:4.94999981px;fill:#000000;fill-opacity:1;stroke:none"
+       id="flowRoot4537">
+      <path
+         d="m 24.019875,234.51142 q 1.696,0 3.072,0.384 1.376,0.384 2.368,1.184 0.992,0.8 1.504,2.048 0.544,1.216 0.544,2.88 0,1.728 -0.704,3.136 -0.704,1.408 -2.336,2.144 0.544,0.832 1.152,1.856 0.608,0.992 1.184,2.112 0.576,1.088 1.088,2.208 0.544,1.088 0.928,2.08 h -4.128 q -0.736,-1.92 -1.728,-3.68 -0.96,-1.792 -2.08,-3.616 h -2.048 v 7.296 h -3.936 v -19.52 q 0.576,-0.128 1.248,-0.224 0.704,-0.128 1.408,-0.16 0.704,-0.064 1.344,-0.096 0.64,-0.032 1.12,-0.032 z m 3.488,6.464 q 0,-1.568 -0.96,-2.304 -0.96,-0.736 -2.528,-0.736 -0.224,0 -0.544,0.032 -0.32,0 -0.64,0.064 v 5.952 h 0.864 q 2.016,0 2.912,-0.768 0.896,-0.8 0.896,-2.24 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4623"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 48.275875,246.99142 q 0,1.792 -0.512,3.264 -0.512,1.44 -1.44,2.496 -0.928,1.056 -2.24,1.632 -1.28,0.576 -2.88,0.576 -1.6,0 -2.912,-0.576 -1.28,-0.576 -2.24,-1.632 -0.928,-1.056 -1.44,-2.496 -0.512,-1.472 -0.512,-3.264 0,-1.76 0.512,-3.2 0.544,-1.472 1.472,-2.496 0.96,-1.056 2.272,-1.6 1.312,-0.576 2.848,-0.576 1.568,0 2.848,0.576 1.312,0.544 2.24,1.6 0.96,1.024 1.472,2.496 0.512,1.44 0.512,3.2 z m -10.176,0.032 q 0,2.016 0.704,3.264 0.736,1.248 2.336,1.248 1.536,0 2.304,-1.248 0.8,-1.248 0.8,-3.264 0,-2.016 -0.736,-3.232 -0.704,-1.248 -2.304,-1.248 -1.536,0 -2.336,1.248 -0.768,1.216 -0.768,3.232 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4625"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 63.187875,254.03142 q -1.088,0.288 -2.656,0.576 -1.536,0.288 -3.36,0.288 -1.856,0 -3.04,-0.512 -1.184,-0.512 -1.888,-1.44 -0.672,-0.96 -0.928,-2.24 -0.256,-1.28 -0.256,-2.816 v -8.384 h 3.936 v 7.872 q 0,2.08 0.48,3.104 0.512,0.992 1.952,0.992 0.896,0 1.792,-0.16 v -11.808 h 3.968 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4627"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 67.187875,239.98342 q 1.056,-0.288 2.592,-0.544 1.568,-0.256 3.392,-0.256 1.792,0 2.976,0.512 1.184,0.48 1.856,1.408 0.704,0.896 0.992,2.176 0.288,1.248 0.288,2.784 v 8.48 h -3.936 v -7.968 q 0,-2.112 -0.48,-3.04 -0.48,-0.928 -1.952,-0.928 -0.448,0 -0.896,0.032 -0.416,0.032 -0.896,0.096 v 11.808 h -3.936 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4629"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 86.067875,246.92742 q 0,2.016 0.736,3.296 0.768,1.28 2.432,1.28 0.48,0 0.896,-0.032 0.416,-0.032 0.864,-0.096 v -8.192 q -0.448,-0.256 -1.024,-0.416 -0.576,-0.192 -1.216,-0.192 -1.408,0 -2.048,1.12 -0.64,1.12 -0.64,3.232 z m 8.864,7.168 q -1.088,0.352 -2.656,0.576 -1.568,0.224 -3.072,0.224 -3.52,0 -5.344,-2.08 -1.792,-2.08 -1.792,-5.696 0,-3.68 1.504,-5.792 1.536,-2.144 4.512,-2.144 0.8,0 1.568,0.192 0.768,0.16 1.344,0.48 v -6.848 l 3.936,-0.672 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4631"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 130.92187,253.55142 q 0,1.824 -0.448,3.04 -0.448,1.248 -1.248,1.984 -0.768,0.768 -1.856,1.088 -1.056,0.32 -2.304,0.32 -1.536,0 -2.848,-0.384 -1.312,-0.384 -2.528,-0.992 l 1.216,-3.328 q 0.8,0.448 1.92,0.832 1.152,0.416 2.08,0.416 0.992,0 1.536,-0.608 0.544,-0.576 0.544,-2.176 v -10.976 h -6.08 v -3.264 h 10.016 z m -0.48,-18.368 q 0,1.152 -0.736,1.824 -0.736,0.672 -1.76,0.672 -1.024,0 -1.76,-0.672 -0.704,-0.672 -0.704,-1.824 0,-1.184 0.704,-1.856 0.736,-0.672 1.76,-0.672 1.024,0 1.76,0.672 0.736,0.672 0.736,1.856 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4633"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 149.22587,246.99142 q 0,1.792 -0.512,3.264 -0.512,1.44 -1.44,2.496 -0.928,1.056 -2.24,1.632 -1.28,0.576 -2.88,0.576 -1.6,0 -2.912,-0.576 -1.28,-0.576 -2.24,-1.632 -0.928,-1.056 -1.44,-2.496 -0.512,-1.472 -0.512,-3.264 0,-1.76 0.512,-3.2 0.544,-1.472 1.472,-2.496 0.96,-1.056 2.272,-1.6 1.312,-0.576 2.848,-0.576 1.568,0 2.848,0.576 1.312,0.544 2.24,1.6 0.96,1.024 1.472,2.496 0.512,1.44 0.512,3.2 z m -10.176,0.032 q 0,2.016 0.704,3.264 0.736,1.248 2.336,1.248 1.536,0 2.304,-1.248 0.8,-1.248 0.8,-3.264 0,-2.016 -0.736,-3.232 -0.704,-1.248 -2.304,-1.248 -1.536,0 -2.336,1.248 -0.768,1.216 -0.768,3.232 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4635"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 159.20987,235.18342 q 0,1.152 -0.736,1.824 -0.736,0.672 -1.76,0.672 -1.024,0 -1.76,-0.672 -0.704,-0.672 -0.704,-1.824 0,-1.184 0.704,-1.856 0.736,-0.672 1.76,-0.672 1.024,0 1.76,0.672 0.736,0.672 0.736,1.856 z m 5.856,18.72 q -1.216,0.576 -2.304,0.768 -1.056,0.224 -1.952,0.224 -1.536,0 -2.592,-0.448 -1.024,-0.448 -1.664,-1.28 -0.608,-0.864 -0.864,-2.08 -0.256,-1.216 -0.256,-2.784 v -5.536 h -4.16 v -3.264 h 8.096 v 9.312 q 0,1.344 0.48,2.016 0.512,0.64 1.664,0.64 0.544,0 1.312,-0.128 0.768,-0.16 1.728,-0.608 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4637"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 168.13787,239.98342 q 1.056,-0.288 2.592,-0.544 1.568,-0.256 3.392,-0.256 1.792,0 2.976,0.512 1.184,0.48 1.856,1.408 0.704,0.896 0.992,2.176 0.288,1.248 0.288,2.784 v 8.48 h -3.936 v -7.968 q 0,-2.112 -0.48,-3.04 -0.48,-0.928 -1.952,-0.928 -0.448,0 -0.896,0.032 -0.416,0.032 -0.896,0.096 v 11.808 h -3.936 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold'"
+         id="path4639"
+         inkscape:connector-curvature="0" />
+    </g>
+  </g>
+</svg>

+ 155 - 0
examples/models/svg/threejs.svg

@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="210mm"
+   height="297mm"
+   viewBox="0 0 210 297"
+   version="1.1"
+   id="svg8"
+   inkscape:version="0.92.3 (2405546, 2018-03-11)"
+   sodipodi:docname="threejs.svg">
+  <defs
+     id="defs2" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.25000001"
+     inkscape:cx="533.56472"
+     inkscape:cy="387.42134"
+     inkscape:document-units="mm"
+     inkscape:current-layer="flowRoot886"
+     showgrid="false"
+     inkscape:window-width="1920"
+     inkscape:window-height="1015"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
+     inkscape:window-maximized="1"
+     inkscape:snap-global="false" />
+  <metadata
+     id="metadata5">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Capa 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <g
+       aria-label="Three.js"
+       transform="matrix(1.4760313,0,0,1.4760313,-85.535562,-805.12602)"
+       style="font-style:normal;font-weight:normal;font-size:18.66666603px;line-height:0;font-family:sans-serif;letter-spacing:0px;word-spacing:4.94999981px;fill:#66b57f;fill-opacity:1;stroke:#192ea7;stroke-width:6.85148048;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="flowRoot886">
+      <path
+         style="opacity:0.93199978;fill:none;fill-opacity:1;stroke:#d5a063;stroke-width:16.33271599;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         d="M -34.843672,735.8507 A 91.365098,299.21622 83.501293 0 1 227.96853,616.3661 91.365098,299.21622 83.501293 0 1 557.92046,662.5428"
+         id="path849"
+         inkscape:connector-curvature="0" />
+      <path
+         style="opacity:0.93199978;fill:none;fill-opacity:1;stroke:#c6be72;stroke-width:13.32288837;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         d="M -5.039649,732.2031 A 68.474724,265.65322 83.501293 0 1 229.68767,638.40964 68.474724,265.65322 83.501293 0 1 521.24821,667.80071"
+         id="path852"
+         inkscape:connector-curvature="0" />
+      <path
+         style="opacity:0.93199978;fill:none;fill-opacity:1;stroke:#c6aa72;stroke-width:10.7236681;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         d="M 17.771095,729.41199 A 48.859142,241.20674 83.501293 0 1 232.35237,656.60683 48.859142,241.20674 83.501293 0 1 495.51719,670.94944"
+         id="path857"
+         inkscape:connector-curvature="0" />
+      <path
+         style="opacity:0.93199978;fill:none;fill-opacity:1;stroke:#c6be72;stroke-width:13.32288837;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         d="m 521.24821,667.80071 a 68.474724,265.65322 83.501293 0 1 0.0774,0.16447"
+         id="path854"
+         inkscape:connector-curvature="0" />
+      <path
+         style="opacity:0.93199978;fill:none;fill-opacity:1;stroke:#c6aa72;stroke-width:10.7236681;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         d="m 495.51719,670.94944 a 48.859142,241.20674 83.501293 0 1 0.26282,0.44309"
+         id="path859"
+         inkscape:connector-curvature="0" />
+      <path
+         style="opacity:1;fill:#506de4;fill-opacity:1;stroke:#72c6c6;stroke-width:13.32288837;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         id="path825"
+         sodipodi:type="arc"
+         sodipodi:cx="253.33569"
+         sodipodi:cy="704.37823"
+         sodipodi:rx="150.21419"
+         sodipodi:ry="141.25153"
+         sodipodi:start="6.2129773"
+         sodipodi:end="6.1973326"
+         d="M 403.17982,694.46939 A 150.21419,141.25153 0 0 1 264.45925,845.24194 150.21419,141.25153 0 0 1 103.57858,715.38896 150.21419,141.25153 0 0 1 241.04068,563.60065 150.21419,141.25153 0 0 1 402.99663,692.2663"
+         sodipodi:open="true" />
+      <path
+         style="opacity:0.93199978;fill:none;fill-opacity:1;stroke:#c6aa72;stroke-width:10.7236681;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         d="M 496.15836,672.11609 A 48.859142,241.20674 83.501293 0 1 280.74287,749.14475 48.859142,241.20674 83.501293 0 1 18.98356,733.7558 48.859142,241.20674 83.501293 0 1 17.771095,729.41199"
+         id="path834"
+         inkscape:connector-curvature="0" />
+      <path
+         style="opacity:0.93199978;fill:none;fill-opacity:1;stroke:#c6be72;stroke-width:13.32288837;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         d="M 521.76824,668.98919 A 68.474724,265.65322 83.501293 0 1 286.2917,769.37635 68.474724,265.65322 83.501293 0 1 -3.522802,739.03402 68.474724,265.65322 83.501293 0 1 -5.039649,732.2031"
+         id="path827"
+         inkscape:connector-curvature="0" />
+      <path
+         style="opacity:0.93199978;fill:none;fill-opacity:1;stroke:#d5a063;stroke-width:16.33271599;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         d="M 558.44409,663.9168 A 91.365098,299.21622 83.501293 0 1 294.93737,792.08838 91.365098,299.21622 83.501293 0 1 -32.974239,744.90661 91.365098,299.21622 83.501293 0 1 -34.843672,735.8507"
+         id="path829"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 29.808444,700.17242 64.851248,-8.46579 1.459387,16.82538 -23.329483,3.04548 6.028117,69.49866 -18.145153,2.36871 -6.028117,-69.49868 -23.376612,3.05162 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:96px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#66b57f;fill-opacity:1;stroke:#192ea7;stroke-width:7.67327881;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         id="path911"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 160.3733,730.55057 3.42029,39.43269 -16.9669,2.21489 -0.55668,-6.41794 -2.05116,-23.64805 q -0.73722,-8.49943 -1.34296,-11.6364 -0.55861,-3.14315 -1.44308,-4.54801 -1.16526,-1.89446 -2.95724,-2.77155 -1.79699,-0.9349 -3.96498,-0.65188 -5.2786,0.68908 -7.85862,6.11311 -2.58504,5.3662 -1.81773,14.21254 l 2.76331,31.85838 -16.87265,2.20259 -7.80345,-89.96667 16.87264,-2.20259 3.00904,34.69153 q 3.32607,-6.16464 7.38424,-9.38419 4.05313,-3.27739 9.23747,-3.95416 9.14327,-1.19358 14.45309,5.07166 5.35695,6.25908 6.49537,19.38405 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:96px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#66b57f;fill-opacity:1;stroke:#192ea7;stroke-width:7.67327881;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         id="path913"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 214.6602,715.68683 q -2.32546,-0.98285 -4.59074,-1.27188 -2.22315,-0.353 -4.43828,-0.0639 -6.50398,0.84905 -9.5924,6.45638 -3.04632,5.54338 -2.21883,15.08355 l 2.58778,29.83469 -16.87263,2.20259 -5.61689,-64.7575 16.87265,-2.20258 0.92277,10.63874 q 2.70032,-6.78464 6.64417,-10.22316 3.98595,-3.50252 9.87722,-4.27157 0.84836,-0.11077 1.84812,-0.12438 0.99474,-0.0713 2.90503,-0.0284 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:96px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#66b57f;fill-opacity:1;stroke:#192ea7;stroke-width:7.67327881;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         id="path915"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 277.09908,722.27137 0.51154,5.89756 -39.44805,5.14961 q 1.2446,7.20523 5.23672,10.36795 3.99211,3.16272 10.59035,2.30138 5.32571,-0.69523 10.72158,-3.32925 5.43799,-2.698 10.98826,-7.39877 l 1.38415,15.95811 q -5.74481,3.55668 -11.61,5.72571 -5.86016,2.22684 -11.84571,3.00819 -14.3276,1.87036 -23.06494,-5.99404 -8.69522,-7.92835 -10.09442,-24.05992 -1.37413,-15.84245 5.61499,-25.93521 7.03626,-10.09893 20.70404,-11.88315 12.44239,-1.62425 20.68637,6.59692 8.29111,8.215 9.62512,23.59491 z M 259.15835,717.655 q -0.51154,-5.89756 -3.65029,-9.1132 -3.09664,-3.27962 -7.62115,-2.68899 -4.90155,0.63986 -7.66912,4.45111 -2.7726,3.75342 -2.97502,10.21198 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:96px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#66b57f;fill-opacity:1;stroke:#192ea7;stroke-width:7.67327881;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         id="path917"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 342.51589,713.73175 0.51153,5.89755 -39.44803,5.14962 q 1.24459,7.20523 5.2367,10.36795 3.99212,3.16272 10.59035,2.30137 5.32572,-0.69523 10.7216,-3.32924 5.43799,-2.698 10.98824,-7.39877 l 1.38416,15.9581 q -5.74481,3.55668 -11.61,5.72571 -5.86017,2.22683 -11.84571,3.0082 -14.3276,1.87035 -23.06493,-5.99404 -8.69522,-7.92836 -10.09443,-24.05992 -1.37413,-15.84246 5.61499,-25.93522 7.03626,-10.09893 20.70403,-11.88314 12.4424,-1.62426 20.68638,6.59692 8.29111,8.21499 9.62512,23.59491 z m -17.94073,-4.61637 q -0.51154,-5.89756 -3.65028,-9.1132 -3.09665,-3.27963 -7.62115,-2.68899 -4.90155,0.63986 -7.66912,4.45111 -2.77261,3.75342 -2.97503,10.21197 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:96px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#66b57f;fill-opacity:1;stroke:#192ea7;stroke-width:7.67327881;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         id="path919"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 357.86753,722.01911 16.9669,-2.21489 1.94083,22.37605 -16.96689,2.21488 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:96px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#66b57f;fill-opacity:1;stroke:#192ea7;stroke-width:7.67327881;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         id="path921"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 389.20928,675.06636 16.87263,-2.20257 5.51658,63.60111 q 1.12839,13.00931 -3.3699,20.49645 -4.49327,7.54494 -14.15498,8.8062 l -8.34206,1.08899 -1.17854,-13.58751 2.92208,-0.38146 q 4.80729,-0.62755 6.36754,-3.52103 1.56026,-2.89347 0.88323,-10.69906 z m -2.18658,-25.20916 16.87264,-2.20258 1.4644,16.8832 -16.87264,2.20259 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:96px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#66b57f;fill-opacity:1;stroke:#192ea7;stroke-width:7.67327881;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         id="path923"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 463.80349,667.37529 1.3641,15.72681 q -5.6607,-2.06778 -10.824,-2.79712 -5.1633,-0.72936 -9.64068,-0.14486 -4.80728,0.62755 -7.03341,2.43847 -2.18399,1.74696 -1.9182,4.81136 0.21565,2.48624 2.07481,3.58843 1.9063,1.09606 6.5331,1.13527 l 3.01434,0.13277 q 13.13635,0.33173 18.01494,4.37279 4.87858,4.04105 5.73615,13.92814 0.89771,10.34963 -4.87214,16.36548 -5.76984,6.01586 -18.11796,7.6278 -5.23146,0.68292 -10.93024,0.37433 -5.64663,-0.25694 -11.71344,-1.51155 l -1.3641,-15.72682 q 5.30372,2.34828 10.71271,3.1625 5.4561,0.80807 10.92322,0.0944 4.94868,-0.64601 7.30114,-2.64884 2.35248,-2.00285 2.06661,-5.29853 -0.24072,-2.77533 -2.09989,-3.87753 -1.81705,-1.16618 -7.01944,-1.2472 l -3.00933,-0.0749 q -11.41458,-0.26413 -16.34531,-4.35686 -4.93073,-4.09271 -5.75822,-13.63288 -0.89268,-10.29182 4.42592,-16.01487 5.3186,-5.72306 17.19543,-7.27348 4.66588,-0.60909 9.87832,-0.41242 5.21242,0.19667 11.40557,1.25937 z"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:96px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#66b57f;fill-opacity:1;stroke:#192ea7;stroke-width:7.67327881;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         id="path925"
+         inkscape:connector-curvature="0" />
+    </g>
+  </g>
+</svg>

+ 168 - 0
examples/webgl_geometry_text_stroke.html

@@ -0,0 +1,168 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - Simple text from json</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<style>
+			body {
+				font-family: Monospace;
+				background-color: #f0f0f0;
+				margin: 0px;
+				overflow: hidden;
+			}
+			#info {
+				position: absolute;
+				top: 10px;
+				width: 100%;
+				text-align: center;
+			}
+		</style>
+	</head>
+	<body>
+		<div id="info">
+			<a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> webgl - Simple text from json fonts.
+		</div>
+
+		<script src="../build/three.js"></script>
+		<script src="js/controls/OrbitControls.js"></script>
+		<script src="js/loaders/SVGLoader.js"></script>
+
+		<script>
+
+			var camera, scene, renderer;
+
+			init();
+			animate();
+
+			function init( ) {
+
+				camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 10000 );
+				camera.position.set( 0, - 400, 600 );
+
+				var controls = new THREE.OrbitControls( camera );
+				controls.target.set( 0, 0, 0 );
+				controls.update();
+
+				scene = new THREE.Scene();
+				scene.background = new THREE.Color( 0xf0f0f0 );
+
+				var loader = new THREE.FontLoader();
+				loader.load( 'fonts/helvetiker_regular.typeface.json', function ( font ) {
+
+					var xMid, text;
+
+					var color = new THREE.Color( 0x006699 );
+
+					var matDark = new THREE.MeshBasicMaterial( {
+						color: color,
+						side: THREE.DoubleSide
+					} );
+
+					var matLite = new THREE.MeshBasicMaterial( {
+						color: color,
+						transparent: true,
+						opacity: 0.4,
+						side: THREE.DoubleSide
+					} );
+
+					var message = "   Three.js\nStroke text.";
+
+					var shapes = font.generateShapes( message, 100 );
+
+					var geometry = new THREE.ShapeBufferGeometry( shapes );
+
+					geometry.computeBoundingBox();
+
+					xMid = - 0.5 * ( geometry.boundingBox.max.x - geometry.boundingBox.min.x );
+
+					geometry.translate( xMid, 0, 0 );
+
+					// make shape ( N.B. edge view not visible )
+
+					text = new THREE.Mesh( geometry, matLite );
+					text.position.z = - 150;
+					scene.add( text );
+
+					// make line shape ( N.B. edge view remains visible )
+
+					var holeShapes = [];
+
+					for ( var i = 0; i < shapes.length; i ++ ) {
+
+						var shape = shapes[ i ];
+
+						if ( shape.holes && shape.holes.length > 0 ) {
+
+							for ( var j = 0; j < shape.holes.length; j ++ ) {
+
+								var hole = shape.holes[ j ];
+								holeShapes.push( hole );
+
+							}
+
+						}
+
+					}
+
+					shapes.push.apply( shapes, holeShapes );
+
+					var style = THREE.SVGLoader.getStrokeStyle( 5, color.getStyle() );
+
+					var strokeText = new THREE.Group();
+
+					for ( var i = 0; i < shapes.length; i ++ ) {
+
+						var shape = shapes[ i ];
+
+						var points = shape.getPoints();
+
+						var geometry = THREE.SVGLoader.pointsToStroke( points, style );
+
+						geometry.translate( xMid, 0, 0 );
+
+						var strokeMesh = new THREE.Mesh( geometry, matDark );
+						strokeText.add( strokeMesh );
+
+					}
+
+					scene.add( strokeText );
+
+				} ); //end load function
+
+				renderer = new THREE.WebGLRenderer( { antialias: true } );
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				document.body.appendChild( renderer.domElement );
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+			} // end init
+
+			function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			function animate() {
+
+				requestAnimationFrame( animate );
+
+				render();
+
+			}
+
+			function render() {
+
+				renderer.render( scene, camera );
+
+			}
+
+		</script>
+
+	</body>
+</html>

+ 78 - 24
examples/webgl_loader_svg.html

@@ -44,7 +44,7 @@
 
 		<script>
 
-			var renderer, stats, scene, camera, gui, currentURL;
+			var renderer, stats, scene, camera, gui, guiData;
 
 			init();
 			animate();
@@ -81,7 +81,7 @@
 
 				window.addEventListener( 'resize', onWindowResize, false );
 
-				document.body.addEventListener( 'dblclick', function () {
+				document.body.addEventListener( 'dblclick', function ( event ) {
 
 					var group = scene.children[ 1 ];
 					group.traverse( function ( child ) {
@@ -92,9 +92,15 @@
 
 				} );
 
-				currentURL = 'models/svg/tiger.svg';
+				guiData = {
+					currentURL: 'models/svg/tiger.svg',
+					drawFillShapes: true,
+					drawStrokes: true,
+					fillShapesWireframe: false,
+					strokesWireframe: false
+				}
 
-				loadSVG( currentURL );
+				loadSVG( guiData.currentURL );
 
 				createGUI();
 
@@ -102,17 +108,17 @@
 
 			function createGUI() {
 
-				if ( gui ) gui.destroy();
-
-				var guiData = {
-					currentURL: currentURL
-				};
+				if ( gui ) {
+					gui.destroy();
+				}
 
 				gui = new dat.GUI( { width: 350 } );
 
 				gui.add( guiData, 'currentURL', {
 
 					"Tiger": 'models/svg/tiger.svg',
+					"Three.js": 'models/svg/threejs.svg',
+					"Joins and caps": 'models/svg/lineJoinsAndCaps.svg',
 					"Hexagon": 'models/svg/hexagon.svg',
 					"Test 1": 'models/svg/tests/1.svg',
 					"Test 2": 'models/svg/tests/2.svg',
@@ -123,13 +129,21 @@
 					"Test 7": 'models/svg/tests/7.svg',
 					"Test 8": 'models/svg/tests/8.svg'
 
-				} ).name( 'SVG File' ).onChange( function ( value ) {
+				} ).name( 'SVG File' ).onChange( update );
 
-					currentURL = value;
+				gui.add( guiData, 'drawStrokes' ).name( 'Draw strokes' ).onChange( update );
 
-					loadSVG( currentURL );
+				gui.add( guiData, 'drawFillShapes' ).name( 'Draw fill shapes' ).onChange( update );
 
-				} );
+				gui.add( guiData, 'strokesWireframe' ).name( 'Wireframe strokes' ).onChange( update );
+
+				gui.add( guiData, 'fillShapesWireframe' ).name( 'Wireframe fill shapes' ).onChange( update );
+
+				function update() {
+
+					loadSVG( guiData.currentURL );
+
+				}
 
 			}
 
@@ -149,6 +163,7 @@
 				//
 
 				var loader = new THREE.SVGLoader();
+
 				loader.load( url, function ( data ) {
 
 					var paths = data.paths;
@@ -163,22 +178,61 @@
 
 						var path = paths[ i ];
 
-						var material = new THREE.MeshBasicMaterial( {
-							color: path.color,
-							side: THREE.DoubleSide,
-							depthWrite: false
-						} );
+						var fillColor = path.userData.style.fill;
+						if ( guiData.drawFillShapes && fillColor !== undefined && fillColor !== 'none') {
+
+							var material = new THREE.MeshBasicMaterial( {
+								color: new THREE.Color().setStyle( fillColor ),
+								opacity: path.userData.style.fillOpacity,
+								transparent: path.userData.style.fillOpacity !== 1,
+								side: THREE.DoubleSide,
+								depthWrite: false,
+								wireframe: guiData.fillShapesWireframe
+							} );
+
+							var shapes = path.toShapes( true );
+
+							for ( var j = 0; j < shapes.length; j ++ ) {
+
+								var shape = shapes[ j ];
+
+								var geometry = new THREE.ShapeBufferGeometry( shape );
+								var mesh = new THREE.Mesh( geometry, material );
+
+								group.add( mesh );
+
+							}
+
+						}
+
+						var strokeColor = path.userData.style.stroke;
+
+						if ( guiData.drawStrokes && strokeColor !== undefined && strokeColor !== 'none' ) {
+
+							var material = new THREE.MeshBasicMaterial( {
+								color: new THREE.Color().setStyle( strokeColor ),
+								opacity: path.userData.style.strokeOpacity,
+								transparent: path.userData.style.strokeOpacity !== 1,
+								side: THREE.DoubleSide,
+								depthWrite: false,
+								wireframe: guiData.strokesWireframe
+							} );
+
+							for ( var j = 0, jl = path.subPaths.length; j < jl; j ++ ) {
+
+								subPath = path.subPaths[ j ];
+
+								var geometry = THREE.SVGLoader.pointsToStroke( subPath.getPoints(), path.userData.style );
 
-						var shapes = path.toShapes( true );
+								if ( geometry ) {
 
-						for ( var j = 0; j < shapes.length; j ++ ) {
+									var mesh = new THREE.Mesh( geometry, material );
 
-							var shape = shapes[ j ];
+									group.add( mesh );
 
-							var geometry = new THREE.ShapeBufferGeometry( shape );
-							var mesh = new THREE.Mesh( geometry, material );
+								}
 
-							group.add( mesh );
+							}
 
 						}