sketchmark 1.2.0 → 1.2.1
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/README.md +7 -5
- package/dist/index.cjs +483 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +483 -10
- package/dist/index.js.map +1 -1
- package/dist/renderer/shapes/path-geometry.d.ts +8 -0
- package/dist/renderer/shapes/path-geometry.d.ts.map +1 -0
- package/dist/renderer/shapes/path.d.ts.map +1 -1
- package/dist/renderer/svg/index.d.ts.map +1 -1
- package/dist/sketchmark.iife.js +483 -10
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -4297,37 +4297,510 @@ const lineShape = {
|
|
|
4297
4297
|
},
|
|
4298
4298
|
};
|
|
4299
4299
|
|
|
4300
|
+
const COMMAND_RE = /^[AaCcHhLlMmQqSsTtVvZz]$/;
|
|
4301
|
+
const TOKEN_RE = /[AaCcHhLlMmQqSsTtVvZz]|[-+]?(?:\d*\.\d+|\d+)(?:[eE][-+]?\d+)?/g;
|
|
4302
|
+
const EPSILON = 1e-6;
|
|
4303
|
+
const PARAM_COUNTS = {
|
|
4304
|
+
A: 7,
|
|
4305
|
+
C: 6,
|
|
4306
|
+
H: 1,
|
|
4307
|
+
L: 2,
|
|
4308
|
+
M: 2,
|
|
4309
|
+
Q: 4,
|
|
4310
|
+
S: 4,
|
|
4311
|
+
T: 2,
|
|
4312
|
+
V: 1,
|
|
4313
|
+
Z: 0,
|
|
4314
|
+
};
|
|
4315
|
+
function isCommandToken(token) {
|
|
4316
|
+
return COMMAND_RE.test(token);
|
|
4317
|
+
}
|
|
4318
|
+
function formatNumber(value) {
|
|
4319
|
+
const rounded = Math.abs(value) < EPSILON ? 0 : Number(value.toFixed(3));
|
|
4320
|
+
return Object.is(rounded, -0) ? "0" : String(rounded);
|
|
4321
|
+
}
|
|
4322
|
+
function parseRawSegments(pathData) {
|
|
4323
|
+
const tokens = pathData.match(TOKEN_RE) ?? [];
|
|
4324
|
+
if (!tokens.length)
|
|
4325
|
+
return [];
|
|
4326
|
+
const segments = [];
|
|
4327
|
+
let index = 0;
|
|
4328
|
+
let currentCommand = null;
|
|
4329
|
+
while (index < tokens.length) {
|
|
4330
|
+
const token = tokens[index];
|
|
4331
|
+
if (isCommandToken(token)) {
|
|
4332
|
+
currentCommand = token;
|
|
4333
|
+
index += 1;
|
|
4334
|
+
if (token === "Z" || token === "z") {
|
|
4335
|
+
segments.push({ command: "Z", values: [] });
|
|
4336
|
+
}
|
|
4337
|
+
continue;
|
|
4338
|
+
}
|
|
4339
|
+
if (!currentCommand)
|
|
4340
|
+
break;
|
|
4341
|
+
const upper = currentCommand.toUpperCase();
|
|
4342
|
+
const paramCount = PARAM_COUNTS[upper];
|
|
4343
|
+
if (!paramCount) {
|
|
4344
|
+
index += 1;
|
|
4345
|
+
continue;
|
|
4346
|
+
}
|
|
4347
|
+
let isFirstMove = upper === "M";
|
|
4348
|
+
while (index < tokens.length && !isCommandToken(tokens[index])) {
|
|
4349
|
+
if (index + paramCount > tokens.length)
|
|
4350
|
+
return segments;
|
|
4351
|
+
const values = tokens
|
|
4352
|
+
.slice(index, index + paramCount)
|
|
4353
|
+
.map((value) => Number(value));
|
|
4354
|
+
if (values.some((value) => Number.isNaN(value))) {
|
|
4355
|
+
return segments;
|
|
4356
|
+
}
|
|
4357
|
+
if (upper === "M") {
|
|
4358
|
+
const moveCommand = isFirstMove
|
|
4359
|
+
? currentCommand
|
|
4360
|
+
: currentCommand === "m"
|
|
4361
|
+
? "l"
|
|
4362
|
+
: "L";
|
|
4363
|
+
segments.push({ command: moveCommand, values });
|
|
4364
|
+
isFirstMove = false;
|
|
4365
|
+
}
|
|
4366
|
+
else {
|
|
4367
|
+
segments.push({ command: currentCommand, values });
|
|
4368
|
+
}
|
|
4369
|
+
index += paramCount;
|
|
4370
|
+
}
|
|
4371
|
+
}
|
|
4372
|
+
return segments;
|
|
4373
|
+
}
|
|
4374
|
+
function reflect(control, around) {
|
|
4375
|
+
return {
|
|
4376
|
+
x: around.x * 2 - control.x,
|
|
4377
|
+
y: around.y * 2 - control.y,
|
|
4378
|
+
};
|
|
4379
|
+
}
|
|
4380
|
+
function toAbsoluteSegments(rawSegments) {
|
|
4381
|
+
const segments = [];
|
|
4382
|
+
let current = { x: 0, y: 0 };
|
|
4383
|
+
let subpathStart = { x: 0, y: 0 };
|
|
4384
|
+
let previousCubicControl = null;
|
|
4385
|
+
let previousQuadraticControl = null;
|
|
4386
|
+
for (const segment of rawSegments) {
|
|
4387
|
+
const isRelative = segment.command === segment.command.toLowerCase();
|
|
4388
|
+
const command = segment.command.toUpperCase();
|
|
4389
|
+
const values = segment.values;
|
|
4390
|
+
switch (command) {
|
|
4391
|
+
case "M": {
|
|
4392
|
+
const x = isRelative ? current.x + values[0] : values[0];
|
|
4393
|
+
const y = isRelative ? current.y + values[1] : values[1];
|
|
4394
|
+
current = { x, y };
|
|
4395
|
+
subpathStart = { x, y };
|
|
4396
|
+
previousCubicControl = null;
|
|
4397
|
+
previousQuadraticControl = null;
|
|
4398
|
+
segments.push({ command: "M", values: [x, y] });
|
|
4399
|
+
break;
|
|
4400
|
+
}
|
|
4401
|
+
case "L": {
|
|
4402
|
+
const x = isRelative ? current.x + values[0] : values[0];
|
|
4403
|
+
const y = isRelative ? current.y + values[1] : values[1];
|
|
4404
|
+
current = { x, y };
|
|
4405
|
+
previousCubicControl = null;
|
|
4406
|
+
previousQuadraticControl = null;
|
|
4407
|
+
segments.push({ command: "L", values: [x, y] });
|
|
4408
|
+
break;
|
|
4409
|
+
}
|
|
4410
|
+
case "H": {
|
|
4411
|
+
const x = isRelative ? current.x + values[0] : values[0];
|
|
4412
|
+
current = { x, y: current.y };
|
|
4413
|
+
previousCubicControl = null;
|
|
4414
|
+
previousQuadraticControl = null;
|
|
4415
|
+
segments.push({ command: "L", values: [x, current.y] });
|
|
4416
|
+
break;
|
|
4417
|
+
}
|
|
4418
|
+
case "V": {
|
|
4419
|
+
const y = isRelative ? current.y + values[0] : values[0];
|
|
4420
|
+
current = { x: current.x, y };
|
|
4421
|
+
previousCubicControl = null;
|
|
4422
|
+
previousQuadraticControl = null;
|
|
4423
|
+
segments.push({ command: "L", values: [current.x, y] });
|
|
4424
|
+
break;
|
|
4425
|
+
}
|
|
4426
|
+
case "C": {
|
|
4427
|
+
const x1 = isRelative ? current.x + values[0] : values[0];
|
|
4428
|
+
const y1 = isRelative ? current.y + values[1] : values[1];
|
|
4429
|
+
const x2 = isRelative ? current.x + values[2] : values[2];
|
|
4430
|
+
const y2 = isRelative ? current.y + values[3] : values[3];
|
|
4431
|
+
const x = isRelative ? current.x + values[4] : values[4];
|
|
4432
|
+
const y = isRelative ? current.y + values[5] : values[5];
|
|
4433
|
+
current = { x, y };
|
|
4434
|
+
previousCubicControl = { x: x2, y: y2 };
|
|
4435
|
+
previousQuadraticControl = null;
|
|
4436
|
+
segments.push({ command: "C", values: [x1, y1, x2, y2, x, y] });
|
|
4437
|
+
break;
|
|
4438
|
+
}
|
|
4439
|
+
case "S": {
|
|
4440
|
+
const control1 = previousCubicControl
|
|
4441
|
+
? reflect(previousCubicControl, current)
|
|
4442
|
+
: { ...current };
|
|
4443
|
+
const x2 = isRelative ? current.x + values[0] : values[0];
|
|
4444
|
+
const y2 = isRelative ? current.y + values[1] : values[1];
|
|
4445
|
+
const x = isRelative ? current.x + values[2] : values[2];
|
|
4446
|
+
const y = isRelative ? current.y + values[3] : values[3];
|
|
4447
|
+
current = { x, y };
|
|
4448
|
+
previousCubicControl = { x: x2, y: y2 };
|
|
4449
|
+
previousQuadraticControl = null;
|
|
4450
|
+
segments.push({
|
|
4451
|
+
command: "C",
|
|
4452
|
+
values: [control1.x, control1.y, x2, y2, x, y],
|
|
4453
|
+
});
|
|
4454
|
+
break;
|
|
4455
|
+
}
|
|
4456
|
+
case "Q": {
|
|
4457
|
+
const x1 = isRelative ? current.x + values[0] : values[0];
|
|
4458
|
+
const y1 = isRelative ? current.y + values[1] : values[1];
|
|
4459
|
+
const x = isRelative ? current.x + values[2] : values[2];
|
|
4460
|
+
const y = isRelative ? current.y + values[3] : values[3];
|
|
4461
|
+
current = { x, y };
|
|
4462
|
+
previousCubicControl = null;
|
|
4463
|
+
previousQuadraticControl = { x: x1, y: y1 };
|
|
4464
|
+
segments.push({ command: "Q", values: [x1, y1, x, y] });
|
|
4465
|
+
break;
|
|
4466
|
+
}
|
|
4467
|
+
case "T": {
|
|
4468
|
+
const control = previousQuadraticControl
|
|
4469
|
+
? reflect(previousQuadraticControl, current)
|
|
4470
|
+
: { ...current };
|
|
4471
|
+
const x = isRelative ? current.x + values[0] : values[0];
|
|
4472
|
+
const y = isRelative ? current.y + values[1] : values[1];
|
|
4473
|
+
current = { x, y };
|
|
4474
|
+
previousCubicControl = null;
|
|
4475
|
+
previousQuadraticControl = control;
|
|
4476
|
+
segments.push({ command: "Q", values: [control.x, control.y, x, y] });
|
|
4477
|
+
break;
|
|
4478
|
+
}
|
|
4479
|
+
case "A": {
|
|
4480
|
+
const rx = Math.abs(values[0]);
|
|
4481
|
+
const ry = Math.abs(values[1]);
|
|
4482
|
+
const rotation = values[2];
|
|
4483
|
+
const largeArc = values[3];
|
|
4484
|
+
const sweep = values[4];
|
|
4485
|
+
const x = isRelative ? current.x + values[5] : values[5];
|
|
4486
|
+
const y = isRelative ? current.y + values[6] : values[6];
|
|
4487
|
+
current = { x, y };
|
|
4488
|
+
previousCubicControl = null;
|
|
4489
|
+
previousQuadraticControl = null;
|
|
4490
|
+
segments.push({
|
|
4491
|
+
command: "A",
|
|
4492
|
+
values: [rx, ry, rotation, largeArc, sweep, x, y],
|
|
4493
|
+
});
|
|
4494
|
+
break;
|
|
4495
|
+
}
|
|
4496
|
+
case "Z": {
|
|
4497
|
+
current = { ...subpathStart };
|
|
4498
|
+
previousCubicControl = null;
|
|
4499
|
+
previousQuadraticControl = null;
|
|
4500
|
+
segments.push({ command: "Z", values: [] });
|
|
4501
|
+
break;
|
|
4502
|
+
}
|
|
4503
|
+
}
|
|
4504
|
+
}
|
|
4505
|
+
return segments;
|
|
4506
|
+
}
|
|
4507
|
+
function cubicAt(p0, p1, p2, p3, t) {
|
|
4508
|
+
const mt = 1 - t;
|
|
4509
|
+
return (mt * mt * mt * p0 +
|
|
4510
|
+
3 * mt * mt * t * p1 +
|
|
4511
|
+
3 * mt * t * t * p2 +
|
|
4512
|
+
t * t * t * p3);
|
|
4513
|
+
}
|
|
4514
|
+
function quadraticAt(p0, p1, p2, t) {
|
|
4515
|
+
const mt = 1 - t;
|
|
4516
|
+
return mt * mt * p0 + 2 * mt * t * p1 + t * t * p2;
|
|
4517
|
+
}
|
|
4518
|
+
function cubicExtrema(p0, p1, p2, p3) {
|
|
4519
|
+
const a = -p0 + 3 * p1 - 3 * p2 + p3;
|
|
4520
|
+
const b = 3 * p0 - 6 * p1 + 3 * p2;
|
|
4521
|
+
const c = -3 * p0 + 3 * p1;
|
|
4522
|
+
if (Math.abs(a) < EPSILON) {
|
|
4523
|
+
if (Math.abs(b) < EPSILON)
|
|
4524
|
+
return [];
|
|
4525
|
+
return [-c / (2 * b)].filter((t) => t > 0 && t < 1);
|
|
4526
|
+
}
|
|
4527
|
+
const discriminant = 4 * b * b - 12 * a * c;
|
|
4528
|
+
if (discriminant < 0)
|
|
4529
|
+
return [];
|
|
4530
|
+
const sqrtDiscriminant = Math.sqrt(discriminant);
|
|
4531
|
+
return [
|
|
4532
|
+
(-2 * b + sqrtDiscriminant) / (6 * a),
|
|
4533
|
+
(-2 * b - sqrtDiscriminant) / (6 * a),
|
|
4534
|
+
].filter((t) => t > 0 && t < 1);
|
|
4535
|
+
}
|
|
4536
|
+
function quadraticExtrema(p0, p1, p2) {
|
|
4537
|
+
const denominator = p0 - 2 * p1 + p2;
|
|
4538
|
+
if (Math.abs(denominator) < EPSILON)
|
|
4539
|
+
return [];
|
|
4540
|
+
const t = (p0 - p1) / denominator;
|
|
4541
|
+
return t > 0 && t < 1 ? [t] : [];
|
|
4542
|
+
}
|
|
4543
|
+
function angleBetween(u, v) {
|
|
4544
|
+
const magnitude = Math.hypot(u.x, u.y) * Math.hypot(v.x, v.y);
|
|
4545
|
+
if (magnitude < EPSILON)
|
|
4546
|
+
return 0;
|
|
4547
|
+
const sign = u.x * v.y - u.y * v.x < 0 ? -1 : 1;
|
|
4548
|
+
const cosine = Math.min(1, Math.max(-1, (u.x * v.x + u.y * v.y) / magnitude));
|
|
4549
|
+
return sign * Math.acos(cosine);
|
|
4550
|
+
}
|
|
4551
|
+
function sampleArc(start, values) {
|
|
4552
|
+
let [rx, ry, rotation, largeArcFlag, sweepFlag, endX, endY] = values;
|
|
4553
|
+
if ((Math.abs(start.x - endX) < EPSILON && Math.abs(start.y - endY) < EPSILON) || rx < EPSILON || ry < EPSILON) {
|
|
4554
|
+
return [start, { x: endX, y: endY }];
|
|
4555
|
+
}
|
|
4556
|
+
rx = Math.abs(rx);
|
|
4557
|
+
ry = Math.abs(ry);
|
|
4558
|
+
const phi = (rotation * Math.PI) / 180;
|
|
4559
|
+
const cosPhi = Math.cos(phi);
|
|
4560
|
+
const sinPhi = Math.sin(phi);
|
|
4561
|
+
const dx2 = (start.x - endX) / 2;
|
|
4562
|
+
const dy2 = (start.y - endY) / 2;
|
|
4563
|
+
const x1p = cosPhi * dx2 + sinPhi * dy2;
|
|
4564
|
+
const y1p = -sinPhi * dx2 + cosPhi * dy2;
|
|
4565
|
+
const lambda = (x1p * x1p) / (rx * rx) + (y1p * y1p) / (ry * ry);
|
|
4566
|
+
if (lambda > 1) {
|
|
4567
|
+
const scale = Math.sqrt(lambda);
|
|
4568
|
+
rx *= scale;
|
|
4569
|
+
ry *= scale;
|
|
4570
|
+
}
|
|
4571
|
+
const rx2 = rx * rx;
|
|
4572
|
+
const ry2 = ry * ry;
|
|
4573
|
+
const x1p2 = x1p * x1p;
|
|
4574
|
+
const y1p2 = y1p * y1p;
|
|
4575
|
+
const numerator = rx2 * ry2 - rx2 * y1p2 - ry2 * x1p2;
|
|
4576
|
+
const denominator = rx2 * y1p2 + ry2 * x1p2;
|
|
4577
|
+
const factor = denominator < EPSILON ? 0 : Math.sqrt(Math.max(0, numerator / denominator));
|
|
4578
|
+
const sign = largeArcFlag === sweepFlag ? -1 : 1;
|
|
4579
|
+
const cxp = sign * factor * ((rx * y1p) / ry);
|
|
4580
|
+
const cyp = sign * factor * (-(ry * x1p) / rx);
|
|
4581
|
+
const cx = cosPhi * cxp - sinPhi * cyp + (start.x + endX) / 2;
|
|
4582
|
+
const cy = sinPhi * cxp + cosPhi * cyp + (start.y + endY) / 2;
|
|
4583
|
+
const startVector = {
|
|
4584
|
+
x: (x1p - cxp) / rx,
|
|
4585
|
+
y: (y1p - cyp) / ry,
|
|
4586
|
+
};
|
|
4587
|
+
const endVector = {
|
|
4588
|
+
x: (-x1p - cxp) / rx,
|
|
4589
|
+
y: (-y1p - cyp) / ry,
|
|
4590
|
+
};
|
|
4591
|
+
let deltaTheta = angleBetween(startVector, endVector);
|
|
4592
|
+
if (!sweepFlag && deltaTheta > 0)
|
|
4593
|
+
deltaTheta -= Math.PI * 2;
|
|
4594
|
+
if (sweepFlag && deltaTheta < 0)
|
|
4595
|
+
deltaTheta += Math.PI * 2;
|
|
4596
|
+
const theta1 = angleBetween({ x: 1, y: 0 }, startVector);
|
|
4597
|
+
const steps = Math.max(12, Math.ceil(Math.abs(deltaTheta) / (Math.PI / 8)));
|
|
4598
|
+
const points = [];
|
|
4599
|
+
for (let index = 0; index <= steps; index += 1) {
|
|
4600
|
+
const theta = theta1 + (deltaTheta * index) / steps;
|
|
4601
|
+
const cosTheta = Math.cos(theta);
|
|
4602
|
+
const sinTheta = Math.sin(theta);
|
|
4603
|
+
points.push({
|
|
4604
|
+
x: cx + rx * cosPhi * cosTheta - ry * sinPhi * sinTheta,
|
|
4605
|
+
y: cy + rx * sinPhi * cosTheta + ry * cosPhi * sinTheta,
|
|
4606
|
+
});
|
|
4607
|
+
}
|
|
4608
|
+
return points;
|
|
4609
|
+
}
|
|
4610
|
+
function boundsFromAbsoluteSegments(segments) {
|
|
4611
|
+
if (!segments.length)
|
|
4612
|
+
return null;
|
|
4613
|
+
let minX = Infinity;
|
|
4614
|
+
let minY = Infinity;
|
|
4615
|
+
let maxX = -Infinity;
|
|
4616
|
+
let maxY = -Infinity;
|
|
4617
|
+
const include = (point) => {
|
|
4618
|
+
minX = Math.min(minX, point.x);
|
|
4619
|
+
minY = Math.min(minY, point.y);
|
|
4620
|
+
maxX = Math.max(maxX, point.x);
|
|
4621
|
+
maxY = Math.max(maxY, point.y);
|
|
4622
|
+
};
|
|
4623
|
+
let current = { x: 0, y: 0 };
|
|
4624
|
+
let subpathStart = { x: 0, y: 0 };
|
|
4625
|
+
for (const segment of segments) {
|
|
4626
|
+
switch (segment.command) {
|
|
4627
|
+
case "M": {
|
|
4628
|
+
current = { x: segment.values[0], y: segment.values[1] };
|
|
4629
|
+
subpathStart = { ...current };
|
|
4630
|
+
include(current);
|
|
4631
|
+
break;
|
|
4632
|
+
}
|
|
4633
|
+
case "L": {
|
|
4634
|
+
include(current);
|
|
4635
|
+
current = { x: segment.values[0], y: segment.values[1] };
|
|
4636
|
+
include(current);
|
|
4637
|
+
break;
|
|
4638
|
+
}
|
|
4639
|
+
case "C": {
|
|
4640
|
+
const [x1, y1, x2, y2, x, y] = segment.values;
|
|
4641
|
+
const ts = new Set([0, 1]);
|
|
4642
|
+
cubicExtrema(current.x, x1, x2, x).forEach((value) => ts.add(value));
|
|
4643
|
+
cubicExtrema(current.y, y1, y2, y).forEach((value) => ts.add(value));
|
|
4644
|
+
for (const t of ts) {
|
|
4645
|
+
include({
|
|
4646
|
+
x: cubicAt(current.x, x1, x2, x, t),
|
|
4647
|
+
y: cubicAt(current.y, y1, y2, y, t),
|
|
4648
|
+
});
|
|
4649
|
+
}
|
|
4650
|
+
current = { x, y };
|
|
4651
|
+
break;
|
|
4652
|
+
}
|
|
4653
|
+
case "Q": {
|
|
4654
|
+
const [x1, y1, x, y] = segment.values;
|
|
4655
|
+
const ts = new Set([0, 1]);
|
|
4656
|
+
quadraticExtrema(current.x, x1, x).forEach((value) => ts.add(value));
|
|
4657
|
+
quadraticExtrema(current.y, y1, y).forEach((value) => ts.add(value));
|
|
4658
|
+
for (const t of ts) {
|
|
4659
|
+
include({
|
|
4660
|
+
x: quadraticAt(current.x, x1, x, t),
|
|
4661
|
+
y: quadraticAt(current.y, y1, y, t),
|
|
4662
|
+
});
|
|
4663
|
+
}
|
|
4664
|
+
current = { x, y };
|
|
4665
|
+
break;
|
|
4666
|
+
}
|
|
4667
|
+
case "A": {
|
|
4668
|
+
for (const point of sampleArc(current, segment.values)) {
|
|
4669
|
+
include(point);
|
|
4670
|
+
}
|
|
4671
|
+
current = { x: segment.values[5], y: segment.values[6] };
|
|
4672
|
+
break;
|
|
4673
|
+
}
|
|
4674
|
+
case "Z": {
|
|
4675
|
+
include(current);
|
|
4676
|
+
include(subpathStart);
|
|
4677
|
+
current = { ...subpathStart };
|
|
4678
|
+
break;
|
|
4679
|
+
}
|
|
4680
|
+
}
|
|
4681
|
+
}
|
|
4682
|
+
if (!Number.isFinite(minX) || !Number.isFinite(minY) || !Number.isFinite(maxX) || !Number.isFinite(maxY)) {
|
|
4683
|
+
return null;
|
|
4684
|
+
}
|
|
4685
|
+
return { minX, minY, maxX, maxY };
|
|
4686
|
+
}
|
|
4687
|
+
function transformX(x, bounds, scaleX) {
|
|
4688
|
+
return (x - bounds.minX) * scaleX;
|
|
4689
|
+
}
|
|
4690
|
+
function transformY(y, bounds, scaleY) {
|
|
4691
|
+
return (y - bounds.minY) * scaleY;
|
|
4692
|
+
}
|
|
4693
|
+
function buildScaledPathData(segments, bounds, width, height) {
|
|
4694
|
+
const sourceWidth = Math.max(bounds.maxX - bounds.minX, EPSILON);
|
|
4695
|
+
const sourceHeight = Math.max(bounds.maxY - bounds.minY, EPSILON);
|
|
4696
|
+
const scaleX = width / sourceWidth;
|
|
4697
|
+
const scaleY = height / sourceHeight;
|
|
4698
|
+
return segments
|
|
4699
|
+
.map((segment) => {
|
|
4700
|
+
switch (segment.command) {
|
|
4701
|
+
case "M":
|
|
4702
|
+
case "L":
|
|
4703
|
+
return [
|
|
4704
|
+
segment.command,
|
|
4705
|
+
formatNumber(transformX(segment.values[0], bounds, scaleX)),
|
|
4706
|
+
formatNumber(transformY(segment.values[1], bounds, scaleY)),
|
|
4707
|
+
].join(" ");
|
|
4708
|
+
case "C":
|
|
4709
|
+
return [
|
|
4710
|
+
"C",
|
|
4711
|
+
formatNumber(transformX(segment.values[0], bounds, scaleX)),
|
|
4712
|
+
formatNumber(transformY(segment.values[1], bounds, scaleY)),
|
|
4713
|
+
formatNumber(transformX(segment.values[2], bounds, scaleX)),
|
|
4714
|
+
formatNumber(transformY(segment.values[3], bounds, scaleY)),
|
|
4715
|
+
formatNumber(transformX(segment.values[4], bounds, scaleX)),
|
|
4716
|
+
formatNumber(transformY(segment.values[5], bounds, scaleY)),
|
|
4717
|
+
].join(" ");
|
|
4718
|
+
case "Q":
|
|
4719
|
+
return [
|
|
4720
|
+
"Q",
|
|
4721
|
+
formatNumber(transformX(segment.values[0], bounds, scaleX)),
|
|
4722
|
+
formatNumber(transformY(segment.values[1], bounds, scaleY)),
|
|
4723
|
+
formatNumber(transformX(segment.values[2], bounds, scaleX)),
|
|
4724
|
+
formatNumber(transformY(segment.values[3], bounds, scaleY)),
|
|
4725
|
+
].join(" ");
|
|
4726
|
+
case "A":
|
|
4727
|
+
return [
|
|
4728
|
+
"A",
|
|
4729
|
+
formatNumber(segment.values[0] * scaleX),
|
|
4730
|
+
formatNumber(segment.values[1] * scaleY),
|
|
4731
|
+
formatNumber(segment.values[2]),
|
|
4732
|
+
formatNumber(segment.values[3]),
|
|
4733
|
+
formatNumber(segment.values[4]),
|
|
4734
|
+
formatNumber(transformX(segment.values[5], bounds, scaleX)),
|
|
4735
|
+
formatNumber(transformY(segment.values[6], bounds, scaleY)),
|
|
4736
|
+
].join(" ");
|
|
4737
|
+
case "Z":
|
|
4738
|
+
return "Z";
|
|
4739
|
+
}
|
|
4740
|
+
})
|
|
4741
|
+
.join(" ");
|
|
4742
|
+
}
|
|
4743
|
+
function intrinsicSizeFromBounds(bounds) {
|
|
4744
|
+
if (!bounds)
|
|
4745
|
+
return { width: 100, height: 100 };
|
|
4746
|
+
return {
|
|
4747
|
+
width: Math.max(1, Math.ceil(bounds.maxX - bounds.minX)),
|
|
4748
|
+
height: Math.max(1, Math.ceil(bounds.maxY - bounds.minY)),
|
|
4749
|
+
};
|
|
4750
|
+
}
|
|
4751
|
+
function parsePathGeometry(pathData) {
|
|
4752
|
+
const segments = toAbsoluteSegments(parseRawSegments(pathData));
|
|
4753
|
+
return {
|
|
4754
|
+
segments,
|
|
4755
|
+
bounds: boundsFromAbsoluteSegments(segments),
|
|
4756
|
+
};
|
|
4757
|
+
}
|
|
4758
|
+
function getPathIntrinsicSize(pathData) {
|
|
4759
|
+
if (!pathData)
|
|
4760
|
+
return { width: 100, height: 100 };
|
|
4761
|
+
return intrinsicSizeFromBounds(parsePathGeometry(pathData).bounds);
|
|
4762
|
+
}
|
|
4763
|
+
function getRenderablePathData(pathData, width, height) {
|
|
4764
|
+
if (!pathData)
|
|
4765
|
+
return null;
|
|
4766
|
+
const { segments, bounds } = parsePathGeometry(pathData);
|
|
4767
|
+
if (!segments.length || !bounds)
|
|
4768
|
+
return pathData;
|
|
4769
|
+
return buildScaledPathData(segments, bounds, Math.max(1, width), Math.max(1, height));
|
|
4770
|
+
}
|
|
4771
|
+
function getRenderableNodePathData(node) {
|
|
4772
|
+
return getRenderablePathData(node.pathData, node.w, node.h);
|
|
4773
|
+
}
|
|
4774
|
+
|
|
4300
4775
|
const pathShape = {
|
|
4301
4776
|
size(n, labelW) {
|
|
4302
|
-
|
|
4303
|
-
const w = n.width ?? Math.max(
|
|
4777
|
+
const intrinsic = getPathIntrinsicSize(n.pathData);
|
|
4778
|
+
const w = n.width ?? Math.max(intrinsic.width, Math.min(300, labelW + 20));
|
|
4304
4779
|
n.w = w;
|
|
4305
4780
|
if (!n.h) {
|
|
4306
|
-
if (!n.width && labelW + 20 > w) {
|
|
4781
|
+
if (!n.width && !n.height && labelW + 20 > w) {
|
|
4307
4782
|
const fontSize = Number(n.style?.fontSize ?? 14);
|
|
4308
4783
|
const lines = Math.ceil(labelW / (w - 20));
|
|
4309
|
-
n.h = Math.max(
|
|
4784
|
+
n.h = Math.max(intrinsic.height, lines * fontSize * 1.5 + 20);
|
|
4310
4785
|
}
|
|
4311
4786
|
else {
|
|
4312
|
-
n.h = n.height ??
|
|
4787
|
+
n.h = n.height ?? intrinsic.height;
|
|
4313
4788
|
}
|
|
4314
4789
|
}
|
|
4315
4790
|
},
|
|
4316
4791
|
renderSVG(rc, n, _palette, opts) {
|
|
4317
|
-
const d = n
|
|
4792
|
+
const d = getRenderableNodePathData(n);
|
|
4318
4793
|
if (!d) {
|
|
4319
|
-
// No path data — render placeholder box
|
|
4320
4794
|
return [rc.rectangle(n.x + 1, n.y + 1, n.w - 2, n.h - 2, opts)];
|
|
4321
4795
|
}
|
|
4322
4796
|
const el = rc.path(d, opts);
|
|
4323
|
-
// Wrap in a group to translate the user's path to the node position
|
|
4324
4797
|
const g = document.createElementNS(SVG_NS, "g");
|
|
4325
4798
|
g.setAttribute("transform", `translate(${n.x},${n.y})`);
|
|
4326
4799
|
g.appendChild(el);
|
|
4327
4800
|
return [g];
|
|
4328
4801
|
},
|
|
4329
4802
|
renderCanvas(rc, ctx, n, _palette, opts) {
|
|
4330
|
-
const d = n
|
|
4803
|
+
const d = getRenderableNodePathData(n);
|
|
4331
4804
|
if (!d) {
|
|
4332
4805
|
rc.rectangle(n.x + 1, n.y + 1, n.w - 2, n.h - 2, opts);
|
|
4333
4806
|
return;
|
|
@@ -7939,7 +8412,7 @@ function renderToSVG(sg, container, options = {}) {
|
|
|
7939
8412
|
ng.dataset.w = String(n.w);
|
|
7940
8413
|
ng.dataset.h = String(n.h);
|
|
7941
8414
|
if (n.pathData)
|
|
7942
|
-
ng.dataset.pathData = n.pathData;
|
|
8415
|
+
ng.dataset.pathData = getRenderableNodePathData(n) ?? n.pathData;
|
|
7943
8416
|
if (n.meta?.animationParent)
|
|
7944
8417
|
ng.dataset.animationParent = n.meta.animationParent;
|
|
7945
8418
|
if (n.style?.opacity != null)
|