PageSourceSearch

https://renderdiagrams.org/lib/sceneObject_arcWall.js?v=1.3

js renderdiagrams.org collected 2026-10-08 02:14:17 UTC 14,613 bytes, 477 lines download raw bytes

1// ----------------------------------------------------------------------------------------------------------------
2// Arc wall
3// ----------------------------------------------------------------------------------------------------------------
4function ArcWall(centerPos, radius, startAngle, endAngle)
5{
6	this.scene				= null;
7	this.center				= centerPos;
8	this.radius				= radius;
9	this.startAngle			= startAngle;
10	this.endAngle			= endAngle;
11	this.selected			= false;
12	this.visible			= true;
13	this.frozen				= false;
14	this.convex				= false;
15	this.appearance			= new Appearance();
16	this.BRDF				= Phong;
17	this.BRDFIndex			= 1;
18	this.BRDFOptions		= [ Lambert, Phong ];
19	this.roughness			= 0;
20	this.metalness			= 0;
21	this.intensity			= 1;
22	this.onChangeListeners	= [];
23	this.boundsMin			= new Vector(Number.MAX_VALUE,Number.MAX_VALUE);
24	this.boundsMax			= new Vector(-Number.MAX_VALUE,-Number.MAX_VALUE);
25
26	this.getBoundsMin = function()
27	{
28		return this.boundsMin;
29	}
30
31	this.getBoundsMax = function()
32	{
33		return this.boundsMax;
34	}
35
36	this.saveAsJavascript = function()
37	{
38		var str = "var aw = new ArcWall(";
39
40		str += "new Vector(" + this.center.x + ", " + this.center.y + ")";
41		str += ", " + this.radius;
42		str += ", " + (this.startAngle * 180 / Math.PI).toFixed(2) + " * Math.PI/180";
43		str += ", " + (this.endAngle * 180 / Math.PI).toFixed(2) + " * Math.PI/180";
44		str += ");\n";
45
46		str += "aw.convex = " + this.convex + ";\n";
47		str += "aw.roughness = " + this.roughness + ";\n";
48		str += "aw.metalness = " + this.metalness + ";\n";
49		str += this.appearance.saveAsJavascript("aw");
50		str += "aw.intensity = " + this.intensity + ";\n";
51		str += "aw.BRDFIndex = " + this.BRDFIndex + ";\n";
52		str += "aw.visible = " + this.visible + ";\n";
53		str += "aw.frozen = " + this.frozen + ";\n";
54		str += "aw.onChange();\n";
55
56		if (this.name != undefined)
57		{
58			str += "aw.name = \"" + this.name + "\";\n";
59		}
60
61		str += "scene.addObject(aw);\n";
62		return str;
63	}
64
65	this.addChangeListener = function(listener)
66	{
67		this.onChangeListeners.push(listener);
68	}
69
70	this.onChange = function(changeDetails)
71	{
72		// Backwards compatibility
73		this.appearance.SetFromOldProperties(this);
74
75		this.BRDF = this.BRDFOptions[this.BRDFIndex];
76
77		this.boundsMin = new Vector(Number.MAX_VALUE,Number.MAX_VALUE);
78		this.boundsMax = new Vector(-Number.MAX_VALUE,-Number.MAX_VALUE);
79
80		var p0 = add(this.center, mul(fromAngle(this.startAngle), this.radius));
81		var p1 = add(this.center, mul(fromAngle(this.endAngle), this.radius));
82
83		this.boundsMin = min(this.boundsMin, p0);
84		this.boundsMin = min(this.boundsMin, p1);
85		this.boundsMax = max(this.boundsMax, p0);
86		this.boundsMax = max(this.boundsMax, p1);
87
88		var a90 = Math.ceil(this.startAngle/(Math.PI/2)) * Math.PI/2;
89		for (var a = a90; a <= this.endAngle; a += Math.PI/2)
90		{
91			var p = add(this.center, mul(fromAngle(a), this.radius));
92			this.boundsMin = min(this.boundsMin, p);
93			this.boundsMax = max(this.boundsMax, p);
94		}
95
96		if (changeDetails == undefined)
97			changeDetails = {};
98
99		changeDetails.blockersMoved = 1;
100
101		for (var i=0; i!=this.onChangeListeners.length; ++i)
102		{
103			this.onChangeListeners[i](this, changeDetails);
104		}
105	}
106	
107	this.draw = function(camera)
108	{
109		if (this.radius <= 0)
110			return;
111
112		camera.drawArc(this.center, this.radius, this.startAngle, this.endAngle, this.appearance.GetLineColor(), this.appearance.GetLineWidth(this.selected), undefined, undefined, undefined, undefined, this);
113		
114		var angleStep = 10 * Math.PI / 180;
115
116		if ((this.endAngle - this.startAngle) > 0)
117		{
118			angleStep = (this.endAngle - this.startAngle) / Math.round( Math.abs(this.endAngle - this.startAngle) / angleStep );
119		}
120
121		var size = sub(this.boundsMax, this.boundsMin).length();
122		var sizePixels = Math.max(1, size * camera.getUnitScale());
123		var arcLength = (this.endAngle-this.startAngle) * this.radius;
124		var arcLengthPixels = arcLength * camera.getUnitScale();
125		var approxCount = Math.floor(arcLengthPixels / Math.min(sizePixels/15, 15));
126		var stepPixels = arcLengthPixels / approxCount;
127		var stepUnits = stepPixels / camera.getUnitScale();
128		var stepAngle = stepUnits / this.radius;
129
130		var delta = stepUnits;
131
132		if (this.convex)
133			delta = -delta;
134
135		var hatchPoints = [];
136
137		for (var a = this.startAngle + stepAngle; a <= this.endAngle; a += stepAngle)
138		{
139			var p0 = add(this.center, mul(fromAngle(a), this.radius));
140			var p1 = add(this.center, mul(fromAngle(a - stepAngle), this.radius + delta));
141			hatchPoints.push(p0);
142			hatchPoints.push(p1);
143		}
144
145		camera.drawLines(hatchPoints, this.appearance.GetLineColor(), this.appearance.GetLineWidth(this.selected));
146
147		//camera.drawArc(add(this.center, mul(fromAngle(this.startAngle), this.radius)), 0.1, 0, 2 * Math.PI);
148		//camera.drawText(add(this.center, mul(fromAngle(this.startAngle), this.radius)), (this.startAngle * 180 / Math.PI).toFixed(1));
149		//camera.drawText(add(this.center, mul(fromAngle(this.endAngle), this.radius)), (this.endAngle * 180 / Math.PI).toFixed(1));
150	}
151	
152	this.hitTest = function(a, b, camera)
153	{
154		var rectMin = min(a, b);
155		var rectMax = max(a, b);
156		
157		return overlapRectArc(rectMin, rectMax, this.center, this.radius, this.startAngle, this.endAngle);
158	}
159	
160	this.rayHitTest = function(rayPos, rayDir, rayRadius)
161	{
162		var results = [{N:rayDir, P:rayPos, hit:false, tRay:0}];
163
164		var rayDirL = rayDir.length();
165
166		var RO = sub(this.center, rayPos);
167
168		var perpDist = Math.abs(dot(RO, transpose(rayDir))/rayDirL);
169
170		// bail if we're too far away
171		if (perpDist > (this.radius + rayRadius))
172			return results;
173
174		// Distance of arc center along ray. 
175		var distanceToArcCenter = dot(RO, rayDir)/rayDirL;
176
177		var effectiveArcRadius = this.radius;
178
179		if (this.convex)
180			effectiveArcRadius += rayRadius;
181		else
182			effectiveArcRadius -= rayRadius;
183
184		// How far do we need to backtrack along the ray such that the distance between the point 
185		// on the ray and the center is equal to arc radius?
186		var delta = Math.sqrt(effectiveArcRadius*effectiveArcRadius - perpDist*perpDist);
187
188		// For convex we move backwards, towards the ray origin. For concave, forwards.
189		if (this.convex)
190			delta *= -1;
191
192		// Distance along the ray to the point of impact
193		var distanceToImpact = distanceToArcCenter + delta;
194
195		// if the ray starts inside a convex arc or outside a concave one, bail
196		if (distanceToImpact < -rayRadius)
197			return results;
198
199		// impact point
200		var P = add(rayPos, mul(rayDir, distanceToImpact/rayDirL));
201
202		if (rayRadius>0)
203		{
204			P = add(P, mul(sub(this.center, P).unit(), this.convex ? rayRadius : -rayRadius));
205		}
206
207		// check if impact point is outside the (stand,end) angle range
208		var OP = sub(P, this.center);
209		var OPAngle = toAngle(OP);
210
211		if (OPAngle < 0)
212			OPAngle	+= Math.PI*2;
213
214		var midAngle = (this.startAngle + this.endAngle) * 0.5;
215
216		if (Math.cos(OPAngle - midAngle) < Math.cos(this.startAngle - midAngle))
217			return results;
218
219		results[0].hit = true;
220		results[0].N = this.convex ? OP.unit() : OP.unit().neg();
221		results[0].tRay = distanceToImpact/rayDirL;
222		results[0].P = P;
223		//results[0].color = this.appearance.GetLineColorHex();
224		results[0].roughness = this.roughness;
225		results[0].metalness = this.metalness;
226		results[0].intensity = this.intensity;
227		results[0].BRDF = this.BRDF;
228
229		return results;
230	}
231
232	this.getSnapPoints = function ()
233	{
234		var points = [	{ type: "center", p: this.center },
235						{ type: "node", p: add(this.center, mul(fromAngle(this.startAngle), this.radius)) },
236						{ type: "node", p: add(this.center, mul(fromAngle(this.endAngle), this.radius))} ];
237
238		var angleStep = 45 * Math.PI / 180;
239
240		for (var a=Math.ceil(this.startAngle/angleStep)*angleStep; a<=Math.floor(this.endAngle/angleStep)*angleStep; a+=angleStep)
241		{
242			points.push({ type: "node", p: add(this.center, mul(fromAngle(a), this.radius))});
243		}
244
245		return points;
246	}
247
248	this.getDragPoints = function(localSpace, camera)
249	{
250		if (this.isFrozen())
251			return [];
252
253		var points = [];
254
255		if (localSpace)
256		{
257			points.push(add(this.center, mul(fromAngle(this.startAngle + (this.endAngle-this.startAngle)/20), this.radius)));
258			points.push(add(this.center, mul(fromAngle(this.startAngle), this.radius*0.95)));
259
260			points.push(add(this.center, mul(fromAngle(this.endAngle - (this.endAngle - this.startAngle) / 20), this.radius)));
261			points.push(add(this.center, mul(fromAngle(this.endAngle), this.radius*0.95)));
262		}
263		else
264		{
265			points.push(add(this.center, mul(fromAngle(this.startAngle), this.radius)));
266			points.push(add(this.center, mul(fromAngle(this.endAngle), this.radius)));
267			points.push(this.center);
268		}
269
270		return points;
271	}
272
273	this.drawDragPoint = function(camera, index, position, localSpace, alpha)
274	{
275		if (alpha == undefined)
276			alpha = 1;
277
278		if (localSpace)
279		{
280			if (index == 0)
281			{
282				camera.drawArc(this.center, this.radius, this.startAngle, this.startAngle + (this.endAngle-this.startAngle)/10, "rgba(255,0,0," + alpha + ")", 4);
283			}
284			else if (index == 1)
285			{
286				camera.drawArrow(	add(this.center, mul(fromAngle(this.startAngle), this.radius)), 
287									add(this.center, mul(fromAngle(this.startAngle), this.radius - camera.invScale(50))), 13, "rgba(255,0,0," + alpha + ")", 5);
288			}
289			else if (index == 2)
290			{
291				camera.drawArc(this.center, this.radius, this.endAngle - (this.endAngle-this.startAngle)/10, this.endAngle, "rgba(255,0,0," + alpha + ")", 4);
292			}
293			else if (index == 3)
294			{
295				camera.drawArrow(	add(this.center, mul(fromAngle(this.endAngle), this.radius)), 
296									add(this.center, mul(fromAngle(this.endAngle), this.radius - camera.invScale(50))), 13, "rgba(255,0,0," + alpha + ")", 5);
297			}
298		}
299		else
300		{
301			if (index == 0)
302				camera.drawRectangle(add(this.center, mul(fromAngle(this.startAngle), this.radius)), camera.invScale(10), "rgba(255,0,0," + alpha + ")", 2);
303			else if (index == 1)
304				camera.drawRectangle(add(this.center, mul(fromAngle(this.endAngle), this.radius)), camera.invScale(10), "rgba(255,0,0," + alpha + ")", 2);
305			else if (index == 2)
306				camera.drawRectangle(this.center, camera.invScale(10), "rgba(255,0,0," + alpha + ")", 2);
307		}
308	}
309
310	this.setDragPointPos = function(index, p, localSpace, dragStartPoint, camera)
311	{
312		var startAngleChanged = false;
313		var endAngleChanged = false;
314		var radiusChanged = false;
315
316		if (localSpace)
317		{
318			if (index==0)
319			{
320				this.startAngle = toAngle(sub(p, this.center));
321				startAngleChanged = true;
322			}
323			else if (index==1 || index==3)
324			{
325				this.radius = distance(p, this.center);
326				radiusChanged = true;
327			}
328			else if (index==2)
329			{
330				this.endAngle = toAngle(sub(p, this.center));
331				endAngleChanged = true;
332			}
333		}
334		else
335		{
336			if (index==0 || index==1)
337			{
338				var pointA = (index==0) ? p : add(this.center, mul(fromAngle(this.startAngle), this.radius));
339				var pointB = (index==1) ? p : add(this.center, mul(fromAngle(this.endAngle), this.radius));
340
341				this.center = avg(pointA, pointB);
342
343				this.startAngle = toAngle(sub(pointA, this.center));
344
345				this.endAngle = toAngle(sub(pointB, this.center));
346
347				this.radius = distance(pointA, this.center);
348
349				startAngleChanged = true;
350				endAngleChanged = true;
351				radiusChanged = true;
352			}
353			else if (index==2)
354			{
355				this.center = p;
356			}
357		}
358
359		if (this.startAngle < 0)
360			this.startAngle += Math.PI*2;
361
362		if (this.endAngle < 0)
363			this.endAngle += Math.PI*2;
364
365		if (this.endAngle<=this.startAngle)
366			this.startAngle -= Math.PI * 2;
367
368		if (camera != undefined)
369		{
370			var text = "";
371		
372			if (startAngleChanged)
373				text += "Start Angle: " + (this.startAngle * 180 / Math.PI).toFixed(0) + "°";
374
375			if (endAngleChanged)
376				text += " End Angle: " + (this.endAngle * 180 / Math.PI).toFixed(0) + "°";
377
378			if (radiusChanged)
379				text += " Radius: " + this.radius.toFixed(1);
380
381			camera.drawText(this.center, text, "#000000", "center", 0, "12px Arial");
382		}
383
384		this.onChange();
385	}
386
387	this.getOrigin = function()
388	{
389		return this.center;
390	}
391	
392	this.setOrigin = function(p)
393	{
394		this.center = p;
395		this.onChange();
396	}
397
398	this.transform = function(currentRect, newRect)
399	{
400		var localCoord = new Vector(	dot(sub(this.getOrigin(), currentRect.center), currentRect.scaledXAxis),
401										dot(sub(this.getOrigin(), currentRect.center), currentRect.scaledYAxis) );
402
403		localCoord.x /= currentRect.scaledXAxis.lengthSqr();
404		localCoord.y /= currentRect.scaledYAxis.lengthSqr();
405
406		this.setOrigin( add(mad(localCoord.x, newRect.scaledXAxis, newRect.center), mul(localCoord.y, newRect.scaledYAxis)) );
407
408		if (newRect.scaledXAxis.lengthSqr() > newRect.scaledYAxis.lengthSqr())
409		{
410			this.radius *= Math.sqrt(newRect.scaledYAxis.lengthSqr() / currentRect.scaledYAxis.lengthSqr());
411		}
412		else
413		{
414			this.radius *= Math.sqrt(newRect.scaledXAxis.lengthSqr() / currentRect.scaledXAxis.lengthSqr());
415		}
416
417		this.startAngle += toAngle(newRect.scaledXAxis) - toAngle(currentRect.scaledXAxis);
418		this.endAngle += toAngle(newRect.scaledXAxis) - toAngle(currentRect.scaledXAxis);
419
420		this.onChange();
421	}
422
423	this.getProperties = function()
424	{
425		var appearanceControl = new PopoutContainer(undefined, "images/color_wheel.svg");
426		{
427			appearanceControl.addControl("", new AppearanceControl(this.appearance, true, false, function(){ this.onChange(); }.bind(this)) );
428		}
429
430		return	[
431					{ name: "Appearance", control:appearanceControl },
432					{ name: "", control: new PlainButton("Flip Normals", function () { this.convex = !this.convex; this.onChange(); }.bind(this)) },
433					{ name: "Roughness", control: new PopoutSlider(0, 1, this.roughness, 0.1, function (value) { this.roughness = value; this.onChange(); }.bind(this)) },
434					{ name: "Metalness", control: new PopoutSlider(0, 1, this.metalness, 0.1, function (value) { this.metalness = value; this.onChange(); }.bind(this)) },
435					{ name: "Intensity", control: new PopoutSlider(0, 10, this.intensity, 0.1, function (value) { this.intensity = value; this.onChange(); }.bind(this)) },
436					{ name: "BRDF", control: new Dropdown(["Lambert", "Phong"], this.BRDFIndex, function (value) { this.BRDFIndex = value; this.onChange(); }.bind(this)) },
437				];
438	}
439
440	this.toggleVisibility = function(v)
441	{
442		var newVisible = (v!==undefined) ? v : !this.visible;
443		if (this.visible != newVisible)
444		{
445			this.visible = newVisible;
446			this.onChange();
447		}
448	}
449
450	this.isVisible = function()
451	{
452		return this.visible;
453	}
454
455	this.toggleFrozen = function(f)
456	{
457		this.frozen = (f!==undefined) ? f : !this.frozen;
458		this.onChange();
459	}
460
461	this.isFrozen = function()
462	{
463		return this.frozen;
464	}
465
466	this.setSelected = function(selected)
467	{
468		this.selected = selected;
469	}
470
471	this.isSelected = function()
472	{
473		return this.selected;
474	}
475
476	this.onChange();
477}

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.