@joint/core 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +376 -0
- package/README.md +49 -0
- package/dist/geometry.js +6486 -0
- package/dist/geometry.min.js +8 -0
- package/dist/joint.d.ts +5536 -0
- package/dist/joint.js +39629 -0
- package/dist/joint.min.js +8 -0
- package/dist/joint.nowrap.js +39626 -0
- package/dist/joint.nowrap.min.js +8 -0
- package/dist/vectorizer.js +9135 -0
- package/dist/vectorizer.min.js +8 -0
- package/dist/version.mjs +3 -0
- package/index.js +3 -0
- package/joint.mjs +27 -0
- package/package.json +192 -0
- package/src/V/annotation.mjs +0 -0
- package/src/V/index.mjs +2642 -0
- package/src/anchors/index.mjs +123 -0
- package/src/config/index.mjs +12 -0
- package/src/connectionPoints/index.mjs +202 -0
- package/src/connectionStrategies/index.mjs +73 -0
- package/src/connectors/curve.mjs +553 -0
- package/src/connectors/index.mjs +6 -0
- package/src/connectors/jumpover.mjs +452 -0
- package/src/connectors/normal.mjs +12 -0
- package/src/connectors/rounded.mjs +17 -0
- package/src/connectors/smooth.mjs +44 -0
- package/src/connectors/straight.mjs +110 -0
- package/src/dia/Cell.mjs +945 -0
- package/src/dia/CellView.mjs +1316 -0
- package/src/dia/Element.mjs +519 -0
- package/src/dia/ElementView.mjs +859 -0
- package/src/dia/Graph.mjs +1112 -0
- package/src/dia/HighlighterView.mjs +319 -0
- package/src/dia/Link.mjs +565 -0
- package/src/dia/LinkView.mjs +2207 -0
- package/src/dia/Paper.mjs +3171 -0
- package/src/dia/PaperLayer.mjs +75 -0
- package/src/dia/ToolView.mjs +69 -0
- package/src/dia/ToolsView.mjs +128 -0
- package/src/dia/attributes/calc.mjs +128 -0
- package/src/dia/attributes/connection.mjs +75 -0
- package/src/dia/attributes/defs.mjs +76 -0
- package/src/dia/attributes/eval.mjs +64 -0
- package/src/dia/attributes/index.mjs +69 -0
- package/src/dia/attributes/legacy.mjs +148 -0
- package/src/dia/attributes/offset.mjs +53 -0
- package/src/dia/attributes/props.mjs +30 -0
- package/src/dia/attributes/shape.mjs +92 -0
- package/src/dia/attributes/text.mjs +180 -0
- package/src/dia/index.mjs +13 -0
- package/src/dia/layers/GridLayer.mjs +176 -0
- package/src/dia/ports.mjs +874 -0
- package/src/elementTools/Control.mjs +153 -0
- package/src/elementTools/HoverConnect.mjs +37 -0
- package/src/elementTools/index.mjs +5 -0
- package/src/env/index.mjs +43 -0
- package/src/g/bezier.mjs +175 -0
- package/src/g/curve.mjs +956 -0
- package/src/g/ellipse.mjs +245 -0
- package/src/g/extend.mjs +64 -0
- package/src/g/geometry.helpers.mjs +58 -0
- package/src/g/index.mjs +17 -0
- package/src/g/intersection.mjs +511 -0
- package/src/g/line.bearing.mjs +30 -0
- package/src/g/line.length.mjs +5 -0
- package/src/g/line.mjs +356 -0
- package/src/g/line.squaredLength.mjs +10 -0
- package/src/g/path.mjs +2260 -0
- package/src/g/point.mjs +375 -0
- package/src/g/points.mjs +247 -0
- package/src/g/polygon.mjs +51 -0
- package/src/g/polyline.mjs +523 -0
- package/src/g/rect.mjs +556 -0
- package/src/g/types.mjs +10 -0
- package/src/highlighters/addClass.mjs +27 -0
- package/src/highlighters/index.mjs +5 -0
- package/src/highlighters/list.mjs +111 -0
- package/src/highlighters/mask.mjs +220 -0
- package/src/highlighters/opacity.mjs +17 -0
- package/src/highlighters/stroke.mjs +100 -0
- package/src/layout/index.mjs +4 -0
- package/src/layout/ports/port.mjs +188 -0
- package/src/layout/ports/portLabel.mjs +224 -0
- package/src/linkAnchors/index.mjs +76 -0
- package/src/linkTools/Anchor.mjs +235 -0
- package/src/linkTools/Arrowhead.mjs +103 -0
- package/src/linkTools/Boundary.mjs +48 -0
- package/src/linkTools/Button.mjs +121 -0
- package/src/linkTools/Connect.mjs +85 -0
- package/src/linkTools/HoverConnect.mjs +161 -0
- package/src/linkTools/Segments.mjs +393 -0
- package/src/linkTools/Vertices.mjs +253 -0
- package/src/linkTools/helpers.mjs +33 -0
- package/src/linkTools/index.mjs +8 -0
- package/src/mvc/Collection.mjs +560 -0
- package/src/mvc/Data.mjs +46 -0
- package/src/mvc/Dom/Dom.mjs +587 -0
- package/src/mvc/Dom/Event.mjs +130 -0
- package/src/mvc/Dom/animations.mjs +122 -0
- package/src/mvc/Dom/events.mjs +69 -0
- package/src/mvc/Dom/index.mjs +13 -0
- package/src/mvc/Dom/methods.mjs +392 -0
- package/src/mvc/Dom/props.mjs +77 -0
- package/src/mvc/Dom/vars.mjs +5 -0
- package/src/mvc/Events.mjs +337 -0
- package/src/mvc/Listener.mjs +33 -0
- package/src/mvc/Model.mjs +239 -0
- package/src/mvc/View.mjs +323 -0
- package/src/mvc/ViewBase.mjs +182 -0
- package/src/mvc/index.mjs +9 -0
- package/src/mvc/mvcUtils.mjs +90 -0
- package/src/polyfills/array.js +4 -0
- package/src/polyfills/base64.js +68 -0
- package/src/polyfills/index.mjs +5 -0
- package/src/polyfills/number.js +3 -0
- package/src/polyfills/string.js +3 -0
- package/src/polyfills/typedArray.js +47 -0
- package/src/routers/index.mjs +6 -0
- package/src/routers/manhattan.mjs +856 -0
- package/src/routers/metro.mjs +91 -0
- package/src/routers/normal.mjs +6 -0
- package/src/routers/oneSide.mjs +60 -0
- package/src/routers/orthogonal.mjs +323 -0
- package/src/routers/rightAngle.mjs +1056 -0
- package/src/shapes/index.mjs +3 -0
- package/src/shapes/standard.mjs +755 -0
- package/src/util/cloneCells.mjs +67 -0
- package/src/util/getRectPoint.mjs +65 -0
- package/src/util/index.mjs +5 -0
- package/src/util/svgTagTemplate.mjs +110 -0
- package/src/util/util.mjs +1754 -0
- package/src/util/utilHelpers.mjs +2402 -0
- package/src/util/wrappers.mjs +56 -0
- package/types/geometry.d.ts +815 -0
- package/types/index.d.ts +53 -0
- package/types/joint.d.ts +4391 -0
- package/types/joint.head.d.ts +12 -0
- package/types/vectorizer.d.ts +327 -0
package/src/g/rect.mjs
ADDED
|
@@ -0,0 +1,556 @@
|
|
|
1
|
+
import { toRad } from './geometry.helpers.mjs';
|
|
2
|
+
import { Line } from './line.mjs';
|
|
3
|
+
import { Point } from './point.mjs';
|
|
4
|
+
import { Ellipse } from './ellipse.mjs';
|
|
5
|
+
import { types } from './types.mjs';
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
abs,
|
|
9
|
+
cos,
|
|
10
|
+
sin,
|
|
11
|
+
min,
|
|
12
|
+
max,
|
|
13
|
+
round,
|
|
14
|
+
pow
|
|
15
|
+
} = Math;
|
|
16
|
+
|
|
17
|
+
export const Rect = function(x, y, w, h) {
|
|
18
|
+
|
|
19
|
+
if (!(this instanceof Rect)) {
|
|
20
|
+
return new Rect(x, y, w, h);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if ((Object(x) === x)) {
|
|
24
|
+
y = x.y;
|
|
25
|
+
w = x.width;
|
|
26
|
+
h = x.height;
|
|
27
|
+
x = x.x;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
this.x = x === undefined ? 0 : x;
|
|
31
|
+
this.y = y === undefined ? 0 : y;
|
|
32
|
+
this.width = w === undefined ? 0 : w;
|
|
33
|
+
this.height = h === undefined ? 0 : h;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
Rect.fromEllipse = function(e) {
|
|
37
|
+
|
|
38
|
+
e = new Ellipse(e);
|
|
39
|
+
return new Rect(e.x - e.a, e.y - e.b, 2 * e.a, 2 * e.b);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
Rect.fromPointUnion = function(...points) {
|
|
43
|
+
|
|
44
|
+
if (points.length === 0) return null;
|
|
45
|
+
|
|
46
|
+
const p = new Point();
|
|
47
|
+
let minX, minY, maxX, maxY;
|
|
48
|
+
minX = minY = Infinity;
|
|
49
|
+
maxX = maxY = -Infinity;
|
|
50
|
+
|
|
51
|
+
for (let i = 0; i < points.length; i++) {
|
|
52
|
+
p.update(points[i]);
|
|
53
|
+
const x = p.x;
|
|
54
|
+
const y = p.y;
|
|
55
|
+
|
|
56
|
+
if (x < minX) minX = x;
|
|
57
|
+
if (x > maxX) maxX = x;
|
|
58
|
+
if (y < minY) minY = y;
|
|
59
|
+
if (y > maxY) maxY = y;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return new Rect(minX, minY, maxX - minX, maxY - minY);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
Rect.fromRectUnion = function(...rects) {
|
|
66
|
+
|
|
67
|
+
if (rects.length === 0) return null;
|
|
68
|
+
|
|
69
|
+
const r = new Rect();
|
|
70
|
+
let minX, minY, maxX, maxY;
|
|
71
|
+
minX = minY = Infinity;
|
|
72
|
+
maxX = maxY = -Infinity;
|
|
73
|
+
|
|
74
|
+
for (let i = 0; i < rects.length; i++) {
|
|
75
|
+
r.update(rects[i]);
|
|
76
|
+
const x = r.x;
|
|
77
|
+
const y = r.y;
|
|
78
|
+
const mX = x + r.width;
|
|
79
|
+
const mY = y + r.height;
|
|
80
|
+
|
|
81
|
+
if (x < minX) minX = x;
|
|
82
|
+
if (mX > maxX) maxX = mX;
|
|
83
|
+
if (y < minY) minY = y;
|
|
84
|
+
if (mY > maxY) maxY = mY;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return new Rect(minX, minY, maxX - minX, maxY - minY);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
Rect.prototype = {
|
|
91
|
+
|
|
92
|
+
type: types.Rect,
|
|
93
|
+
|
|
94
|
+
// Find my bounding box when I'm rotated with the center of rotation in the center of me.
|
|
95
|
+
// @return r {rectangle} representing a bounding box
|
|
96
|
+
bbox: function(angle) {
|
|
97
|
+
return this.clone().rotateAroundCenter(angle);
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
rotateAroundCenter: function(angle) {
|
|
101
|
+
if (!angle) return this;
|
|
102
|
+
const { width, height } = this;
|
|
103
|
+
const theta = toRad(angle);
|
|
104
|
+
const st = abs(sin(theta));
|
|
105
|
+
const ct = abs(cos(theta));
|
|
106
|
+
const w = width * ct + height * st;
|
|
107
|
+
const h = width * st + height * ct;
|
|
108
|
+
this.x += (width - w) / 2;
|
|
109
|
+
this.y += (height - h) / 2;
|
|
110
|
+
this.width = w;
|
|
111
|
+
this.height = h;
|
|
112
|
+
return this;
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
bottomLeft: function() {
|
|
116
|
+
|
|
117
|
+
return new Point(this.x, this.y + this.height);
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
bottomLine: function() {
|
|
121
|
+
|
|
122
|
+
return new Line(this.bottomLeft(), this.bottomRight());
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
bottomMiddle: function() {
|
|
126
|
+
|
|
127
|
+
return new Point(this.x + this.width / 2, this.y + this.height);
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
center: function() {
|
|
131
|
+
|
|
132
|
+
return new Point(this.x + this.width / 2, this.y + this.height / 2);
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
clone: function() {
|
|
136
|
+
|
|
137
|
+
return new Rect(this);
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
// @return {bool} true if point p is inside me.
|
|
141
|
+
containsPoint: function(p) {
|
|
142
|
+
|
|
143
|
+
if (!(p instanceof Point)) {
|
|
144
|
+
p = new Point(p);
|
|
145
|
+
}
|
|
146
|
+
return p.x >= this.x && p.x <= this.x + this.width && p.y >= this.y && p.y <= this.y + this.height;
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
// @return {bool} true if rectangle `r` is inside me.
|
|
150
|
+
containsRect: function(r) {
|
|
151
|
+
|
|
152
|
+
var r0 = new Rect(this).normalize();
|
|
153
|
+
var r1 = new Rect(r).normalize();
|
|
154
|
+
var w0 = r0.width;
|
|
155
|
+
var h0 = r0.height;
|
|
156
|
+
var w1 = r1.width;
|
|
157
|
+
var h1 = r1.height;
|
|
158
|
+
|
|
159
|
+
if (!w0 || !h0 || !w1 || !h1) {
|
|
160
|
+
// At least one of the dimensions is 0
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
var x0 = r0.x;
|
|
165
|
+
var y0 = r0.y;
|
|
166
|
+
var x1 = r1.x;
|
|
167
|
+
var y1 = r1.y;
|
|
168
|
+
|
|
169
|
+
w1 += x1;
|
|
170
|
+
w0 += x0;
|
|
171
|
+
h1 += y1;
|
|
172
|
+
h0 += y0;
|
|
173
|
+
|
|
174
|
+
return x0 <= x1 && w1 <= w0 && y0 <= y1 && h1 <= h0;
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
corner: function() {
|
|
178
|
+
|
|
179
|
+
return new Point(this.x + this.width, this.y + this.height);
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
// @return {boolean} true if rectangles are equal.
|
|
183
|
+
equals: function(r) {
|
|
184
|
+
|
|
185
|
+
var mr = (new Rect(this)).normalize();
|
|
186
|
+
var nr = (new Rect(r)).normalize();
|
|
187
|
+
return mr.x === nr.x && mr.y === nr.y && mr.width === nr.width && mr.height === nr.height;
|
|
188
|
+
},
|
|
189
|
+
|
|
190
|
+
// inflate by dx and dy, recompute origin [x, y]
|
|
191
|
+
// @param dx {delta_x} representing additional size to x
|
|
192
|
+
// @param dy {delta_y} representing additional size to y -
|
|
193
|
+
// dy param is not required -> in that case y is sized by dx
|
|
194
|
+
inflate: function(dx, dy) {
|
|
195
|
+
|
|
196
|
+
if (dx === undefined) {
|
|
197
|
+
dx = 0;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (dy === undefined) {
|
|
201
|
+
dy = dx;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
this.x -= dx;
|
|
205
|
+
this.y -= dy;
|
|
206
|
+
this.width += 2 * dx;
|
|
207
|
+
this.height += 2 * dy;
|
|
208
|
+
|
|
209
|
+
return this;
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
// @return {rect} if rectangles intersect, {null} if not.
|
|
213
|
+
intersect: function(r) {
|
|
214
|
+
|
|
215
|
+
var myOrigin = this.origin();
|
|
216
|
+
var myCorner = this.corner();
|
|
217
|
+
var rOrigin = r.origin();
|
|
218
|
+
var rCorner = r.corner();
|
|
219
|
+
|
|
220
|
+
// No intersection found
|
|
221
|
+
if (rCorner.x <= myOrigin.x ||
|
|
222
|
+
rCorner.y <= myOrigin.y ||
|
|
223
|
+
rOrigin.x >= myCorner.x ||
|
|
224
|
+
rOrigin.y >= myCorner.y) return null;
|
|
225
|
+
|
|
226
|
+
var x = max(myOrigin.x, rOrigin.x);
|
|
227
|
+
var y = max(myOrigin.y, rOrigin.y);
|
|
228
|
+
|
|
229
|
+
return new Rect(x, y, min(myCorner.x, rCorner.x) - x, min(myCorner.y, rCorner.y) - y);
|
|
230
|
+
},
|
|
231
|
+
|
|
232
|
+
intersectionWithLine: function(line) {
|
|
233
|
+
|
|
234
|
+
var r = this;
|
|
235
|
+
var rectLines = [r.topLine(), r.rightLine(), r.bottomLine(), r.leftLine()];
|
|
236
|
+
var points = [];
|
|
237
|
+
var dedupeArr = [];
|
|
238
|
+
var pt, i;
|
|
239
|
+
|
|
240
|
+
var n = rectLines.length;
|
|
241
|
+
for (i = 0; i < n; i++) {
|
|
242
|
+
|
|
243
|
+
pt = line.intersect(rectLines[i]);
|
|
244
|
+
if (pt !== null && dedupeArr.indexOf(pt.toString()) < 0) {
|
|
245
|
+
points.push(pt);
|
|
246
|
+
dedupeArr.push(pt.toString());
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return points.length > 0 ? points : null;
|
|
251
|
+
},
|
|
252
|
+
|
|
253
|
+
// Find point on my boundary where line starting
|
|
254
|
+
// from my center ending in point p intersects me.
|
|
255
|
+
// @param {number} angle If angle is specified, intersection with rotated rectangle is computed.
|
|
256
|
+
intersectionWithLineFromCenterToPoint: function(p, angle) {
|
|
257
|
+
|
|
258
|
+
p = new Point(p);
|
|
259
|
+
var center = new Point(this.x + this.width / 2, this.y + this.height / 2);
|
|
260
|
+
var result;
|
|
261
|
+
|
|
262
|
+
if (angle) p.rotate(center, angle);
|
|
263
|
+
|
|
264
|
+
// (clockwise, starting from the top side)
|
|
265
|
+
var sides = [
|
|
266
|
+
this.topLine(),
|
|
267
|
+
this.rightLine(),
|
|
268
|
+
this.bottomLine(),
|
|
269
|
+
this.leftLine()
|
|
270
|
+
];
|
|
271
|
+
var connector = new Line(center, p);
|
|
272
|
+
|
|
273
|
+
for (var i = sides.length - 1; i >= 0; --i) {
|
|
274
|
+
var intersection = sides[i].intersection(connector);
|
|
275
|
+
if (intersection !== null) {
|
|
276
|
+
result = intersection;
|
|
277
|
+
break;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
if (result && angle) result.rotate(center, -angle);
|
|
281
|
+
return result;
|
|
282
|
+
},
|
|
283
|
+
|
|
284
|
+
leftLine: function() {
|
|
285
|
+
|
|
286
|
+
return new Line(this.topLeft(), this.bottomLeft());
|
|
287
|
+
},
|
|
288
|
+
|
|
289
|
+
leftMiddle: function() {
|
|
290
|
+
|
|
291
|
+
return new Point(this.x, this.y + this.height / 2);
|
|
292
|
+
},
|
|
293
|
+
|
|
294
|
+
maxRectScaleToFit: function(rect, origin) {
|
|
295
|
+
|
|
296
|
+
rect = new Rect(rect);
|
|
297
|
+
origin || (origin = rect.center());
|
|
298
|
+
|
|
299
|
+
var sx1, sx2, sx3, sx4, sy1, sy2, sy3, sy4;
|
|
300
|
+
var ox = origin.x;
|
|
301
|
+
var oy = origin.y;
|
|
302
|
+
|
|
303
|
+
// Here we find the maximal possible scale for all corner points (for x and y axis) of the rectangle,
|
|
304
|
+
// so when the scale is applied the point is still inside the rectangle.
|
|
305
|
+
|
|
306
|
+
sx1 = sx2 = sx3 = sx4 = sy1 = sy2 = sy3 = sy4 = Infinity;
|
|
307
|
+
|
|
308
|
+
// Top Left
|
|
309
|
+
var p1 = rect.topLeft();
|
|
310
|
+
if (p1.x < ox) {
|
|
311
|
+
sx1 = (this.x - ox) / (p1.x - ox);
|
|
312
|
+
}
|
|
313
|
+
if (p1.y < oy) {
|
|
314
|
+
sy1 = (this.y - oy) / (p1.y - oy);
|
|
315
|
+
}
|
|
316
|
+
// Bottom Right
|
|
317
|
+
var p2 = rect.bottomRight();
|
|
318
|
+
if (p2.x > ox) {
|
|
319
|
+
sx2 = (this.x + this.width - ox) / (p2.x - ox);
|
|
320
|
+
}
|
|
321
|
+
if (p2.y > oy) {
|
|
322
|
+
sy2 = (this.y + this.height - oy) / (p2.y - oy);
|
|
323
|
+
}
|
|
324
|
+
// Top Right
|
|
325
|
+
var p3 = rect.topRight();
|
|
326
|
+
if (p3.x > ox) {
|
|
327
|
+
sx3 = (this.x + this.width - ox) / (p3.x - ox);
|
|
328
|
+
}
|
|
329
|
+
if (p3.y < oy) {
|
|
330
|
+
sy3 = (this.y - oy) / (p3.y - oy);
|
|
331
|
+
}
|
|
332
|
+
// Bottom Left
|
|
333
|
+
var p4 = rect.bottomLeft();
|
|
334
|
+
if (p4.x < ox) {
|
|
335
|
+
sx4 = (this.x - ox) / (p4.x - ox);
|
|
336
|
+
}
|
|
337
|
+
if (p4.y > oy) {
|
|
338
|
+
sy4 = (this.y + this.height - oy) / (p4.y - oy);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return {
|
|
342
|
+
sx: min(sx1, sx2, sx3, sx4),
|
|
343
|
+
sy: min(sy1, sy2, sy3, sy4)
|
|
344
|
+
};
|
|
345
|
+
},
|
|
346
|
+
|
|
347
|
+
maxRectUniformScaleToFit: function(rect, origin) {
|
|
348
|
+
|
|
349
|
+
var scale = this.maxRectScaleToFit(rect, origin);
|
|
350
|
+
return min(scale.sx, scale.sy);
|
|
351
|
+
},
|
|
352
|
+
|
|
353
|
+
// Move and expand me.
|
|
354
|
+
// @param r {rectangle} representing deltas
|
|
355
|
+
moveAndExpand: function(r) {
|
|
356
|
+
|
|
357
|
+
this.x += r.x || 0;
|
|
358
|
+
this.y += r.y || 0;
|
|
359
|
+
this.width += r.width || 0;
|
|
360
|
+
this.height += r.height || 0;
|
|
361
|
+
return this;
|
|
362
|
+
},
|
|
363
|
+
|
|
364
|
+
// Normalize the rectangle; i.e., make it so that it has a non-negative width and height.
|
|
365
|
+
// If width < 0 the function swaps the left and right corners,
|
|
366
|
+
// and it swaps the top and bottom corners if height < 0
|
|
367
|
+
// like in http://qt-project.org/doc/qt-4.8/qrectf.html#normalized
|
|
368
|
+
normalize: function() {
|
|
369
|
+
|
|
370
|
+
var newx = this.x;
|
|
371
|
+
var newy = this.y;
|
|
372
|
+
var newwidth = this.width;
|
|
373
|
+
var newheight = this.height;
|
|
374
|
+
if (this.width < 0) {
|
|
375
|
+
newx = this.x + this.width;
|
|
376
|
+
newwidth = -this.width;
|
|
377
|
+
}
|
|
378
|
+
if (this.height < 0) {
|
|
379
|
+
newy = this.y + this.height;
|
|
380
|
+
newheight = -this.height;
|
|
381
|
+
}
|
|
382
|
+
this.x = newx;
|
|
383
|
+
this.y = newy;
|
|
384
|
+
this.width = newwidth;
|
|
385
|
+
this.height = newheight;
|
|
386
|
+
return this;
|
|
387
|
+
},
|
|
388
|
+
|
|
389
|
+
// Offset me by the specified amount.
|
|
390
|
+
offset: function(dx, dy) {
|
|
391
|
+
|
|
392
|
+
// pretend that this is a point and call offset()
|
|
393
|
+
// rewrites x and y according to dx and dy
|
|
394
|
+
return Point.prototype.offset.call(this, dx, dy);
|
|
395
|
+
},
|
|
396
|
+
|
|
397
|
+
origin: function() {
|
|
398
|
+
|
|
399
|
+
return new Point(this.x, this.y);
|
|
400
|
+
},
|
|
401
|
+
|
|
402
|
+
// @return {point} a point on my boundary nearest to the given point.
|
|
403
|
+
// @see Squeak Smalltalk, Rectangle>>pointNearestTo:
|
|
404
|
+
pointNearestToPoint: function(point) {
|
|
405
|
+
|
|
406
|
+
point = new Point(point);
|
|
407
|
+
if (this.containsPoint(point)) {
|
|
408
|
+
var side = this.sideNearestToPoint(point);
|
|
409
|
+
switch (side) {
|
|
410
|
+
case 'right':
|
|
411
|
+
return new Point(this.x + this.width, point.y);
|
|
412
|
+
case 'left':
|
|
413
|
+
return new Point(this.x, point.y);
|
|
414
|
+
case 'bottom':
|
|
415
|
+
return new Point(point.x, this.y + this.height);
|
|
416
|
+
case 'top':
|
|
417
|
+
return new Point(point.x, this.y);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
return point.adhereToRect(this);
|
|
421
|
+
},
|
|
422
|
+
|
|
423
|
+
rightLine: function() {
|
|
424
|
+
|
|
425
|
+
return new Line(this.topRight(), this.bottomRight());
|
|
426
|
+
},
|
|
427
|
+
|
|
428
|
+
rightMiddle: function() {
|
|
429
|
+
|
|
430
|
+
return new Point(this.x + this.width, this.y + this.height / 2);
|
|
431
|
+
},
|
|
432
|
+
|
|
433
|
+
round: function(precision) {
|
|
434
|
+
|
|
435
|
+
let f = 1; // case 0
|
|
436
|
+
if (precision) {
|
|
437
|
+
switch (precision) {
|
|
438
|
+
case 1: f = 10; break;
|
|
439
|
+
case 2: f = 100; break;
|
|
440
|
+
case 3: f = 1000; break;
|
|
441
|
+
default: f = pow(10, precision); break;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
this.x = round(this.x * f) / f;
|
|
446
|
+
this.y = round(this.y * f) / f;
|
|
447
|
+
this.width = round(this.width * f) / f;
|
|
448
|
+
this.height = round(this.height * f) / f;
|
|
449
|
+
return this;
|
|
450
|
+
},
|
|
451
|
+
|
|
452
|
+
// Scale rectangle with origin.
|
|
453
|
+
scale: function(sx, sy, origin) {
|
|
454
|
+
|
|
455
|
+
origin = this.origin().scale(sx, sy, origin);
|
|
456
|
+
this.x = origin.x;
|
|
457
|
+
this.y = origin.y;
|
|
458
|
+
this.width *= sx;
|
|
459
|
+
this.height *= sy;
|
|
460
|
+
return this;
|
|
461
|
+
},
|
|
462
|
+
|
|
463
|
+
// @return {string} (left|right|top|bottom) side which is nearest to point
|
|
464
|
+
// @see Squeak Smalltalk, Rectangle>>sideNearestTo:
|
|
465
|
+
sideNearestToPoint: function(point) {
|
|
466
|
+
|
|
467
|
+
point = new Point(point);
|
|
468
|
+
var distToLeft = point.x - this.x;
|
|
469
|
+
var distToRight = (this.x + this.width) - point.x;
|
|
470
|
+
var distToTop = point.y - this.y;
|
|
471
|
+
var distToBottom = (this.y + this.height) - point.y;
|
|
472
|
+
var closest = distToLeft;
|
|
473
|
+
var side = 'left';
|
|
474
|
+
|
|
475
|
+
if (distToRight < closest) {
|
|
476
|
+
closest = distToRight;
|
|
477
|
+
side = 'right';
|
|
478
|
+
}
|
|
479
|
+
if (distToTop < closest) {
|
|
480
|
+
closest = distToTop;
|
|
481
|
+
side = 'top';
|
|
482
|
+
}
|
|
483
|
+
if (distToBottom < closest) {
|
|
484
|
+
// closest = distToBottom;
|
|
485
|
+
side = 'bottom';
|
|
486
|
+
}
|
|
487
|
+
return side;
|
|
488
|
+
},
|
|
489
|
+
|
|
490
|
+
snapToGrid: function(gx, gy) {
|
|
491
|
+
|
|
492
|
+
var origin = this.origin().snapToGrid(gx, gy);
|
|
493
|
+
var corner = this.corner().snapToGrid(gx, gy);
|
|
494
|
+
this.x = origin.x;
|
|
495
|
+
this.y = origin.y;
|
|
496
|
+
this.width = corner.x - origin.x;
|
|
497
|
+
this.height = corner.y - origin.y;
|
|
498
|
+
return this;
|
|
499
|
+
},
|
|
500
|
+
|
|
501
|
+
toJSON: function() {
|
|
502
|
+
|
|
503
|
+
return { x: this.x, y: this.y, width: this.width, height: this.height };
|
|
504
|
+
},
|
|
505
|
+
|
|
506
|
+
topLine: function() {
|
|
507
|
+
|
|
508
|
+
return new Line(this.topLeft(), this.topRight());
|
|
509
|
+
},
|
|
510
|
+
|
|
511
|
+
topMiddle: function() {
|
|
512
|
+
|
|
513
|
+
return new Point(this.x + this.width / 2, this.y);
|
|
514
|
+
},
|
|
515
|
+
|
|
516
|
+
topRight: function() {
|
|
517
|
+
|
|
518
|
+
return new Point(this.x + this.width, this.y);
|
|
519
|
+
},
|
|
520
|
+
|
|
521
|
+
toString: function() {
|
|
522
|
+
|
|
523
|
+
return this.origin().toString() + ' ' + this.corner().toString();
|
|
524
|
+
},
|
|
525
|
+
|
|
526
|
+
// @return {rect} representing the union of both rectangles.
|
|
527
|
+
union: function(rect) {
|
|
528
|
+
|
|
529
|
+
return Rect.fromRectUnion(this, rect);
|
|
530
|
+
},
|
|
531
|
+
|
|
532
|
+
update: function(x, y, w, h) {
|
|
533
|
+
|
|
534
|
+
if ((Object(x) === x)) {
|
|
535
|
+
y = x.y;
|
|
536
|
+
w = x.width;
|
|
537
|
+
h = x.height;
|
|
538
|
+
x = x.x;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
this.x = x || 0;
|
|
542
|
+
this.y = y || 0;
|
|
543
|
+
this.width = w || 0;
|
|
544
|
+
this.height = h || 0;
|
|
545
|
+
return this;
|
|
546
|
+
}
|
|
547
|
+
};
|
|
548
|
+
|
|
549
|
+
Rect.prototype.bottomRight = Rect.prototype.corner;
|
|
550
|
+
|
|
551
|
+
Rect.prototype.topLeft = Rect.prototype.origin;
|
|
552
|
+
|
|
553
|
+
Rect.prototype.translate = Rect.prototype.offset;
|
|
554
|
+
|
|
555
|
+
// For backwards compatibility:
|
|
556
|
+
export const rect = Rect;
|
package/src/g/types.mjs
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as util from '../util/index.mjs';
|
|
2
|
+
import V from '../V/index.mjs';
|
|
3
|
+
import { HighlighterView } from '../dia/HighlighterView.mjs';
|
|
4
|
+
|
|
5
|
+
const className = util.addClassNamePrefix('highlighted');
|
|
6
|
+
|
|
7
|
+
export const addClass = HighlighterView.extend({
|
|
8
|
+
|
|
9
|
+
UPDATABLE: false,
|
|
10
|
+
MOUNTABLE: false,
|
|
11
|
+
|
|
12
|
+
options: {
|
|
13
|
+
className
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
highlight: function(_cellView, node) {
|
|
17
|
+
V(node).addClass(this.options.className);
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
unhighlight: function(_cellView, node) {
|
|
21
|
+
V(node).removeClass(this.options.className);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
}, {
|
|
25
|
+
// Backwards Compatibility
|
|
26
|
+
className
|
|
27
|
+
});
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { Rect } from '../g/index.mjs';
|
|
2
|
+
import { HighlighterView } from '../dia/HighlighterView.mjs';
|
|
3
|
+
import {
|
|
4
|
+
normalizeSides,
|
|
5
|
+
isEqual,
|
|
6
|
+
} from '../util/index.mjs';
|
|
7
|
+
import {
|
|
8
|
+
Positions,
|
|
9
|
+
getRectPoint,
|
|
10
|
+
} from '../util/getRectPoint.mjs';
|
|
11
|
+
|
|
12
|
+
const Directions = {
|
|
13
|
+
ROW: 'row',
|
|
14
|
+
COLUMN: 'column'
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const list = HighlighterView.extend({
|
|
18
|
+
|
|
19
|
+
tagName: 'g',
|
|
20
|
+
MOUNTABLE: true,
|
|
21
|
+
UPDATE_ATTRIBUTES: function() {
|
|
22
|
+
return [this.options.attribute];
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
_prevItems: null,
|
|
26
|
+
|
|
27
|
+
highlight(elementView, node) {
|
|
28
|
+
const element = elementView.model;
|
|
29
|
+
const { attribute, size = 20, gap = 5, direction = Directions.ROW } = this.options;
|
|
30
|
+
if (!attribute) throw new Error('List: attribute is required');
|
|
31
|
+
const normalizedSize = (typeof size === 'number') ? { width: size, height: size } : size;
|
|
32
|
+
const isRowDirection = (direction === Directions.ROW);
|
|
33
|
+
const itemWidth = isRowDirection ? normalizedSize.width : normalizedSize.height;
|
|
34
|
+
let items = element.get(attribute);
|
|
35
|
+
if (!Array.isArray(items)) items = [];
|
|
36
|
+
const prevItems = this._prevItems || [];
|
|
37
|
+
const comparison = items.map((item, index) => isEqual(prevItems[index], items[index]));
|
|
38
|
+
if (prevItems.length !== items.length || comparison.some(unchanged => !unchanged)) {
|
|
39
|
+
const prevEls = this.vel.children();
|
|
40
|
+
const itemsEls = items.map((item, index) => {
|
|
41
|
+
const prevEl = (index in prevEls) ? prevEls[index].node : null;
|
|
42
|
+
if (comparison[index]) return prevEl;
|
|
43
|
+
const itemEl = this.createListItem(item, normalizedSize, prevEl);
|
|
44
|
+
if (!itemEl) return null;
|
|
45
|
+
if (!(itemEl instanceof SVGElement)) throw new Error('List: item must be an SVGElement');
|
|
46
|
+
itemEl.dataset.index = index;
|
|
47
|
+
itemEl.dataset.attribute = attribute;
|
|
48
|
+
const offset = index * (itemWidth + gap);
|
|
49
|
+
itemEl.setAttribute('transform', (isRowDirection)
|
|
50
|
+
? `translate(${offset}, 0)`
|
|
51
|
+
: `translate(0, ${offset})`
|
|
52
|
+
);
|
|
53
|
+
return itemEl;
|
|
54
|
+
});
|
|
55
|
+
this.vel.empty().append(itemsEls);
|
|
56
|
+
this._prevItems = items;
|
|
57
|
+
}
|
|
58
|
+
const itemsCount = items.length;
|
|
59
|
+
const length = (itemsCount === 0)
|
|
60
|
+
? 0
|
|
61
|
+
: (itemsCount * itemWidth + (itemsCount - 1) * gap);
|
|
62
|
+
const listSize = (isRowDirection)
|
|
63
|
+
? { width: length, height: normalizedSize.height }
|
|
64
|
+
: { width: normalizedSize.width, height: length };
|
|
65
|
+
|
|
66
|
+
this.position(element, listSize);
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
position(element, listSize) {
|
|
70
|
+
const { vel, options } = this;
|
|
71
|
+
const { margin = 5, position = 'top-left' } = options;
|
|
72
|
+
const { width, height } = element.size();
|
|
73
|
+
const { left, right, top, bottom } = normalizeSides(margin);
|
|
74
|
+
const bbox = new Rect(left, top, width - (left + right), height - (top + bottom));
|
|
75
|
+
let { x, y } = getRectPoint(bbox, position);
|
|
76
|
+
// x
|
|
77
|
+
switch (position) {
|
|
78
|
+
case Positions.CENTER:
|
|
79
|
+
case Positions.TOP:
|
|
80
|
+
case Positions.BOTTOM: {
|
|
81
|
+
x -= listSize.width / 2;
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
case Positions.RIGHT:
|
|
85
|
+
case Positions.BOTTOM_RIGHT:
|
|
86
|
+
case Positions.TOP_RIGHT: {
|
|
87
|
+
x -= listSize.width;
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// y
|
|
92
|
+
switch (position) {
|
|
93
|
+
case Positions.CENTER:
|
|
94
|
+
case Positions.RIGHT:
|
|
95
|
+
case Positions.LEFT: {
|
|
96
|
+
y -= listSize.height / 2;
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
case Positions.BOTTOM:
|
|
100
|
+
case Positions.BOTTOM_RIGHT:
|
|
101
|
+
case Positions.BOTTOM_LEFT: {
|
|
102
|
+
y -= listSize.height;
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
vel.attr('transform', `translate(${x}, ${y})`);
|
|
107
|
+
}
|
|
108
|
+
}, {
|
|
109
|
+
Directions,
|
|
110
|
+
Positions
|
|
111
|
+
});
|