1// ---------------------------------------------------------------------------------------------------------------- 2// Pen 3// ---------------------------------------------------------------------------------------------------------------- 4var lastPenAppearance = new Appearance("#4287f5", 1, 2); 5 6function Pen() 7{ 8 this.scene = null; 9 this.points = []; 10 this.pointsAvg = []; 11 this.bezierCurves = []; 12 this.pointSmoothingFactor = 0.5; 13 this.bezierErrorFactor = 0.5; 14 this.drawLines = false; 15 this.drawBezier = true; 16 this.drawBezierPoints = false; 17 this.arrowStart = false; 18 this.arrowEnd = false; 19 this.selected = false; 20 this.visible = true; 21 this.frozen = false; 22 this.appearance = lastPenAppearance.copy(); 23 this.onChangeListeners = []; 24 this.boundsMin = new Vector(Number.MAX_VALUE,Number.MAX_VALUE); 25 this.boundsMax = new Vector(-Number.MAX_VALUE,-Number.MAX_VALUE); 26 27 this.getBoundsMin = function() 28 { 29 return this.boundsMin; 30 } 31 32 this.getBoundsMax = function() 33 { 34 return this.boundsMax; 35 } 36 37 this.saveAsJavascript = function() 38 { 39 var str = "var pen = new Pen();\n"; 40 41 str += "pen.points = ["; 42 43 for (var i = 0; i != this.points.length; ++i) 44 { 45 if (i > 0) 46 { 47 str += ", "; 48 } 49 50 str += "new Vector(" + this.points[i].x + ", " + this.points[i].y + ")"; 51 } 52 53 str += "];\n"; 54 55 str += "pen.pointSmoothingFactor = " + this.pointSmoothingFactor + ";\n"; 56 str += "pen.bezierError = " + this.bezierErrorFactor + ";\n"; 57 str += "pen.drawLines = " + this.drawLines + ";\n"; 58 str += "pen.drawBezier = " + this.drawBezier + ";\n"; 59 str += "pen.drawBezierPoints = " + this.drawBezierPoints + ";\n"; 60 str += "pen.arrowStart = " + this.arrowStart + ";\n"; 61 str += "pen.arrowEnd = " + this.arrowEnd + ";\n"; 62 str += this.appearance.saveAsJavascript("pen"); 63 str += "pen.visible = " + this.visible + ";\n"; 64 str += "pen.frozen = " + this.frozen + ";\n"; 65 66 if (this.name != undefined) 67 { 68 str += "pen.name = \"" + this.name + "\";\n"; 69 } 70 71 str += "pen.finalize();\n"; 72 73 str += "scene.addObject(pen);\n"; 74 return str; 75 } 76 77 this.addChangeListener = function (listener) 78 { 79 this.onChangeListeners.push(listener); 80 } 81 82 this.onChange = function(changeDetails) 83 { 84 // Backwards compatibility 85 this.appearance.SetFromOldProperties(this); 86 87 lastPenAppearance = this.appearance.copy(); 88 89 this.smoothPoints(); 90 91 this.boundsMin = new Vector(Number.MAX_VALUE,Number.MAX_VALUE); 92 this.boundsMax = new Vector(-Number.MAX_VALUE,-Number.MAX_VALUE); 93 94 for (var i = 0; i != this.pointsAvg.length; ++i) 95 { 96 this.boundsMin = min(this.boundsMin, this.pointsAvg[i]); 97 this.boundsMax = max(this.boundsMax, this.pointsAvg[i]); 98 } 99 100 101 for (var i=0; i!=this.onChangeListeners.length; ++i) 102 { 103 this.onChangeListeners[i](this, changeDetails); 104 } 105 } 106 107 this.smoothPoints = function() 108 { 109 var k = Math.round(this.pointSmoothingFactor * 10); 110 111 if (k>0) 112 { 113 for (var i = Math.max(0, this.pointsAvg.length-k-1); i != this.points.length; ++i) 114 { 115 var avgPoint = new Vector(0,0); 116 var sumWeight = 0; 117 118 for (var j= i-k; j <= i+k; ++j) 119 { 120 if (j>=0 && j<this.points.length) 121 { 122 var weight = Math.pow(1 - Math.abs(j-i)/k, 2); 123 avgPoint = mad(this.points[j], weight, avgPoint); 124 sumWeight += weight; 125 } 126 } 127 128 if (i >= this.pointsAvg.length) 129 this.pointsAvg.push( div(avgPoint, sumWeight) ); 130 else 131 this.pointsAvg[i] = div(avgPoint, sumWeight); 132 } 133 } 134 else 135 { 136 this.pointsAvg = this.points.slice(); 137 } 138 } 139 140 this.addPoint = function(p, camera) 141 { 142 if (this.points.length>0) 143 { 144 if (distanceSqr(p, this.points[this.points.length-1]) <= Math.pow(camera.invScale(1), 2)) 145 { 146 return; 147 } 148 } 149 150 this.points.push( p.copy() ); 151 152 if (this.points.length > 1) 153 this.onChange(); 154 } 155 156 this.finalize = function() 157 { 158 this.pointsAvg = []; 159 160 this.smoothPoints(); 161 162 if (this.points.length > 1) 163 { 164 var maxError = this.bezierErrorFactor * 0.25; 165 166 this.bezierCurves = fitBezierCurve(this.pointsAvg, maxError); 167 this.onChange(); 168 } 169 } 170 171 this.draw = function(camera) 172 { 173 var drawArrows = false;
174 175 if (this.drawLines || this.bezierCurves.length == 0) 176 { 177 drawArrows = true; 178 camera.drawLineStrip(this.pointsAvg, false, this.appearance.GetLineColor(), this.appearance.GetLineWidth(this.selected), this.appearance.GetLineDash(), this.appearance.GetFillColor()); 179 } 180 181 if (this.drawBezier) 182 { 183 drawArrows = true; 184 camera.drawBezierCurves( this.bezierCurves, this.appearance.GetLineColor(), this.appearance.GetLineWidth(this.selected), this.appearance.GetLineDash(), this.appearance.GetFillColor(), this); 185 } 186 187 if (this.drawBezierPoints) 188 { 189 for (var i = 0; i != this.bezierCurves.length; ++i) 190 { 191 camera.drawCross( this.bezierCurves[i][0], camera.invScale(5)); 192 camera.drawCross( this.bezierCurves[i][3], camera.invScale(5)); 193 } 194 } 195 196 // TODO: line/bezier extends a few pixels outside arrowhead! 197 if (drawArrows && this.arrowStart) 198 { 199 camera.drawArrow(this.pointsAvg[1], this.pointsAvg[0], 20, this.appearance.GetLineColor(), this.appearance.GetLineWidth(this.selected), this.appearance.GetLineDash(), this); 200 } 201 202 if (drawArrows && this.arrowEnd) 203 { 204 camera.drawArrow(this.pointsAvg[this.pointsAvg.length-2], this.pointsAvg[this.pointsAvg.length-1], 20, this.appearance.GetLineColor(), this.appearance.GetLineWidth(this.selected), this.appearance.GetLineDash(), this); 205 } 206 } 207 208 this.hitTest = function(a, b, camera) 209 { 210 for (var i = 0; i != this.bezierCurves.length; ++i) 211 { 212 if (overlapRectBezier(a, b, this.bezierCurves[i])) 213 return true; 214 } 215 216 return false; 217 218 if (this.pointsAvg.length>1) 219 { 220 for (var i = 0; i != this.pointsAvg.length-1; ++i) 221 { 222 if (overlapRectLine(a, b, this.pointsAvg[i], this.pointsAvg[i+1])) 223 return true; 224 } 225 } 226 227 return false; 228 } 229 230 this.getSnapPoints = function() 231 { 232 var points = []; 233 234 if (this.bezierCurves.length>0) 235 points.push({ type: "node", p: this.bezierCurves[0 ][0]}); 236 237 for (var i = 0; i != this.bezierCurves.length; ++i) 238 { 239 points.push({ type: "node", p: this.bezierCurves[i][3]}); 240 } 241 242 return points; 243 } 244 245 this.getDragPoints = function(localSpace, camera) 246 { 247 if (this.isFrozen()) 248 return []; 249 250 return []; 251 } 252 253 this.drawDragPoint = function(camera, index, position, localSpace, alpha) 254 { 255 } 256 257 this.setDragPointPos = function(index, p, localSpace, dragStartPoint, camera) 258 { 259 } 260 261 this.getOrigin = function() 262 { 263 return this.pointsAvg[0]; 264 } 265 266 this.setOrigin = function(p) 267 { 268 var delta = sub(p, this.pointsAvg[0]); 269 270 for (var i = 0; i != this.points.length; ++i) 271 { 272 this.points[i] = add(this.points[i], delta); 273 } 274 275 this.finalize(); 276 } 277 278 this.getProperties = function() 279 { 280 var appearanceControl = new PopoutContainer(undefined, "images/color_wheel.svg"); 281 { 282 appearanceControl.addControl("", new AppearanceControl(this.appearance, true, true, function(){ this.onChange(); }.bind(this)) ); 283 } 284 285 return [ 286 {name: "Appearance", control:appearanceControl }, 287 {name: "Point Smoothing", control: new Slider(0, 100, this.pointSmoothingFactor * 100, 1, function (value) { this.pointSmoothingFactor = value / 100; this.pointsAvg = []; this.onChange(); }.bind(this)) }, 288 {name: "Bezier Error Threshold", control: new Slider(0, 100, this.bezierErrorFactor * 100, 1, function (value) { this.bezierErrorFactor = value / 100; this.finalize(); }.bind(this)) }, 289 {name: "drawLines", control: new TickBox(this.drawLines, function (value) { this.drawLines = value; this.onChange({refreshProperties:true}); }.bind(this)) }, 290 {name: "drawBezier", control: new TickBox(this.drawBezier, function (value) { this.drawBezier = value; this.onChange({refreshProperties:true}); }.bind(this)) }, 291 {name: "drawBezierPoints", control: new TickBox(this.drawBezierPoints, function (value) { this.drawBezierPoints = value; this.onChange({refreshProperties:true}); }.bind(this)) }, 292 {name: "Arrow Start", control: new TickBox(this.arrowStart, function (value) { this.arrowStart = value; this.onChange(); }.bind(this)) }, 293 {name: "Arrow End", control: new TickBox(this.arrowEnd, function (value) { this.arrowEnd = value; this.onChange(); }.bind(this)) }, 294 ]; 295 } 296 297 this.toggleVisibility = function(v) 298 { 299 var newVisible = (v!==undefined) ? v : !this.visible; 300 if (this.visible != newVisible) 301 { 302 this.visible = newVisible; 303 this.onChange(); 304 } 305 } 306 307 this.isVisible = function() 308 { 309 return this.visible; 310 } 311 312 this.toggleFrozen = function(f) 313 { 314 this.frozen = (f!==undefined) ? f : !this.frozen; 315 this.onChange(); 316 } 317 318 this.isFrozen = function() 319 { 320 return this.frozen; 321 } 322 323 this.setSelected = function(selected) 324 { 325 this.selected = selected; 326 } 327 328 this.isSelected = function() 329 { 330 return this.selected; 331 } 332 333 this.onChange(); 334}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.