PageSourceSearch

https://renderdiagrams.org/lib/sceneObject_dimension.js?v=1.0

js renderdiagrams.org collected 2026-10-08 02:13:02 UTC 14,131 bytes, 535 lines download raw bytes

1// ----------------------------------------------------------------------------------------------------------------
2// Dimension
3// ----------------------------------------------------------------------------------------------------------------
4function Dimension(A, B)
5{
6	var kAligned    = 0;
7	var kHorizontal = 1;
8	var kVertical   = 2;
9	var kAngle      = 3;
10
11	this.scene				= null;
12	this.selected			= false;
13	this.A					= A;
14	this.B					= B;
15	this.textPoint			= avg(A,B);
16	this.center				= avg(A,B);	// for angles
17	this.alignment			= kAligned;
18	this.decimals			= 2;
19	this.text				= "{value.toFixed(1)}cm";
20	this.appearance			= new Appearance();
21	this.visible			= true;
22	this.frozen				= false;
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 dim = new Dimension(";
40
41		str += "new Vector(" + this.A.x + ", " + this.A.y + ")";
42		str += ", new Vector(" + this.B.x + ", " + this.B.y + ")";
43		str += ");\n";
44
45		str += this.appearance.saveAsJavascript("dim");
46		str += "dim.textPoint = new Vector(" + this.textPoint.x + ", " + this.textPoint.y + ");\n";
47		str += "dim.center = new Vector(" + this.center.x + ", " + this.center.y + ");\n";
48		str += "dim.alignment = " + this.alignment + ";\n";
49		str += "dim.lineOffset = " + this.lineOffset + ";\n";
50		str += "dim.textOffset = " + this.textOffset + ";\n";
51		str += "dim.text = \"" + this.text + "\";\n";
52		str += "dim.visible = " + this.visible + ";\n";
53		str += "dim.frozen = " + this.frozen + ";\n";
54
55		if (this.name != undefined)
56		{
57			str += "dim.name = \"" + this.name + "\";\n";
58		}
59
60		str += "scene.addObject(dim);\n";
61		return str;
62	}
63
64	this.addChangeListener = function(listener)
65	{
66		this.onChangeListeners.push(listener);
67	}
68
69	this.onChange = function(changeDetails)
70	{
71		// Backwards compatibility
72		this.appearance.SetFromOldProperties(this);
73
74		var extensionLineDir;
75
76		if (this.alignment == kHorizontal)
77		{
78			extensionLineDir = new Vector(0, -1);
79		}
80		else if (this.alignment == kVertical)
81		{
82			extensionLineDir = new Vector(1, 0);
83		}
84		else
85		{
86			extensionLineDir = transpose(sub(this.B, this.A).unit());
87		}
88
89		if (this.alignment == kAngle)
90		{
91			var radius = distance(this.textPoint, this.center);
92
93			var dirA = sub(this.A, this.center).unit();
94			var dirB = sub(this.B, this.center).unit();
95
96			var pA = mad(dirA, radius, this.center);
97			var pB = mad(dirB, radius, this.center);
98			var pM = mad(avg(dirA, dirB).unit(), radius, this.center);
99
100			this.boundsMin = pA;
101			this.boundsMax = pA;
102
103			this.boundsMin = min(pB, this.boundsMin);
104			this.boundsMax = max(pB, this.boundsMax);
105
106			this.boundsMin = min(pM, this.boundsMin);
107			this.boundsMax = max(pM, this.boundsMax);
108		}
109		else
110		{
111			var offsetA = dot(sub(this.textPoint, this.A), extensionLineDir);
112			var offsetB = dot(sub(this.textPoint, this.B), extensionLineDir);
113
114			var pA1 = mad(extensionLineDir, offsetA, this.A);
115			var pB1 = mad(extensionLineDir, offsetB, this.B);
116
117			var t0 = dot(sub(this.textPoint, pA1), sub(pB1, pA1)) / distanceSqr(pB1, pA1);
118
119			var pX1 = mad(sub(pB1, pA1), t0, pA1);
120
121			this.boundsMin = this.A;
122			this.boundsMax = this.A;
123
124			this.boundsMin = min(this.B, this.boundsMin);
125			this.boundsMax = max(this.B, this.boundsMax);
126
127			this.boundsMin = min(pA1, this.boundsMin);
128			this.boundsMax = max(pA1, this.boundsMax);
129
130			this.boundsMin = min(pB1, this.boundsMin);
131			this.boundsMax = max(pB1, this.boundsMax);
132
133			this.boundsMin = min(pX1, this.boundsMin);
134			this.boundsMax = max(pX1, this.boundsMax);
135		}
136
137		for (var i=0; i!=this.onChangeListeners.length; ++i)
138		{
139			this.onChangeListeners[i](this, changeDetails);
140		}
141	}
142	
143	this.draw = function(camera)
144	{
145		if (this.A == undefined || this.B == undefined)
146			return;
147
148		if (this.alignment == kAngle)
149		{
150			var radius = distance(this.textPoint, this.center);
151
152			var angleA = toAngle(sub(this.A, this.center));
153			var angleB = toAngle(sub(this.B, this.center));
154
155			var angleMin = Math.min(angleA, angleB);
156			var angleMax = Math.max(angleA, angleB);
157
158			camera.drawArc(this.center, radius, angleMin, angleMax, this.appearance.GetLineColor(), this.appearance.GetLineWidth(this.selected), undefined, undefined, 20, 20)
159
160			var text = this.text;
161
162			var angleDeg = toDegrees(angleMax - angleMin);
163
164			var value = angleDeg;
165
166			var expr = /{(.*)}/.exec(this.text);
167
168			var valueStr = "invalid expr";
169
170			if (expr != null && expr.length == 2)
171			{
172				valueStr = eval(expr[1]);
173			}
174
175			text = this.text.replace(/{.*}/, valueStr );
176
177			var textDir = sub(this.textPoint, this.center).unit();
178
179			var textAnchor = mad( textDir, camera.invScale(5), this.textPoint);
180
181			var textHAlign = (textDir.x > 0) ? "left" : "right";
182			var textVAlign = (textDir.y > 0) ? "bottom" : "top";
183			
184			var textAngle = 0;
185
186			camera.drawText(textAnchor, text, "#000000", textHAlign, textAngle, undefined, textVAlign);
187		}
188		else
189		{
190			var extensionLineDir;
191
192			if (this.alignment == kHorizontal)
193			{
194				extensionLineDir = new Vector(0, -1);
195			}
196			else if (this.alignment == kVertical)
197			{
198				extensionLineDir = new Vector(1, 0);
199			}
200			else
201			{
202				extensionLineDir = transpose(sub(this.B, this.A).unit());
203			}
204
205			var offsetA = dot(sub(this.textPoint, this.A), extensionLineDir);
206			var offsetB = dot(sub(this.textPoint, this.B), extensionLineDir);
207
208			var pA0 = mad(extensionLineDir, Math.sign(offsetA) * camera.invScale(5), this.A);
209			var pA1 = mad(extensionLineDir, offsetA, this.A);
210			var pA2 = mad(extensionLineDir, offsetA + Math.sign(offsetA) * camera.invScale(5), this.A);
211
212			var pB0 = mad(extensionLineDir, Math.sign(offsetB) * camera.invScale(5), this.B);
213			var pB1 = mad(extensionLineDir, offsetB, this.B);
214			var pB2 = mad(extensionLineDir, offsetB + Math.sign(offsetB) * camera.invScale(5), this.B);
215
216			var t0 = dot(sub(this.textPoint, pA1), sub(pB1, pA1)) / distanceSqr(pB1, pA1);
217
218			var pX0 = (t0>=1) ? pB1 : (t0<=0) ? pA1 : mad(sub(pB1, pA1), t0, pA1);
219			var pX1 = mad(sub(pB1, pA1), t0, pA1);
220
221			camera.drawLine(pA0, pA2, this.appearance.GetLineColor(), this.appearance.GetLineWidth(this.selected));
222			camera.drawLine(pB0, pB2, this.appearance.GetLineColor(), this.appearance.GetLineWidth(this.selected));
223			camera.drawLine(pX0, pX1, this.appearance.GetLineColor(), this.appearance.GetLineWidth(this.selected));
224
225			camera.drawArrow(avg(pA1, pB1), pA1, 20, this.appearance.GetLineColor(), this.appearance.GetLineWidth(this.selected));
226			camera.drawArrow(avg(pA1, pB1), pB1, 20, this.appearance.GetLineColor(), this.appearance.GetLineWidth(this.selected));
227
228			var textAlign = "center";
229
230			if (t0>1)
231			{
232				textAlign = "right";
233			}
234			else if (t0<0)
235			{
236				textAlign = "left";
237			}
238
239			var textAngle = toAngle(transpose(extensionLineDir));
240
241			var factor = -1;
242
243			if (textAngle > Math.PI / 2 || textAngle <= -Math.PI / 2)
244			{
245				textAngle -= Math.PI;
246				factor = 1;
247			}
248
249			var textAnchor = this.textPoint;
250			textAnchor = mad(extensionLineDir.neg(), factor * camera.invScale(5), textAnchor);
251
252			var dist = distance(this.A, this.B);
253
254			if (this.alignment == kHorizontal)
255				dist = abs(this.A.x - this.B.x);
256			else if (this.alignment == kVertical)
257				dist = abs(this.A.y - this.B.y);
258
259			var text = this.text;
260
261			var value = dist;
262
263			var expr = /{(.*)}/.exec(this.text);
264
265			var valueStr = "invalid expr";
266
267			if (expr != null && expr.length == 2)
268			{
269				valueStr = eval(expr[1]);
270			}
271
272			text = this.text.replace(/{.*}/, valueStr );
273
274			camera.drawText(textAnchor, text, "#000000", textAlign, textAngle);
275		}
276	}
277	
278	this.hitTest = function(a, b, camera)
279	{
280		if (this.alignment == kAngle)
281		{
282			var radius = distance(this.textPoint, this.center);
283
284			var angleA = toAngle(sub(this.A, this.center));
285			var angleB = toAngle(sub(this.B, this.center));
286
287			var angleMin = Math.min(angleA, angleB);
288			var angleMax = Math.max(angleA, angleB);
289
290			if (overlapRectArc(a, b, this.center, radius, angleMin, angleMax))
291				return true;
292		}
293		else
294		{
295			var extensionLineDir;
296
297			if (this.alignment == kHorizontal)
298			{
299				extensionLineDir = new Vector(0, -1);
300			}
301			else if (this.alignment == kVertical)
302			{
303				extensionLineDir = new Vector(1, 0);
304			}
305			else
306			{
307				extensionLineDir = transpose(sub(this.B, this.A).unit());
308			}
309
310			var offsetA = dot(sub(this.textPoint, this.A), extensionLineDir);
311			var offsetB = dot(sub(this.textPoint, this.B), extensionLineDir);
312
313			var pA1 = mad(extensionLineDir, offsetA, this.A);
314			var pB1 = mad(extensionLineDir, offsetB, this.B);
315
316			var pA2 = mad(extensionLineDir, offsetA + Math.sign(offsetA) * camera.invScale(5), this.A);
317			var pB2 = mad(extensionLineDir, offsetB + Math.sign(offsetB) * camera.invScale(5), this.B);
318
319			var t0 = dot(sub(this.textPoint, pA1), sub(pB1, pA1)) / distanceSqr(pB1, pA1);
320
321			var pX0 = (t0>=1) ? pB1 : (t0<=0) ? pA1 : mad(sub(pB1, pA1), t0, pA1);
322			var pX1 = mad(sub(pB1, pA1), t0, pA1);
323
324			if (	overlapRectLine(a, b, this.A, pA2) ||
325					overlapRectLine(a, b, this.B, pB2) ||
326					overlapRectLine(a, b, pA2, pB2) ||
327					overlapRectLine(a, b, pX0, pX1) )
328				return true;
329		}
330
331		return false;
332	}
333
334	this.getSnapPoints = function ()
335	{
336		var points = [];
337
338		points.push( { type: "node", p: this.A} );
339		points.push( { type: "node", p: this.B} );
340
341		if (this.alignment == kAngle)
342		{
343			points.push( { type: "node", p: this.center } );
344		}
345		else
346		{
347		}
348
349		return points;
350	}
351
352	this.getDragPoints = function(localSpace, camera)
353	{
354		if (this.isFrozen())
355			return [];
356
357		var points = [ ];
358
359		points.push( this.textPoint );
360		points.push( this.A );
361		points.push( this.B );
362
363		if (this.alignment == kAngle)
364		{
365			points.push( this.center );
366		}
367
368		return points;
369	}
370
371	this.drawDragPoint = function(camera, index, position, localSpace, alpha)
372	{
373		if (alpha == undefined)
374			alpha = 1;
375
376		camera.drawRectangle(position, camera.invScale(10), "rgba(255,0,0," + alpha + ")", 2);
377	}
378
379	this.setDragPointPos = function(index, p, localSpace, dragStartPoint, camera)
380	{
381		if (index == 0)
382		{
383			if (localSpace && this.alignment != kAngle)
384			{
385				if ( ( p.x <= Math.min(this.A.x, this.B.x) || p.x >= Math.max(this.A.x, this.B.x) ) && ( p.y >= Math.min(this.A.y, this.B.y) && p.y <= Math.max(this.A.y, this.B.y) ) )
386				{
387					this.alignment = kVertical;
388				}
389				else if ( ( p.y <= Math.min(this.A.y, this.B.y) || p.y >= Math.max(this.A.y, this.B.y) ) && ( p.x >= Math.min(this.A.x, this.B.x) && p.x <= Math.max(this.A.x, this.B.x) ) )
390				{
391					this.alignment = kHorizontal;
392				}
393				else
394				{
395					this.alignment = kAligned;
396				}
397			}
398
399			this.textPoint = p;
400		}
401		else if (index == 1 || index == 2)
402		{
403			var textRelPos = dot( sub(this.textPoint, this.A), sub(this.B, this.A) ) / distanceSqr(this.A, this.B);
404			var textOffset = dot( sub(this.textPoint, this.A), transpose( sub(this.B, this.A).unit() ) );
405
406			if (this.alignment == kHorizontal)
407				textRelPos = (this.textPoint.x - this.A.x) / (this.B.x - this.A.x);
408			else if (this.alignment == kVertical)
409				textRelPos = (this.textPoint.y - this.A.y) / (this.B.y - this.A.y);
410
411			if (index == 1)
412				this.A = p;
413			else
414				this.B = p;
415
416
417			if (this.point1dir != undefined && this.point2dir != undefined)
418			{
419				var dirDot = dot(this.point1dir, this.point2dir);
420
421				if (Math.abs(dirDot) != 1)
422				{
423					var rayDir = this.point1dir;
424
425					var intersection = intersectRayRay(this.A, rayDir, this.B, this.point2dir, true);
426
427					if (intersection.hit)
428					{
429						this.alignment = kAngle;
430						this.center = intersection.P;
431
432						if (this.pointCount == 1)
433						{
434							this.text = "{value.toFixed(0)}°";
435							this.decimals = 0;
436						}
437					}
438				}
439			}
440
441			if (this.alignment == kAligned)
442			{
443				this.textPoint = mad( sub(this.B, this.A), textRelPos, mad( transpose( sub(this.B, this.A).unit() ), textOffset, this.A ) );
444			}
445			else if (this.alignment == kHorizontal && textRelPos>=0 && textRelPos<=1)
446			{
447				this.textPoint.x = lerp(this.A.x, this.B.x, textRelPos);
448			}
449			else if (this.alignment == kVertical && textRelPos>=0 && textRelPos<=1)
450			{
451				this.textPoint.y = lerp(this.A.y, this.B.y, textRelPos);
452			}
453		}
454		else if (index == 3)
455		{
456			this.center = p;
457		}
458
459		this.onChange();
460	}
461
462	this.getOrigin = function()
463	{
464		return this.A;
465	}
466	
467	this.setOrigin = function(p)
468	{
469		var delta = sub(p, this.A);
470
471		this.A = add(this.A, delta);
472		this.B = add(this.B, delta);
473		this.textPoint = add(this.textPoint, delta);
474
475		if (this.center !== undefined)
476		{
477			this.center = add(this.center, delta);
478		}
479
480		this.onChange();
481	}
482
483	this.getProperties = function()
484	{
485		var appearanceControl = new PopoutContainer(undefined, "images/color_wheel.svg");
486		{
487			appearanceControl.addControl("", new AppearanceControl(this.appearance, true, false, function(){ this.onChange(); }.bind(this)) );
488		}
489
490		return	[
491					{ name: "Appearance", control:appearanceControl },
492					{ name: "Align", control: new Dropdown(["Aligned", "Horizontal", "Vertical", "Angle"], this.alignment, function (value) { this.alignment = value; this.onChange(); }.bind(this)) },
493					{ name: "Decimals", control: new PopoutSlider(0, 5, this.decimals, 1, function (value) { this.decimals = value; this.onChange(); }.bind(this)) },
494					{ name: "Text", control: new TextBox(this.text, true, false, 64, function (value) { this.text = value; this.onChange(); }.bind(this)) },
495				];
496	}
497
498	this.toggleVisibility = function(v)
499	{
500		var newVisible = (v!==undefined) ? v : !this.visible;
501		if (this.visible != newVisible)
502		{
503			this.visible = newVisible;
504			this.onChange();
505		}
506	}
507
508	this.isVisible = function()
509	{
510		return this.visible;
511	}
512
513	this.toggleFrozen = function(f)
514	{
515		this.frozen = (f!==undefined) ? f : !this.frozen;
516		this.onChange();
517	}
518
519	this.isFrozen = function()
520	{
521		return this.frozen;
522	}
523
524	this.setSelected = function(selected)
525	{
526		this.selected = selected;
527	}
528
529	this.isSelected = function()
530	{
531		return this.selected;
532	}
533
534	this.onChange();
535}

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.