1 /* 2 * ShapeTools.js 3 * 4 * Sweet Home 3D, Copyright (c) 2024 Space Mushrooms <[email protected]> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 */ 20 21 // Requires scene3d.js 22 23 24 /** 25 * Gathers some useful tools for shapes. 26 * @class 27 * @author Emmanuel Puybaret 28 */ 29 var ShapeTools = { 30 parsedShapes : {} 31 }; 32 33 /** 34 * Returns the line stroke matching the given line styles. 35 * @param {number} thickness 36 * @param {Polyline.CapStyle} capStyle 37 * @param {Polyline.JoinStyle} joinStyle 38 * @param {number[]} dashPattern 39 * @param {number} dashOffset 40 * @return {Object} 41 */ 42 ShapeTools.getStroke = function (thickness, capStyle, joinStyle, dashPattern, dashOffset) { 43 var strokeCapStyle; 44 switch (capStyle) { 45 case Polyline.CapStyle.ROUND: 46 strokeCapStyle = java.awt.BasicStroke.CAP_ROUND; 47 break; 48 case Polyline.CapStyle.SQUARE: 49 strokeCapStyle = java.awt.BasicStroke.CAP_SQUARE; 50 break; 51 default: 52 strokeCapStyle = java.awt.BasicStroke.CAP_BUTT; 53 break; 54 } 55 56 var strokeJoinStyle; 57 switch (joinStyle) { 58 case Polyline.JoinStyle.ROUND: 59 case Polyline.JoinStyle.CURVED: 60 strokeJoinStyle = java.awt.BasicStroke.JOIN_ROUND; 61 break; 62 case Polyline.JoinStyle.BEVEL: 63 strokeJoinStyle = java.awt.BasicStroke.JOIN_BEVEL; 64 break; 65 default: 66 strokeJoinStyle = java.awt.BasicStroke.JOIN_MITER; 67 break; 68 } 69 70 var dashPhase = 0; 71 if (dashPattern != null) { 72 if (!Array.isArray(dashPattern)) { 73 dashPattern = undefined; 74 dashPhase = undefined; 75 } else { 76 dashPattern = dashPattern.slice(0); 77 for (var i = 0; i < dashPattern.length; i++) { 78 dashPattern [i] *= thickness; 79 dashPhase += dashPattern [i]; 80 } 81 dashPhase *= dashOffset; 82 } 83 } 84 return new java.awt.BasicStroke(thickness, strokeCapStyle, strokeJoinStyle, 10, dashPattern, dashPhase); 85 } 86 87 /** 88 * Returns the shape of a polyline. 89 * @param {Array} points 90 * @param {boolean} curved 91 * @param {boolean} closedPath 92 * @return {Object} 93 */ 94 ShapeTools.getPolylineShape = function (points, curved, closedPath) { 95 if (curved) { 96 var polylineShape = new java.awt.geom.GeneralPath(); 97 for (var i = 0, n = closedPath ? points.length : points.length - 1; i < n; i++) { 98 var curve2D = new java.awt.geom.CubicCurve2D.Float(); 99 var previousPoint = points[i === 0 ? points.length - 1 : i - 1]; 100 var point = points[i]; 101 var nextPoint = points[i === points.length - 1 ? 0 : i + 1]; 102 var vectorToBisectorPoint = [nextPoint[0] - previousPoint[0], nextPoint[1] - previousPoint[1]]; 103 var nextNextPoint = points[(i + 2) % points.length]; 104 var vectorToBisectorNextPoint = [point[0] - nextNextPoint[0], point[1] - nextNextPoint[1]]; 105 curve2D.setCurve(point[0], point[1], 106 point[0] + (i !== 0 || closedPath ? vectorToBisectorPoint[0] / 3.625 : 0), 107 point[1] + (i !== 0 || closedPath ? vectorToBisectorPoint[1] / 3.625 : 0), 108 nextPoint[0] + (i !== points.length - 2 || closedPath ? vectorToBisectorNextPoint[0] / 3.625 : 0), 109 nextPoint[1] + (i !== points.length - 2 || closedPath ? vectorToBisectorNextPoint[1] / 3.625 : 0), 110 nextPoint[0], nextPoint[1]); 111 polylineShape.append(curve2D, true); 112 } 113 return polylineShape; 114 } else { 115 return ShapeTools.getShape(points, closedPath, null); 116 } 117 } 118 119 /** 120 * Returns the shape matching the coordinates in <code>points</code> array 121 * or the shape matching the given <a href="http://www.w3.org/TR/SVG/paths.html">SVG path shape</a> 122 * if the first parameter is a string. 123 * @param {Array|string} points array or a SVG path 124 * @param {boolean} [closedPath] 125 * @param {java.awt.geom.AffineTransform} [transform] 126 * @return {Object} 127 * @protected 128 * @ignore 129 */ 130 ShapeTools.getShape = function(points, closedPath, transform) { 131 if (points instanceof Array) { 132 var path = new java.awt.geom.GeneralPath(); 133 path.moveTo(Math.fround(points[0][0]), Math.fround(points[0][1])); 134 for (var i = 1; i < points.length; i++) { 135 path.lineTo(Math.fround(points[i][0]), Math.fround(points[i][1])); 136 } 137 if (closedPath) { 138 path.closePath(); 139 } 140 if (transform != null) { 141 path.transform(transform); 142 } 143 return path; 144 } else { 145 var svgPathShape = points; 146 var shape2D = ShapeTools.parsedShapes [svgPathShape]; 147 if (!shape2D) { 148 shape2D = new java.awt.geom.Rectangle2D.Float(0, 0, 1, 1); 149 try { 150 var pathProducer = new org.apache.batik.parser.AWTPathProducer(); 151 var pathParser = new org.apache.batik.parser.PathParser(); 152 pathParser.setPathHandler(pathProducer); 153 pathParser.parse(svgPathShape); 154 shape2D = pathProducer.getShape(); 155 } catch (ex) { 156 // Keep default value if Batik is not available or if the path is incorrect 157 } 158 ShapeTools.parsedShapes[svgPathShape] = shape2D; 159 } 160 return shape2D; 161 } 162 }