1 /* 2 * Polyline3D.js 3 * 4 * Sweet Home 3D, Copyright (c) 2024 Space Mushrooms <info@sweethome3d.com> 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 // Object3DBranch.js 23 // geom.js 24 // stroke.js 25 26 27 /** 28 * Creates the 3D polyline matching the given home <code>polyline</code>. 29 * @param {Polyline} polyline 30 * @param {Home} home 31 * @param {UserPreferences} [preferences] 32 * @param {boolean} waitModelAndTextureLoadingEnd 33 * @constructor 34 * @extends Object3DBranch 35 * @author Emmanuel Puybaret 36 */ 37 function Polyline3D(polyline, home, preferences, waitModelAndTextureLoadingEnd) { 38 if (waitModelAndTextureLoadingEnd === undefined) { 39 // 3 parameters 40 waitModelAndTextureLoadingEnd = preferences; 41 preferences = null; 42 } 43 Object3DBranch.call(this, polyline, home, preferences); 44 45 if (Polyline3D.ARROW === null) { 46 Polyline3D.ARROW = new java.awt.geom.GeneralPath(); 47 Polyline3D.ARROW.moveTo(-5, -2); 48 Polyline3D.ARROW.lineTo(0, 0); 49 Polyline3D.ARROW.lineTo(-5, 2); 50 } 51 52 this.setCapability(Group3D.ALLOW_CHILDREN_EXTEND); 53 54 this.update(); 55 } 56 Polyline3D.prototype = Object.create(Object3DBranch.prototype); 57 Polyline3D.prototype.constructor = Polyline3D; 58 59 Polyline3D.ARROW = null; 60 61 Polyline3D.prototype.update = function() { 62 var polyline = this.getUserData(); 63 if (polyline.isVisibleIn3D() 64 && (polyline.getLevel() == null 65 || polyline.getLevel().isViewableAndVisible())) { 66 var stroke = ShapeTools.getStroke(polyline.getThickness(), polyline.getCapStyle(), polyline.getJoinStyle(), 67 polyline.getDashStyle() !== Polyline.DashStyle.SOLID ? polyline.getDashPattern() : null, // null renders better closed shapes with a solid style 68 polyline.getDashOffset()); 69 var polylineShape = ShapeTools.getPolylineShape(polyline.getPoints(), 70 polyline.getJoinStyle() === Polyline.JoinStyle.CURVED, polyline.isClosedPath()); 71 72 var firstPoint = null; 73 var secondPoint = null; 74 var beforeLastPoint = null; 75 var lastPoint = null; 76 for (var it = polylineShape.getPathIterator(null, 0.5); !it.isDone(); it.next()) { 77 var pathPoint = [0, 0]; 78 if (it.currentSegment(pathPoint) !== java.awt.geom.PathIterator.SEG_CLOSE) { 79 if (firstPoint === null) { 80 firstPoint = pathPoint; 81 } else if (secondPoint === null) { 82 secondPoint = pathPoint; 83 } 84 beforeLastPoint = lastPoint; 85 lastPoint = pathPoint; 86 } 87 } 88 89 var angleAtStart = Math.atan2(firstPoint[1] - secondPoint[1], 90 firstPoint[0] - secondPoint[0]); 91 var angleAtEnd = Math.atan2(lastPoint[1] - beforeLastPoint[1], 92 lastPoint[0] - beforeLastPoint[0]); 93 var arrowDelta = polyline.getCapStyle() !== Polyline.CapStyle.BUTT 94 ? polyline.getThickness() / 2 95 : 0; 96 var polylineShapes = [this.getArrowShape(firstPoint, angleAtStart, polyline.getStartArrowStyle(), polyline.getThickness(), arrowDelta), 97 this.getArrowShape(lastPoint, angleAtEnd, polyline.getEndArrowStyle(), polyline.getThickness(), arrowDelta), 98 stroke.createStrokedShape(polylineShape)]; 99 var polylineArea = new java.awt.geom.Area(); 100 for (var i = 0; i < polylineShapes.length; i++) { 101 var shape = polylineShapes[i]; 102 if (shape != null) { 103 polylineArea.add(new java.awt.geom.Area(shape)); 104 } 105 } 106 107 var polylinePoints = this.getAreaPoints(polylineArea, 0.5, false); 108 var pointsCount = 0; 109 for (var i = 0; i < polylinePoints.length; i++) { 110 pointsCount += polylinePoints [i].length; 111 } 112 var vertices = new Array(pointsCount); 113 var stripCounts = new Array(polylinePoints.length); 114 var selectionIndices = new Array(pointsCount * 2); 115 for (var i = 0, j = 0, k = 0; i < polylinePoints.length; i++) { 116 var points = polylinePoints [i]; 117 var initialIndex = j; 118 for (var l = 0; l < points.length; l++) { 119 var point = points [l]; 120 selectionIndices [k++] = j; 121 if (l > 0) { 122 selectionIndices [k++] = j; 123 } 124 vertices [j++] = vec3.fromValues(point [0], 0, point [1]); 125 } 126 selectionIndices [k++] = initialIndex; 127 stripCounts [i] = points.length; 128 } 129 130 var geometryInfo = new GeometryInfo3D(GeometryInfo3D.POLYGON_ARRAY); 131 geometryInfo.setCoordinates(vertices); 132 var normals = new Array(vertices.length); 133 var normal = vec3.fromValues(0, 1, 0); 134 for (var i = 0; i < vertices.length; i++) { 135 normals [i] = normal; 136 } 137 geometryInfo.setNormals(normals); 138 geometryInfo.setStripCounts(stripCounts); 139 var geometry = geometryInfo.getIndexedGeometryArray(); 140 141 var selectionGeometry = new IndexedLineArray3D(vertices, selectionIndices); 142 143 var transformGroup; 144 var selectionAppearance; 145 if (this.getChildren().length === 0) { 146 var group = new BranchGroup3D(); 147 transformGroup = new TransformGroup3D(); 148 transformGroup.setCapability(TransformGroup3D.ALLOW_TRANSFORM_WRITE); 149 group.addChild(transformGroup); 150 151 var appearance = new Appearance3D(); 152 this.updateAppearanceMaterial(appearance, Object3DBranch.DEFAULT_COLOR, Object3DBranch.DEFAULT_AMBIENT_COLOR, 0); 153 appearance.setCullFace(Appearance3D.CULL_NONE); 154 var shape = new Shape3D(geometry, appearance); 155 shape.setCapability(Shape3D.ALLOW_GEOMETRY_WRITE); 156 transformGroup.addChild(shape); 157 this.addChild(group); 158 159 var selectionAppearance = this.getSelectionAppearance(); 160 var selectionLinesShape = new Shape3D(selectionGeometry, selectionAppearance); 161 selectionLinesShape.setCapability(Shape3D.ALLOW_GEOMETRY_WRITE); 162 selectionLinesShape.setPickable(false); 163 transformGroup.addChild(selectionLinesShape); 164 } else { 165 transformGroup = this.getChild(0).getChild(0); 166 var shape = transformGroup.getChild(0); 167 shape.setGeometry(geometry, 0); 168 169 selectionLinesShape = transformGroup.getChild(1); 170 selectionLinesShape.setGeometry(selectionGeometry, 0); 171 selectionAppearance = selectionLinesShape.getAppearance(); 172 } 173 174 var transform = mat4.create(); 175 mat4.fromTranslation(transform, vec3.fromValues(0, polyline.getGroundElevation() + (polyline.getElevation() < 0.05 ? 0.05 : 0), 0)); 176 transformGroup.setTransform(transform); 177 this.updateAppearanceMaterial(transformGroup.getChild(0).getAppearance(), polyline.getColor(), polyline.getColor(), 0); 178 179 selectionAppearance.setVisible(this.getUserPreferences() != null 180 && this.getUserPreferences().isEditingIn3DViewEnabled() 181 && this.getHome().isItemSelected(polyline)); 182 } else { 183 this.removeAllChildren(); 184 } 185 } 186 187 /** 188 * Returns the shape of polyline arrow at the given point and orientation. 189 * @param {Array} point 190 * @param {number} angle 191 * @param {Polyline.ArrowStyle} arrowStyle 192 * @param {number} thickness 193 * @param {number} arrowDelta 194 * @return {Object} 195 * @private 196 */ 197 Polyline3D.prototype.getArrowShape = function (point, angle, arrowStyle, thickness, arrowDelta) { 198 if (arrowStyle != null 199 && arrowStyle !== Polyline.ArrowStyle.NONE) { 200 var transform = java.awt.geom.AffineTransform.getTranslateInstance(point[0], point[1]); 201 transform.rotate(angle); 202 transform.translate(arrowDelta, 0); 203 var scale = Math.pow(thickness, 0.66) * 2; 204 transform.scale(scale, scale); 205 var arrowPath = new java.awt.geom.GeneralPath(); 206 switch (arrowStyle) { 207 case Polyline.ArrowStyle.DISC: 208 arrowPath.append(new java.awt.geom.Ellipse2D.Float(-3.5, -2, 4, 4), false); 209 break; 210 case Polyline.ArrowStyle.OPEN: 211 var arrowStroke = new java.awt.BasicStroke((thickness / scale / 0.9), java.awt.BasicStroke.CAP_BUTT, java.awt.BasicStroke.JOIN_MITER); 212 arrowPath.append(arrowStroke.createStrokedShape(Polyline3D.ARROW).getPathIterator(java.awt.geom.AffineTransform.getScaleInstance(0.9, 0.9), 0), false); 213 break; 214 case Polyline.ArrowStyle.DELTA: 215 var deltaPath = new java.awt.geom.GeneralPath(Polyline3D.ARROW); 216 deltaPath.closePath(); 217 arrowPath.append(deltaPath.getPathIterator(java.awt.geom.AffineTransform.getTranslateInstance(1.65, 0), 0), false); 218 break; 219 default: 220 return null; 221 } 222 return arrowPath.createTransformedShape(transform); 223 } 224 return null; 225 } 226