@ngroznykh/papirus 0.3.1 → 0.3.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/dist/papirus.js +95 -46
- package/dist/papirus.js.map +1 -1
- package/dist/utils/SvgExporter.d.ts +4 -5
- package/dist/utils/SvgExporter.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/papirus.js
CHANGED
|
@@ -8104,7 +8104,7 @@ class SvgExporter {
|
|
|
8104
8104
|
const padding = options.padding ?? 20;
|
|
8105
8105
|
const includeBackground = options.includeBackground ?? true;
|
|
8106
8106
|
const backgroundColor = options.backgroundColor ?? "#ffffff";
|
|
8107
|
-
const edgeLabelOffset = options.edgeLabelOffset
|
|
8107
|
+
const edgeLabelOffset = options.edgeLabelOffset;
|
|
8108
8108
|
const bounds = getContentBounds({
|
|
8109
8109
|
nodes: this.renderer.nodes.values(),
|
|
8110
8110
|
edges: this.renderer.edges.values(),
|
|
@@ -8124,17 +8124,15 @@ class SvgExporter {
|
|
|
8124
8124
|
this.renderer.nodes.values()
|
|
8125
8125
|
);
|
|
8126
8126
|
const parts = [];
|
|
8127
|
-
const markerDefs = /* @__PURE__ */ new Map();
|
|
8128
8127
|
const renderedEdges = [];
|
|
8129
8128
|
parts.push(
|
|
8130
8129
|
`<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">`
|
|
8131
8130
|
);
|
|
8132
8131
|
for (const edge of this.renderer.edges.values()) {
|
|
8133
8132
|
if (edge.visible) {
|
|
8134
|
-
renderedEdges.push(this.renderEdge(edge,
|
|
8133
|
+
renderedEdges.push(this.renderEdge(edge, edgeLabelOffset));
|
|
8135
8134
|
}
|
|
8136
8135
|
}
|
|
8137
|
-
parts.push(this.buildDefs(markerDefs));
|
|
8138
8136
|
if (includeBackground) {
|
|
8139
8137
|
parts.push(`<rect width="100%" height="100%" fill="${backgroundColor}"/>`);
|
|
8140
8138
|
}
|
|
@@ -8222,7 +8220,7 @@ class SvgExporter {
|
|
|
8222
8220
|
label
|
|
8223
8221
|
].join("");
|
|
8224
8222
|
}
|
|
8225
|
-
renderEdge(edge,
|
|
8223
|
+
renderEdge(edge, edgeLabelOffset) {
|
|
8226
8224
|
const path = edge.path;
|
|
8227
8225
|
if (path.length < 2) {
|
|
8228
8226
|
return "";
|
|
@@ -8235,12 +8233,9 @@ class SvgExporter {
|
|
|
8235
8233
|
const dash = dashValues ? ` stroke-dasharray="${dashValues.join(" ")}"` : "";
|
|
8236
8234
|
const dashOffset = style.lineDashOffset !== void 0 ? ` stroke-dashoffset="${style.lineDashOffset}"` : "";
|
|
8237
8235
|
const d = this.buildPath(edge);
|
|
8238
|
-
const
|
|
8239
|
-
const endMarkerConfig = this.resolveMarkerConfig(edge, "end");
|
|
8240
|
-
const markerStart = startMarkerConfig ? ` marker-start="url(#${this.ensureMarkerDef(markerDefs, startMarkerConfig, "start", stroke)})"` : "";
|
|
8241
|
-
const markerEnd = endMarkerConfig ? ` marker-end="url(#${this.ensureMarkerDef(markerDefs, endMarkerConfig, "end", stroke)})"` : "";
|
|
8236
|
+
const markerShapes = this.renderEdgeMarkers(edge, stroke);
|
|
8242
8237
|
const label = edge.label ? this.renderTextLabel(edge.label.text, this.getEdgeLabelPoint(edge, edgeLabelOffset), edge.label.style) : "";
|
|
8243
|
-
return `<path d="${d}" fill="none" stroke="${stroke}" stroke-width="${strokeWidth}" opacity="${opacity}" color="${stroke}"${dash}${dashOffset}
|
|
8238
|
+
return `<path d="${d}" fill="none" stroke="${stroke}" stroke-width="${strokeWidth}" opacity="${opacity}" color="${stroke}"${dash}${dashOffset}/>${markerShapes}${label}`;
|
|
8244
8239
|
}
|
|
8245
8240
|
resolveMarkerConfig(edge, side) {
|
|
8246
8241
|
const marker = side === "start" ? edge.startMarker : edge.endMarker;
|
|
@@ -8252,40 +8247,99 @@ class SvgExporter {
|
|
|
8252
8247
|
}
|
|
8253
8248
|
return edge.arrowType === "double" ? { type: "arrow" } : null;
|
|
8254
8249
|
}
|
|
8255
|
-
|
|
8256
|
-
const
|
|
8257
|
-
const
|
|
8258
|
-
const
|
|
8259
|
-
|
|
8260
|
-
|
|
8261
|
-
|
|
8262
|
-
|
|
8263
|
-
|
|
8264
|
-
|
|
8265
|
-
|
|
8266
|
-
|
|
8267
|
-
|
|
8268
|
-
|
|
8269
|
-
|
|
8270
|
-
);
|
|
8250
|
+
renderEdgeMarkers(edge, edgeStroke) {
|
|
8251
|
+
const startMarker = this.resolveMarkerConfig(edge, "start");
|
|
8252
|
+
const endMarker = this.resolveMarkerConfig(edge, "end");
|
|
8253
|
+
const parts = [];
|
|
8254
|
+
if (endMarker) {
|
|
8255
|
+
const points = this.getMarkerPoints(edge, "end");
|
|
8256
|
+
if (points) {
|
|
8257
|
+
parts.push(this.renderMarkerShape(endMarker, points.from, points.to, edgeStroke));
|
|
8258
|
+
}
|
|
8259
|
+
}
|
|
8260
|
+
if (startMarker) {
|
|
8261
|
+
const points = this.getMarkerPoints(edge, "start");
|
|
8262
|
+
if (points) {
|
|
8263
|
+
parts.push(this.renderMarkerShape(startMarker, points.from, points.to, edgeStroke));
|
|
8264
|
+
}
|
|
8271
8265
|
}
|
|
8272
|
-
return
|
|
8266
|
+
return parts.join("");
|
|
8273
8267
|
}
|
|
8274
|
-
|
|
8275
|
-
const
|
|
8276
|
-
|
|
8268
|
+
getMarkerPoints(edge, position) {
|
|
8269
|
+
const path = edge.path;
|
|
8270
|
+
if (path.length < 2) {
|
|
8271
|
+
return null;
|
|
8272
|
+
}
|
|
8273
|
+
if (edge.type === "bezier" && path.length >= 4) {
|
|
8274
|
+
const epsilon = 1e-3;
|
|
8275
|
+
const isSame = (a, b) => Math.abs(a.x - b.x) < epsilon && Math.abs(a.y - b.y) < epsilon;
|
|
8276
|
+
if (position === "end") {
|
|
8277
|
+
const endIndex = path.length - 1;
|
|
8278
|
+
const endPoint = path[endIndex];
|
|
8279
|
+
let from = path[endIndex - 1];
|
|
8280
|
+
if (isSame(from, endPoint)) {
|
|
8281
|
+
from = path[endIndex - 2];
|
|
8282
|
+
if (isSame(from, endPoint)) {
|
|
8283
|
+
from = path[0];
|
|
8284
|
+
}
|
|
8285
|
+
}
|
|
8286
|
+
return { from, to: endPoint };
|
|
8287
|
+
}
|
|
8288
|
+
const start = path[0];
|
|
8289
|
+
let next = path[1];
|
|
8290
|
+
if (isSame(next, start)) {
|
|
8291
|
+
next = path[2];
|
|
8292
|
+
if (isSame(next, start)) {
|
|
8293
|
+
next = path[path.length - 1];
|
|
8294
|
+
}
|
|
8295
|
+
}
|
|
8296
|
+
return { from: next, to: start };
|
|
8297
|
+
}
|
|
8298
|
+
if (position === "end") {
|
|
8299
|
+
return { from: path[path.length - 2], to: path[path.length - 1] };
|
|
8300
|
+
}
|
|
8301
|
+
return { from: path[1], to: path[0] };
|
|
8277
8302
|
}
|
|
8278
|
-
renderMarkerShape(marker,
|
|
8303
|
+
renderMarkerShape(marker, from, to, edgeStroke) {
|
|
8304
|
+
const angle2 = Math.atan2(to.y - from.y, to.x - from.x);
|
|
8305
|
+
const size = marker.size ?? 12;
|
|
8306
|
+
const stroke = marker.strokeColor ?? edgeStroke;
|
|
8307
|
+
const fill = marker.fillColor ?? stroke;
|
|
8308
|
+
const fillOpacity = marker.fillOpacity ?? 1;
|
|
8279
8309
|
switch (marker.type) {
|
|
8280
|
-
case "open":
|
|
8281
|
-
|
|
8282
|
-
|
|
8283
|
-
|
|
8284
|
-
|
|
8285
|
-
return `<
|
|
8310
|
+
case "open": {
|
|
8311
|
+
const x1 = to.x - size * Math.cos(angle2 - ARROW_ANGLE);
|
|
8312
|
+
const y1 = to.y - size * Math.sin(angle2 - ARROW_ANGLE);
|
|
8313
|
+
const x2 = to.x - size * Math.cos(angle2 + ARROW_ANGLE);
|
|
8314
|
+
const y2 = to.y - size * Math.sin(angle2 + ARROW_ANGLE);
|
|
8315
|
+
return `<path d="M ${to.x} ${to.y} L ${x1} ${y1} M ${to.x} ${to.y} L ${x2} ${y2}" fill="none" stroke="${stroke}" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>`;
|
|
8316
|
+
}
|
|
8317
|
+
case "diamond": {
|
|
8318
|
+
const halfLength = size / 2;
|
|
8319
|
+
const halfWidth = size * 0.3;
|
|
8320
|
+
const cos = Math.cos(angle2);
|
|
8321
|
+
const sin = Math.sin(angle2);
|
|
8322
|
+
const p1x = to.x - halfLength * cos + halfWidth * sin;
|
|
8323
|
+
const p1y = to.y - halfLength * sin - halfWidth * cos;
|
|
8324
|
+
const backX = to.x - size * cos;
|
|
8325
|
+
const backY = to.y - size * sin;
|
|
8326
|
+
const p2x = to.x - halfLength * cos - halfWidth * sin;
|
|
8327
|
+
const p2y = to.y - halfLength * sin + halfWidth * cos;
|
|
8328
|
+
return `<path d="M ${to.x} ${to.y} L ${p1x} ${p1y} L ${backX} ${backY} L ${p2x} ${p2y} Z" fill="${fill}" fill-opacity="${fillOpacity}" stroke="${stroke}" stroke-width="1"/>`;
|
|
8329
|
+
}
|
|
8330
|
+
case "circle": {
|
|
8331
|
+
const cx = to.x - size * Math.cos(angle2);
|
|
8332
|
+
const cy = to.y - size * Math.sin(angle2);
|
|
8333
|
+
return `<circle cx="${cx}" cy="${cy}" r="${size}" fill="${fill}" fill-opacity="${fillOpacity}" stroke="${stroke}" stroke-width="1"/>`;
|
|
8334
|
+
}
|
|
8286
8335
|
case "arrow":
|
|
8287
|
-
default:
|
|
8288
|
-
|
|
8336
|
+
default: {
|
|
8337
|
+
const x1 = to.x - size * Math.cos(angle2 - ARROW_ANGLE);
|
|
8338
|
+
const y1 = to.y - size * Math.sin(angle2 - ARROW_ANGLE);
|
|
8339
|
+
const x2 = to.x - size * Math.cos(angle2 + ARROW_ANGLE);
|
|
8340
|
+
const y2 = to.y - size * Math.sin(angle2 + ARROW_ANGLE);
|
|
8341
|
+
return `<path d="M ${to.x} ${to.y} L ${x1} ${y1} L ${x2} ${y2} Z" fill="${fill}" fill-opacity="${fillOpacity}" stroke="${stroke}" stroke-width="1"/>`;
|
|
8342
|
+
}
|
|
8289
8343
|
}
|
|
8290
8344
|
}
|
|
8291
8345
|
buildPath(edge) {
|
|
@@ -8338,9 +8392,10 @@ class SvgExporter {
|
|
|
8338
8392
|
}
|
|
8339
8393
|
getEdgeLabelPoint(edge, edgeLabelOffset) {
|
|
8340
8394
|
const midpoint = this.getPathMidpoint(edge);
|
|
8395
|
+
const effectiveOffset = edgeLabelOffset ?? edge.labelOffset ?? 0;
|
|
8341
8396
|
return {
|
|
8342
8397
|
x: midpoint.x,
|
|
8343
|
-
y: midpoint.y +
|
|
8398
|
+
y: midpoint.y + effectiveOffset
|
|
8344
8399
|
};
|
|
8345
8400
|
}
|
|
8346
8401
|
renderTextLabel(text, point, style = {}) {
|
|
@@ -8354,12 +8409,6 @@ class SvgExporter {
|
|
|
8354
8409
|
text
|
|
8355
8410
|
)}</text>`;
|
|
8356
8411
|
}
|
|
8357
|
-
buildDefs(markerDefs) {
|
|
8358
|
-
if (markerDefs.size === 0) {
|
|
8359
|
-
return "<defs></defs>";
|
|
8360
|
-
}
|
|
8361
|
-
return `<defs>${Array.from(markerDefs.values()).join("")}</defs>`;
|
|
8362
|
-
}
|
|
8363
8412
|
createEmptySvg(width, height, backgroundColor, includeBackground) {
|
|
8364
8413
|
return [
|
|
8365
8414
|
`<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">`,
|