@phin-tech/dottie 0.6.2 → 0.6.3
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/PARITY.md +2 -0
- package/dist/compile.js +12 -5
- package/dist/dottie-compile.mjs +13 -6
- package/dist/dottie-player.js +270 -42
- package/dist/dottie-player.min.js +52 -52
- package/dist/dottie.js +270 -42
- package/dist/types/connector.d.ts +6 -6
- package/dist/types/element.d.ts +3 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/model.d.ts +6 -0
- package/dist/types/shapes.d.ts +17 -4
- package/package.json +1 -1
package/PARITY.md
CHANGED
|
@@ -24,7 +24,9 @@ fixture, so the gap is visible rather than silent.
|
|
|
24
24
|
| Ports and edge-following, rotated shapes, free ends | Supported | [edge/connectors](../fixtures/edge/connectors.dottie) | |
|
|
25
25
|
| Connectors and labels on outlines (ADR 0001) | Supported | [edge/extensions](../fixtures/edge/extensions.dottie) | Edge-following meets a `.path`'s actual outline, not its box (ray against 16-step flattened curves). Endpoints pinned with `portIndex` attach to the element's `ports`. A path's label goes in its `labelRect`. |
|
|
26
26
|
| Extension shapes as stored (ADR 0001) | Supported | [edge/extensions](../fixtures/edge/extensions.dottie) | Drawn from the stored path with no extension installed. The `extension` field is kept as metadata and nothing runs. Parameters, commands and handles are Dottie-only. |
|
|
27
|
+
| Waypoints (bends) on every routing, and hops | Supported | [edge/waypoints](../fixtures/edge/waypoints.dottie), [edge/waypoints-symbol](../fixtures/edge/waypoints-symbol.dottie) | Hops need the page, so `connectorParts` takes it; without one a line draws none. |
|
|
27
28
|
| Arrowheads: arrow, triangle, dot, bar | Supported | [edge/connectors](../fixtures/edge/connectors.dottie) | |
|
|
29
|
+
| Label colors (`textColor`, apart from the stroke) | Supported | [edge/text-colors](../fixtures/edge/text-colors.dottie) | Text boxes and notes ignore it, as in Dottie |
|
|
28
30
|
| Connector labels | Supported | [edge/connectors](../fixtures/edge/connectors.dottie) | Label boxes sized with CoreText's measurements. The player paints them on the page color, like the app; Dottie's flipbook export leaves them unpainted |
|
|
29
31
|
| Keys and eases (linear, in, out, in-out, hold, spring, bounce, elastic) | Supported | [edge/timeline](../fixtures/edge/timeline.dottie), [edge/eases](../fixtures/edge/eases.dottie) | Unknown ease names play linear, with a warning |
|
|
30
32
|
| Visibility, reveal (draw-on, typing), opacity | Supported | [edge/timeline](../fixtures/edge/timeline.dottie) | |
|
package/dist/compile.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! dottie.js 0.6.
|
|
1
|
+
/*! dottie.js 0.6.3 | Apache-2.0 | https://github.com/phin-tech/dottie */
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
4
4
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
@@ -54,7 +54,7 @@ var KNOWN_FIELDS = {
|
|
|
54
54
|
symbol: ["id", "name", "width", "height", "layers", "elements", "timeline", "button", "params"],
|
|
55
55
|
layer: ["id", "name", "visible", "locked"],
|
|
56
56
|
rgba: ["r", "g", "b", "a"],
|
|
57
|
-
style: ["stroke", "fill", "fillStyle", "strokeWidth", "dash", "roughness", "bowing", "fontSize", "font", "gridSize", "opacity", "dashOffset"],
|
|
57
|
+
style: ["stroke", "fill", "fillStyle", "strokeWidth", "dash", "roughness", "bowing", "fontSize", "font", "gridSize", "opacity", "dashOffset", "textColor"],
|
|
58
58
|
element: [
|
|
59
59
|
"id",
|
|
60
60
|
"kind",
|
|
@@ -95,7 +95,8 @@ var KNOWN_FIELDS = {
|
|
|
95
95
|
extensionRef: ["id", "shape", "version", "params"],
|
|
96
96
|
normalizedPoint: ["x", "y"],
|
|
97
97
|
normalizedRect: ["x", "y", "width", "height"],
|
|
98
|
-
connector: ["start", "end", "routing", "startHead", "endHead"],
|
|
98
|
+
connector: ["start", "end", "routing", "startHead", "endHead", "waypoints", "hops"],
|
|
99
|
+
waypoint: ["x", "y"],
|
|
99
100
|
timeline: ["fps", "duration", "boil", "settle", "poster", "tracks", "camera", "markers"],
|
|
100
101
|
track: ["element", "keys", "boil", "follow", "repeats", "march"],
|
|
101
102
|
keyframe: ["t", "x", "y", "width", "height", "rotation", "strokeWidth", "stroke", "fill", "visible", "reveal", "opacity", "ease"],
|
|
@@ -172,7 +173,8 @@ var Reader = class {
|
|
|
172
173
|
font: str(s.font) || null,
|
|
173
174
|
gridSize: num(s.gridSize) ?? null,
|
|
174
175
|
opacity: num(s.opacity) ?? null,
|
|
175
|
-
dashOffset: num(s.dashOffset) ?? null
|
|
176
|
+
dashOffset: num(s.dashOffset) ?? null,
|
|
177
|
+
textColor: rgba(s.textColor) ?? null
|
|
176
178
|
};
|
|
177
179
|
}
|
|
178
180
|
endpoint(v) {
|
|
@@ -200,7 +202,12 @@ var Reader = class {
|
|
|
200
202
|
end: this.endpoint(v.end),
|
|
201
203
|
routing: routing && ["straight", "curved", "elbow"].includes(routing) ? routing : "straight",
|
|
202
204
|
startHead: head(v.startHead, "none"),
|
|
203
|
-
endHead: head(v.endHead, "arrow")
|
|
205
|
+
endHead: head(v.endHead, "arrow"),
|
|
206
|
+
waypoints: Array.isArray(v.waypoints) ? v.waypoints.filter(isObj).map((w) => {
|
|
207
|
+
this.fields(w, "waypoint");
|
|
208
|
+
return { x: num(w.x) ?? 0, y: num(w.y) ?? 0 };
|
|
209
|
+
}) : null,
|
|
210
|
+
hops: typeof v.hops === "boolean" ? v.hops : null
|
|
204
211
|
};
|
|
205
212
|
}
|
|
206
213
|
image(v, table) {
|
package/dist/dottie-compile.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
/*! dottie.js 0.6.
|
|
2
|
+
/*! dottie.js 0.6.3 | Apache-2.0 | https://github.com/phin-tech/dottie */
|
|
3
3
|
var __defProp = Object.defineProperty;
|
|
4
4
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
5
5
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
@@ -62,7 +62,7 @@ var KNOWN_FIELDS = {
|
|
|
62
62
|
symbol: ["id", "name", "width", "height", "layers", "elements", "timeline", "button", "params"],
|
|
63
63
|
layer: ["id", "name", "visible", "locked"],
|
|
64
64
|
rgba: ["r", "g", "b", "a"],
|
|
65
|
-
style: ["stroke", "fill", "fillStyle", "strokeWidth", "dash", "roughness", "bowing", "fontSize", "font", "gridSize", "opacity", "dashOffset"],
|
|
65
|
+
style: ["stroke", "fill", "fillStyle", "strokeWidth", "dash", "roughness", "bowing", "fontSize", "font", "gridSize", "opacity", "dashOffset", "textColor"],
|
|
66
66
|
element: [
|
|
67
67
|
"id",
|
|
68
68
|
"kind",
|
|
@@ -103,7 +103,8 @@ var KNOWN_FIELDS = {
|
|
|
103
103
|
extensionRef: ["id", "shape", "version", "params"],
|
|
104
104
|
normalizedPoint: ["x", "y"],
|
|
105
105
|
normalizedRect: ["x", "y", "width", "height"],
|
|
106
|
-
connector: ["start", "end", "routing", "startHead", "endHead"],
|
|
106
|
+
connector: ["start", "end", "routing", "startHead", "endHead", "waypoints", "hops"],
|
|
107
|
+
waypoint: ["x", "y"],
|
|
107
108
|
timeline: ["fps", "duration", "boil", "settle", "poster", "tracks", "camera", "markers"],
|
|
108
109
|
track: ["element", "keys", "boil", "follow", "repeats", "march"],
|
|
109
110
|
keyframe: ["t", "x", "y", "width", "height", "rotation", "strokeWidth", "stroke", "fill", "visible", "reveal", "opacity", "ease"],
|
|
@@ -180,7 +181,8 @@ var Reader = class {
|
|
|
180
181
|
font: str(s.font) || null,
|
|
181
182
|
gridSize: num(s.gridSize) ?? null,
|
|
182
183
|
opacity: num(s.opacity) ?? null,
|
|
183
|
-
dashOffset: num(s.dashOffset) ?? null
|
|
184
|
+
dashOffset: num(s.dashOffset) ?? null,
|
|
185
|
+
textColor: rgba(s.textColor) ?? null
|
|
184
186
|
};
|
|
185
187
|
}
|
|
186
188
|
endpoint(v) {
|
|
@@ -208,7 +210,12 @@ var Reader = class {
|
|
|
208
210
|
end: this.endpoint(v.end),
|
|
209
211
|
routing: routing && ["straight", "curved", "elbow"].includes(routing) ? routing : "straight",
|
|
210
212
|
startHead: head(v.startHead, "none"),
|
|
211
|
-
endHead: head(v.endHead, "arrow")
|
|
213
|
+
endHead: head(v.endHead, "arrow"),
|
|
214
|
+
waypoints: Array.isArray(v.waypoints) ? v.waypoints.filter(isObj).map((w) => {
|
|
215
|
+
this.fields(w, "waypoint");
|
|
216
|
+
return { x: num(w.x) ?? 0, y: num(w.y) ?? 0 };
|
|
217
|
+
}) : null,
|
|
218
|
+
hops: typeof v.hops === "boolean" ? v.hops : null
|
|
212
219
|
};
|
|
213
220
|
}
|
|
214
221
|
image(v, table) {
|
|
@@ -581,7 +588,7 @@ function compile(source2, runtime, options2 = {}) {
|
|
|
581
588
|
}
|
|
582
589
|
|
|
583
590
|
// src/index.ts
|
|
584
|
-
var VERSION = "0.6.
|
|
591
|
+
var VERSION = "0.6.3";
|
|
585
592
|
|
|
586
593
|
// src/cli.ts
|
|
587
594
|
var usage = `dottie-compile ${VERSION}: a Dottie drawing as one script that plays it on any page.
|
package/dist/dottie-player.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! dottie.js 0.6.
|
|
1
|
+
/*! dottie.js 0.6.3 | Apache-2.0 | https://github.com/phin-tech/dottie */
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __defNormalProp = (obj, key, value2) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value: value2 }) : obj[key] = value2;
|
|
4
4
|
var __export = (target, all) => {
|
|
@@ -76,7 +76,7 @@ var KNOWN_FIELDS = {
|
|
|
76
76
|
symbol: ["id", "name", "width", "height", "layers", "elements", "timeline", "button", "params"],
|
|
77
77
|
layer: ["id", "name", "visible", "locked"],
|
|
78
78
|
rgba: ["r", "g", "b", "a"],
|
|
79
|
-
style: ["stroke", "fill", "fillStyle", "strokeWidth", "dash", "roughness", "bowing", "fontSize", "font", "gridSize", "opacity", "dashOffset"],
|
|
79
|
+
style: ["stroke", "fill", "fillStyle", "strokeWidth", "dash", "roughness", "bowing", "fontSize", "font", "gridSize", "opacity", "dashOffset", "textColor"],
|
|
80
80
|
element: [
|
|
81
81
|
"id",
|
|
82
82
|
"kind",
|
|
@@ -117,7 +117,8 @@ var KNOWN_FIELDS = {
|
|
|
117
117
|
extensionRef: ["id", "shape", "version", "params"],
|
|
118
118
|
normalizedPoint: ["x", "y"],
|
|
119
119
|
normalizedRect: ["x", "y", "width", "height"],
|
|
120
|
-
connector: ["start", "end", "routing", "startHead", "endHead"],
|
|
120
|
+
connector: ["start", "end", "routing", "startHead", "endHead", "waypoints", "hops"],
|
|
121
|
+
waypoint: ["x", "y"],
|
|
121
122
|
timeline: ["fps", "duration", "boil", "settle", "poster", "tracks", "camera", "markers"],
|
|
122
123
|
track: ["element", "keys", "boil", "follow", "repeats", "march"],
|
|
123
124
|
keyframe: ["t", "x", "y", "width", "height", "rotation", "strokeWidth", "stroke", "fill", "visible", "reveal", "opacity", "ease"],
|
|
@@ -199,7 +200,8 @@ var Reader = class {
|
|
|
199
200
|
font: str(s.font) || null,
|
|
200
201
|
gridSize: num(s.gridSize) ?? null,
|
|
201
202
|
opacity: num(s.opacity) ?? null,
|
|
202
|
-
dashOffset: num(s.dashOffset) ?? null
|
|
203
|
+
dashOffset: num(s.dashOffset) ?? null,
|
|
204
|
+
textColor: rgba(s.textColor) ?? null
|
|
203
205
|
};
|
|
204
206
|
}
|
|
205
207
|
endpoint(v) {
|
|
@@ -227,7 +229,12 @@ var Reader = class {
|
|
|
227
229
|
end: this.endpoint(v.end),
|
|
228
230
|
routing: routing && ["straight", "curved", "elbow"].includes(routing) ? routing : "straight",
|
|
229
231
|
startHead: head(v.startHead, "none"),
|
|
230
|
-
endHead: head(v.endHead, "arrow")
|
|
232
|
+
endHead: head(v.endHead, "arrow"),
|
|
233
|
+
waypoints: Array.isArray(v.waypoints) ? v.waypoints.filter(isObj).map((w) => {
|
|
234
|
+
this.fields(w, "waypoint");
|
|
235
|
+
return { x: num(w.x) ?? 0, y: num(w.y) ?? 0 };
|
|
236
|
+
}) : null,
|
|
237
|
+
hops: typeof v.hops === "boolean" ? v.hops : null
|
|
231
238
|
};
|
|
232
239
|
}
|
|
233
240
|
image(v, table) {
|
|
@@ -504,8 +511,18 @@ function isAutomaticInk(c) {
|
|
|
504
511
|
function cloneElement(e) {
|
|
505
512
|
return {
|
|
506
513
|
...e,
|
|
507
|
-
style: {
|
|
508
|
-
|
|
514
|
+
style: {
|
|
515
|
+
...e.style,
|
|
516
|
+
stroke: { ...e.style.stroke },
|
|
517
|
+
fill: { ...e.style.fill },
|
|
518
|
+
textColor: e.style.textColor ? { ...e.style.textColor } : null
|
|
519
|
+
},
|
|
520
|
+
connector: e.connector ? {
|
|
521
|
+
...e.connector,
|
|
522
|
+
start: { ...e.connector.start },
|
|
523
|
+
end: { ...e.connector.end },
|
|
524
|
+
waypoints: e.connector.waypoints?.map((w) => ({ ...w })) ?? null
|
|
525
|
+
} : null
|
|
509
526
|
};
|
|
510
527
|
}
|
|
511
528
|
function orderedElements(page) {
|
|
@@ -935,13 +952,19 @@ function rotatedBounds(e) {
|
|
|
935
952
|
return { x, y, width: Math.max(...xs) - x, height: Math.max(...ys) - y };
|
|
936
953
|
}
|
|
937
954
|
function resolvingInk(e, ink) {
|
|
938
|
-
|
|
955
|
+
const text = e.style.textColor !== null && isAutomaticInk(e.style.textColor);
|
|
956
|
+
if (!isAutomaticInk(e.style.stroke) && !text) return e;
|
|
939
957
|
const copy = cloneElement(e);
|
|
940
958
|
const covers = e.style.fillStyle === "solid" || e.style.fillStyle === "grid";
|
|
941
959
|
const onLightFill = covers && e.style.fill.a >= 0.5 && luminance(e.style.fill) > 0.5;
|
|
942
|
-
|
|
960
|
+
const resolved2 = luminance(ink) > 0.5 && onLightFill ? black : ink;
|
|
961
|
+
if (isAutomaticInk(e.style.stroke)) copy.style.stroke = { ...resolved2 };
|
|
962
|
+
if (text) copy.style.textColor = { ...resolved2 };
|
|
943
963
|
return copy;
|
|
944
964
|
}
|
|
965
|
+
function labelColor(e) {
|
|
966
|
+
return e.kind === "text" || e.kind === "note" ? e.style.stroke : e.style.textColor ?? e.style.stroke;
|
|
967
|
+
}
|
|
945
968
|
function shapeContours(e) {
|
|
946
969
|
if (e.kind !== "path" || !e.subpaths) return [];
|
|
947
970
|
const out = [];
|
|
@@ -1035,34 +1058,93 @@ function resolve(ep, other, index) {
|
|
|
1035
1058
|
function endpoints(e, index) {
|
|
1036
1059
|
const c = e.connector;
|
|
1037
1060
|
if (!c) return [{ x: 0, y: 0 }, { x: 0, y: 0 }];
|
|
1038
|
-
|
|
1061
|
+
const w = c.waypoints ?? [];
|
|
1062
|
+
return [
|
|
1063
|
+
resolve(c.start, w.length > 0 ? w[0] : reference(c.end, index), index),
|
|
1064
|
+
resolve(c.end, w.length > 0 ? w[w.length - 1] : reference(c.start, index), index)
|
|
1065
|
+
];
|
|
1039
1066
|
}
|
|
1040
1067
|
function routePoints(r) {
|
|
1041
1068
|
if (r.kind === "polyline") return r.points;
|
|
1042
|
-
const out = [];
|
|
1043
|
-
for (let
|
|
1044
|
-
const
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1069
|
+
const out = [], p = r.points;
|
|
1070
|
+
for (let s = 0; s < Math.floor((p.length - 1) / 3); s++) {
|
|
1071
|
+
const a = p[3 * s], b = p[3 * s + 1], c = p[3 * s + 2], d = p[3 * s + 3];
|
|
1072
|
+
for (let i = s === 0 ? 0 : 1; i <= 24; i++) {
|
|
1073
|
+
const t = i / 24, u = 1 - t;
|
|
1074
|
+
out.push({
|
|
1075
|
+
x: u * u * u * a.x + 3 * u * u * t * b.x + 3 * u * t * t * c.x + t * t * t * d.x,
|
|
1076
|
+
y: u * u * u * a.y + 3 * u * u * t * b.y + 3 * u * t * t * c.y + t * t * t * d.y
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1049
1079
|
}
|
|
1050
1080
|
return out;
|
|
1051
1081
|
}
|
|
1082
|
+
var vertical = (p) => p === null ? null : p === "top" || p === "bottom";
|
|
1083
|
+
function simplified(pts) {
|
|
1084
|
+
const out = [];
|
|
1085
|
+
for (const q of pts) {
|
|
1086
|
+
const l = out[out.length - 1];
|
|
1087
|
+
if (l && Math.abs(l.x - q.x) < 0.5 && Math.abs(l.y - q.y) < 0.5) continue;
|
|
1088
|
+
if (out.length >= 2) {
|
|
1089
|
+
const p = out[out.length - 2], m = out[out.length - 1];
|
|
1090
|
+
if (Math.abs(p.x - m.x) < 0.5 && Math.abs(m.x - q.x) < 0.5 || Math.abs(p.y - m.y) < 0.5 && Math.abs(m.y - q.y) < 0.5) {
|
|
1091
|
+
out[out.length - 1] = q;
|
|
1092
|
+
continue;
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
out.push(q);
|
|
1096
|
+
}
|
|
1097
|
+
return out.length >= 2 ? out : pts;
|
|
1098
|
+
}
|
|
1099
|
+
function routeThrough(p, c) {
|
|
1100
|
+
switch (c.routing) {
|
|
1101
|
+
case "straight":
|
|
1102
|
+
return { kind: "polyline", points: p };
|
|
1103
|
+
case "curved": {
|
|
1104
|
+
const out = [p[0]];
|
|
1105
|
+
for (let i = 0; i < p.length - 1; i++) {
|
|
1106
|
+
const p0 = p[Math.max(i - 1, 0)], p1 = p[i], p2 = p[i + 1], p3 = p[Math.min(i + 2, p.length - 1)];
|
|
1107
|
+
out.push({ x: p1.x + (p2.x - p0.x) / 6, y: p1.y + (p2.y - p0.y) / 6 });
|
|
1108
|
+
out.push({ x: p2.x - (p3.x - p1.x) / 6, y: p2.y - (p3.y - p1.y) / 6 });
|
|
1109
|
+
out.push(p2);
|
|
1110
|
+
}
|
|
1111
|
+
return { kind: "curve", points: out };
|
|
1112
|
+
}
|
|
1113
|
+
case "elbow": {
|
|
1114
|
+
const a = p[0], first = p[1];
|
|
1115
|
+
let goVertical = vertical(c.start.port) ?? Math.abs(first.y - a.y) > Math.abs(first.x - a.x);
|
|
1116
|
+
const endV = vertical(c.end.port);
|
|
1117
|
+
const out = [a];
|
|
1118
|
+
for (let i = 1; i < p.length; i++) {
|
|
1119
|
+
const q = p[i], cur = out[out.length - 1];
|
|
1120
|
+
const alignedX = Math.abs(cur.x - q.x) < 0.5, alignedY = Math.abs(cur.y - q.y) < 0.5;
|
|
1121
|
+
if (alignedX || alignedY) {
|
|
1122
|
+
out.push(q);
|
|
1123
|
+
if (alignedX !== alignedY) goVertical = !alignedX;
|
|
1124
|
+
continue;
|
|
1125
|
+
}
|
|
1126
|
+
const up = i === p.length - 1 && endV !== null ? !endV : goVertical;
|
|
1127
|
+
out.push(up ? { x: cur.x, y: q.y } : { x: q.x, y: cur.y }, q);
|
|
1128
|
+
goVertical = up;
|
|
1129
|
+
}
|
|
1130
|
+
return { kind: "polyline", points: simplified(out) };
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1052
1134
|
function route(e, index) {
|
|
1053
1135
|
const c = e.connector;
|
|
1054
1136
|
if (!c) return { kind: "polyline", points: [] };
|
|
1055
1137
|
const [a, b] = endpoints(e, index);
|
|
1138
|
+
if (c.waypoints && c.waypoints.length > 0) return routeThrough([a, ...c.waypoints.map((w) => ({ x: w.x, y: w.y })), b], c);
|
|
1056
1139
|
const dx = b.x - a.x, dy = b.y - a.y;
|
|
1057
1140
|
const horizontal = Math.abs(dx) >= Math.abs(dy);
|
|
1058
1141
|
switch (c.routing) {
|
|
1059
1142
|
case "straight":
|
|
1060
1143
|
return { kind: "polyline", points: [a, b] };
|
|
1061
1144
|
case "curved":
|
|
1062
|
-
if (horizontal) return { kind: "
|
|
1063
|
-
return { kind: "
|
|
1145
|
+
if (horizontal) return { kind: "curve", points: [a, { x: a.x + dx / 2, y: a.y }, { x: b.x - dx / 2, y: b.y }, b] };
|
|
1146
|
+
return { kind: "curve", points: [a, { x: a.x, y: a.y + dy / 2 }, { x: b.x, y: b.y - dy / 2 }, b] };
|
|
1064
1147
|
case "elbow": {
|
|
1065
|
-
const vertical = (p) => p === null ? null : p === "top" || p === "bottom";
|
|
1066
1148
|
const startV = vertical(c.start.port) ?? !horizontal;
|
|
1067
1149
|
const endV = vertical(c.end.port) ?? !horizontal;
|
|
1068
1150
|
if (startV && endV) {
|
|
@@ -1642,10 +1724,12 @@ function dashPattern(dash, width) {
|
|
|
1642
1724
|
return [0.01, Math.max(5, width * 2.6)];
|
|
1643
1725
|
}
|
|
1644
1726
|
}
|
|
1645
|
-
function connectorParts(e, index) {
|
|
1727
|
+
function connectorParts(e, index, page) {
|
|
1646
1728
|
const c = e.connector;
|
|
1647
1729
|
if (!c) return [new Path(), new Path()];
|
|
1648
1730
|
const [a, b] = endpoints(e, index);
|
|
1731
|
+
const r = route(e, index);
|
|
1732
|
+
const hops = c.hops === true && page ? hopCrossings(e, r, page, index) : [];
|
|
1649
1733
|
const key = [
|
|
1650
1734
|
e.seed,
|
|
1651
1735
|
e.style.roughness,
|
|
@@ -1659,28 +1743,166 @@ function connectorParts(e, index) {
|
|
|
1659
1743
|
a.x,
|
|
1660
1744
|
a.y,
|
|
1661
1745
|
b.x,
|
|
1662
|
-
b.y
|
|
1746
|
+
b.y,
|
|
1747
|
+
JSON.stringify(c.waypoints),
|
|
1748
|
+
JSON.stringify(hops)
|
|
1663
1749
|
].join("|");
|
|
1664
|
-
return connectors.get(key, () => computeConnector(e,
|
|
1750
|
+
return connectors.get(key, () => computeConnector(e, r, hops));
|
|
1751
|
+
}
|
|
1752
|
+
function hopRadius(strokeWidth) {
|
|
1753
|
+
return Math.max(8, strokeWidth * 4);
|
|
1754
|
+
}
|
|
1755
|
+
function hopSamples(r) {
|
|
1756
|
+
const pts = r.points, out = [];
|
|
1757
|
+
if (r.kind === "polyline") {
|
|
1758
|
+
for (let i = 0; i < pts.length - 1; i++) out.push([pts[i], pts[i + 1]]);
|
|
1759
|
+
return out;
|
|
1760
|
+
}
|
|
1761
|
+
for (let s = 0; s < Math.floor((pts.length - 1) / 3); s++) {
|
|
1762
|
+
const seg = pts.slice(3 * s, 3 * s + 4), samples = [];
|
|
1763
|
+
for (let i = 0; i <= 24; i++) samples.push(cubicPoint(seg, i / 24));
|
|
1764
|
+
out.push(samples);
|
|
1765
|
+
}
|
|
1766
|
+
return out;
|
|
1767
|
+
}
|
|
1768
|
+
function runningLengths(samples) {
|
|
1769
|
+
const out = [0];
|
|
1770
|
+
for (let i = 1; i < samples.length; i++) out.push(out[i - 1] + Math.hypot(samples[i].x - samples[i - 1].x, samples[i].y - samples[i - 1].y));
|
|
1771
|
+
return out;
|
|
1772
|
+
}
|
|
1773
|
+
function cubicPoint(p, t) {
|
|
1774
|
+
const u = 1 - t;
|
|
1775
|
+
return {
|
|
1776
|
+
x: u * u * u * p[0].x + 3 * u * u * t * p[1].x + 3 * u * t * t * p[2].x + t * t * t * p[3].x,
|
|
1777
|
+
y: u * u * u * p[0].y + 3 * u * u * t * p[1].y + 3 * u * t * t * p[2].y + t * t * t * p[3].y
|
|
1778
|
+
};
|
|
1779
|
+
}
|
|
1780
|
+
function subCubic(p, t0, t1) {
|
|
1781
|
+
const split = (q, t) => {
|
|
1782
|
+
const lerp = (a, b) => ({ x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t });
|
|
1783
|
+
const ab = lerp(q[0], q[1]), bc = lerp(q[1], q[2]), cd = lerp(q[2], q[3]);
|
|
1784
|
+
const abc = lerp(ab, bc), bcd = lerp(bc, cd), m = lerp(abc, bcd);
|
|
1785
|
+
return [[q[0], ab, abc, m], [m, bcd, cd, q[3]]];
|
|
1786
|
+
};
|
|
1787
|
+
const left = split(p, t1)[0];
|
|
1788
|
+
return t1 > 0 ? split(left, t0 / t1)[1] : [p[0], p[0], p[0], p[0]];
|
|
1789
|
+
}
|
|
1790
|
+
function hopCrossings(e, r, page, index) {
|
|
1791
|
+
const segments = hopSamples(r);
|
|
1792
|
+
if (segments.length === 0) return [];
|
|
1793
|
+
const visible = new Set(page.layers.filter((l) => l.visible).map((l) => l.id));
|
|
1794
|
+
const beneath = [];
|
|
1795
|
+
for (const o of orderedElements(page)) {
|
|
1796
|
+
if (o.id === e.id) break;
|
|
1797
|
+
if (o.kind !== "connector" || !visible.has(o.layerID)) continue;
|
|
1798
|
+
beneath.push(routePoints(route(o, index)));
|
|
1799
|
+
}
|
|
1800
|
+
const rad = hopRadius(e.style.strokeWidth);
|
|
1801
|
+
const out = [];
|
|
1802
|
+
for (const samples of segments) {
|
|
1803
|
+
const along = runningLengths(samples);
|
|
1804
|
+
const length = along[along.length - 1];
|
|
1805
|
+
const found = [];
|
|
1806
|
+
for (let i = 0; i < samples.length - 1; i++) {
|
|
1807
|
+
const p = samples[i], q = samples[i + 1];
|
|
1808
|
+
const dx = q.x - p.x, dy = q.y - p.y;
|
|
1809
|
+
for (const line2 of beneath) {
|
|
1810
|
+
for (let k = 0; k < line2.length - 1; k++) {
|
|
1811
|
+
const a = line2[k], b = line2[k + 1];
|
|
1812
|
+
const ex = b.x - a.x, ey = b.y - a.y;
|
|
1813
|
+
const det = dx * ey - dy * ex;
|
|
1814
|
+
if (Math.abs(det) < 1e-9) continue;
|
|
1815
|
+
const wx = a.x - p.x, wy = a.y - p.y;
|
|
1816
|
+
const t = (wx * ey - wy * ex) / det, s = (wx * dy - wy * dx) / det;
|
|
1817
|
+
if (t > 0 && t < 1 && s >= 0 && s <= 1) found.push(along[i] + t * (along[i + 1] - along[i]));
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
}
|
|
1821
|
+
const kept = [];
|
|
1822
|
+
for (const at of found.sort((x, y) => x - y)) {
|
|
1823
|
+
if (at < rad + 2 || at > length - rad - 2) continue;
|
|
1824
|
+
if (kept.length > 0 && at - kept[kept.length - 1] < 2 * rad + 2) continue;
|
|
1825
|
+
kept.push(at);
|
|
1826
|
+
}
|
|
1827
|
+
out.push(kept);
|
|
1828
|
+
}
|
|
1829
|
+
return out;
|
|
1665
1830
|
}
|
|
1666
|
-
function computeConnector(e,
|
|
1831
|
+
function computeConnector(e, r, hops) {
|
|
1667
1832
|
const c = e.connector;
|
|
1668
1833
|
const rng = new SeededRNG(e.seed);
|
|
1669
1834
|
const rough = e.style.roughness, bow = e.style.bowing;
|
|
1670
1835
|
const path = new Path(), solid = new Path();
|
|
1671
|
-
const
|
|
1836
|
+
const w = rough * 3;
|
|
1837
|
+
const j = () => rng.signed() * w;
|
|
1838
|
+
const hop = (s, t, u, rad2) => {
|
|
1839
|
+
let n = { x: u.y, y: -u.x };
|
|
1840
|
+
if (n.y > 1e-9 || Math.abs(n.y) <= 1e-9 && n.x > 0) n = { x: -n.x, y: -n.y };
|
|
1841
|
+
const k = rad2 * 0.5523;
|
|
1842
|
+
const m = { x: (s.x + t.x) / 2, y: (s.y + t.y) / 2 };
|
|
1843
|
+
const top = { x: m.x + n.x * rad2, y: m.y + n.y * rad2 };
|
|
1844
|
+
path.moveTo(s);
|
|
1845
|
+
const a1x = s.x + n.x * k + j(), a1y = s.y + n.y * k + j(), a2x = top.x - u.x * k + j(), a2y = top.y - u.y * k + j();
|
|
1846
|
+
path.curveTo(top, { x: a1x, y: a1y }, { x: a2x, y: a2y });
|
|
1847
|
+
const b1x = top.x + u.x * k + j(), b1y = top.y + u.y * k + j(), b2x = t.x + n.x * k + j(), b2y = t.y + n.y * k + j();
|
|
1848
|
+
path.curveTo(t, { x: b1x, y: b1y }, { x: b2x, y: b2y });
|
|
1849
|
+
};
|
|
1850
|
+
const rad = hopRadius(e.style.strokeWidth);
|
|
1672
1851
|
if (r.kind === "polyline") {
|
|
1673
1852
|
const pts2 = r.points;
|
|
1853
|
+
const straight = (p, q) => {
|
|
1854
|
+
if (rough <= 0) path.moveTo(p).lineTo(q);
|
|
1855
|
+
else line(p, q, rng, rough, bow, true, path);
|
|
1856
|
+
};
|
|
1674
1857
|
for (let i = 0; i < Math.max(pts2.length - 1, 0); i++) {
|
|
1675
|
-
|
|
1676
|
-
|
|
1858
|
+
const p = pts2[i], q = pts2[i + 1];
|
|
1859
|
+
const length = Math.hypot(q.x - p.x, q.y - p.y);
|
|
1860
|
+
const u = length > 0 ? { x: (q.x - p.x) / length, y: (q.y - p.y) / length } : { x: 0, y: 0 };
|
|
1861
|
+
const at = (d) => ({ x: p.x + u.x * d, y: p.y + u.y * d });
|
|
1862
|
+
let from = p;
|
|
1863
|
+
for (const d of i < hops.length ? hops[i] : []) {
|
|
1864
|
+
const s = at(d - rad), t = at(d + rad);
|
|
1865
|
+
straight(from, s);
|
|
1866
|
+
hop(s, t, u, rad);
|
|
1867
|
+
from = t;
|
|
1868
|
+
}
|
|
1869
|
+
straight(from, q);
|
|
1677
1870
|
}
|
|
1678
1871
|
} else {
|
|
1679
|
-
|
|
1680
|
-
const
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1872
|
+
const pts2 = r.points;
|
|
1873
|
+
const samples = hopSamples(r);
|
|
1874
|
+
path.moveTo(pts2[0]);
|
|
1875
|
+
for (let s = 0; s < Math.floor((pts2.length - 1) / 3); s++) {
|
|
1876
|
+
const b = pts2[3 * s + 1], c2 = pts2[3 * s + 2], d = pts2[3 * s + 3];
|
|
1877
|
+
const c1x = b.x + j(), c1y = b.y + j(), c2x = c2.x + j(), c2y = c2.y + j();
|
|
1878
|
+
const here = s < hops.length ? hops[s] : [];
|
|
1879
|
+
if (here.length === 0) {
|
|
1880
|
+
path.curveTo(d, { x: c1x, y: c1y }, { x: c2x, y: c2y });
|
|
1881
|
+
continue;
|
|
1882
|
+
}
|
|
1883
|
+
const cubic = [pts2[3 * s], { x: c1x, y: c1y }, { x: c2x, y: c2y }, d];
|
|
1884
|
+
const along = runningLengths(samples[s]);
|
|
1885
|
+
const param = (at) => {
|
|
1886
|
+
let i = 0;
|
|
1887
|
+
while (i < along.length - 2 && along[i + 1] < at) i++;
|
|
1888
|
+
const span = along[i + 1] - along[i];
|
|
1889
|
+
return (i + (span > 0 ? (at - along[i]) / span : 0)) / 24;
|
|
1890
|
+
};
|
|
1891
|
+
let from = 0;
|
|
1892
|
+
for (const at of here) {
|
|
1893
|
+
const t0 = param(at - rad), t1 = param(at + rad);
|
|
1894
|
+
const piece = subCubic(cubic, from, t0);
|
|
1895
|
+
if (s > 0 || from > 0) path.moveTo(piece[0]);
|
|
1896
|
+
path.curveTo(piece[3], piece[1], piece[2]);
|
|
1897
|
+
const a = cubicPoint(cubic, t0), z = cubicPoint(cubic, t1);
|
|
1898
|
+
const width = Math.hypot(z.x - a.x, z.y - a.y);
|
|
1899
|
+
const u = width > 0 ? { x: (z.x - a.x) / width, y: (z.y - a.y) / width } : { x: 0, y: 0 };
|
|
1900
|
+
hop(a, z, u, width / 2);
|
|
1901
|
+
from = t1;
|
|
1902
|
+
}
|
|
1903
|
+
const rest = subCubic(cubic, from, 1);
|
|
1904
|
+
path.moveTo(rest[0]).curveTo(rest[3], rest[1], rest[2]);
|
|
1905
|
+
}
|
|
1684
1906
|
}
|
|
1685
1907
|
const pts = routePoints(r);
|
|
1686
1908
|
const size = Math.max(10, e.style.strokeWidth * 5);
|
|
@@ -1702,10 +1924,10 @@ function computeConnector(e, index) {
|
|
|
1702
1924
|
solid.moveTo(tip).lineTo(w1).lineTo(w2).close();
|
|
1703
1925
|
break;
|
|
1704
1926
|
case "dot": {
|
|
1705
|
-
const
|
|
1927
|
+
const rad2 = size * 0.32;
|
|
1706
1928
|
const ang = Math.atan2(tip.y - from.y, tip.x - from.x);
|
|
1707
|
-
const cx = tip.x -
|
|
1708
|
-
solid.addEllipse({ x: cx -
|
|
1929
|
+
const cx = tip.x - rad2 * Math.cos(ang), cy = tip.y - rad2 * Math.sin(ang);
|
|
1930
|
+
solid.addEllipse({ x: cx - rad2, y: cy - rad2, width: 2 * rad2, height: 2 * rad2 });
|
|
1709
1931
|
break;
|
|
1710
1932
|
}
|
|
1711
1933
|
case "bar": {
|
|
@@ -2172,7 +2394,12 @@ function placed(inner, instance, symbol) {
|
|
|
2172
2394
|
const q = point(p);
|
|
2173
2395
|
return { elementID: null, port: p.port, x: q.x, y: q.y };
|
|
2174
2396
|
};
|
|
2175
|
-
e.connector = {
|
|
2397
|
+
e.connector = {
|
|
2398
|
+
...inner.connector,
|
|
2399
|
+
start: end(inner.connector.start),
|
|
2400
|
+
end: end(inner.connector.end),
|
|
2401
|
+
waypoints: inner.connector.waypoints?.map(point) ?? null
|
|
2402
|
+
};
|
|
2176
2403
|
}
|
|
2177
2404
|
return e;
|
|
2178
2405
|
}
|
|
@@ -2948,7 +3175,7 @@ function svgParts(page, background, fixed, links = false) {
|
|
|
2948
3175
|
if (!visible.has(e.layerID)) continue;
|
|
2949
3176
|
const alpha = Math.min(Math.max(e.style.opacity ?? 1, 0), 1);
|
|
2950
3177
|
if (alpha <= 0) continue;
|
|
2951
|
-
let body = svgElement(e, index, bg);
|
|
3178
|
+
let body = svgElement(e, index, bg, d);
|
|
2952
3179
|
if (angle(e) !== 0 && e.kind !== "connector") {
|
|
2953
3180
|
body = `<g transform="rotate(${fmt(angle(e) * 180 / Math.PI)} ${fmt(midX(frame(e)))} ${fmt(midY(frame(e)))})">
|
|
2954
3181
|
${body}</g>
|
|
@@ -2983,7 +3210,7 @@ function typed(s, progress) {
|
|
|
2983
3210
|
function revealProgress(e) {
|
|
2984
3211
|
return e.reveal !== null && e.reveal < 1 ? Math.max(0, e.reveal) : null;
|
|
2985
3212
|
}
|
|
2986
|
-
function svgElement(e, index, labelBackground) {
|
|
3213
|
+
function svgElement(e, index, labelBackground, page) {
|
|
2987
3214
|
const stroke = color(e.style.stroke);
|
|
2988
3215
|
let strokeAttrs = `stroke="${stroke}" stroke-width="${fmt(e.style.strokeWidth)}" stroke-linecap="round" stroke-linejoin="round"`;
|
|
2989
3216
|
const dash = dashPattern(e.style.dash, e.style.strokeWidth);
|
|
@@ -2994,7 +3221,7 @@ function svgElement(e, index, labelBackground) {
|
|
|
2994
3221
|
if (r !== null) {
|
|
2995
3222
|
if (e.kind === "text") text = typed(text ?? "", r);
|
|
2996
3223
|
else if (e.kind === "connector") {
|
|
2997
|
-
return `<path d="${connectorParts(e, index)[0].trimmed(r).data()}" fill="none" ${strokeAttrs}/>
|
|
3224
|
+
return `<path d="${connectorParts(e, index, page)[0].trimmed(r).data()}" fill="none" ${strokeAttrs}/>
|
|
2998
3225
|
`;
|
|
2999
3226
|
} else {
|
|
3000
3227
|
return `<path d="${outline(e).trimmed(r).data()}" fill="none" ${strokeAttrs}/>
|
|
@@ -3003,7 +3230,7 @@ function svgElement(e, index, labelBackground) {
|
|
|
3003
3230
|
}
|
|
3004
3231
|
switch (e.kind) {
|
|
3005
3232
|
case "connector": {
|
|
3006
|
-
const [line2, filled2] = connectorParts(e, index);
|
|
3233
|
+
const [line2, filled2] = connectorParts(e, index, page);
|
|
3007
3234
|
let s = `<path d="${line2.data()}" fill="none" ${strokeAttrs}/>
|
|
3008
3235
|
`;
|
|
3009
3236
|
if (!filled2.isEmpty) s += `<path d="${filled2.data()}" fill="${stroke}" stroke="none"/>
|
|
@@ -3086,7 +3313,7 @@ function textElement(text, f, e, centered) {
|
|
|
3086
3313
|
const total = lineHeight * lines.length;
|
|
3087
3314
|
const x = centered ? midX(f) : f.x;
|
|
3088
3315
|
const top = centered ? midY(f) - total / 2 : f.y;
|
|
3089
|
-
let s = `<text x="${fmt(x)}" font-family="${cssFamily(e.style.font)}" font-size="${fmt(size)}" fill="${color(e
|
|
3316
|
+
let s = `<text x="${fmt(x)}" font-family="${cssFamily(e.style.font)}" font-size="${fmt(size)}" fill="${color(labelColor(e))}"${centered ? ` text-anchor="middle"` : ""}>`;
|
|
3090
3317
|
lines.forEach((line2, i) => {
|
|
3091
3318
|
s += `<tspan x="${fmt(x)}" y="${fmt(top + lineHeight * (i + 0.8))}">${escapeXML(line2)}</tspan>`;
|
|
3092
3319
|
});
|
|
@@ -3735,6 +3962,7 @@ function inspectFrame(page, t) {
|
|
|
3735
3962
|
d.strokeWidth = r4(e.style.strokeWidth);
|
|
3736
3963
|
d.stroke = rgba2(e.style.stroke);
|
|
3737
3964
|
d.fill = rgba2(e.style.fill);
|
|
3965
|
+
if (e.style.textColor !== null) d.textColor = rgba2(e.style.textColor);
|
|
3738
3966
|
if (e.kind === "connector") {
|
|
3739
3967
|
const [a, b] = endpoints(e, index);
|
|
3740
3968
|
d.start = pt(a);
|
|
@@ -3778,7 +4006,7 @@ function render(source, options = {}) {
|
|
|
3778
4006
|
function isFile(v) {
|
|
3779
4007
|
return typeof v === "object" && v !== null && Array.isArray(v.pages) && Array.isArray(v.warnings);
|
|
3780
4008
|
}
|
|
3781
|
-
var VERSION = "0.6.
|
|
4009
|
+
var VERSION = "0.6.3";
|
|
3782
4010
|
|
|
3783
4011
|
// src/player-entry.ts
|
|
3784
4012
|
definePlayer();
|