PageSourceSearch

https://renderdiagrams.org/lib/sceneObject_pen.js?v=1.2

js renderdiagrams.org collected 2026-10-08 02:14:43 UTC 8,799 bytes, 334 lines download raw bytes

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.