@pooder/kit 6.2.0 → 6.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/.test-dist/src/extensions/dieline/DielineTool.js +22 -9
- package/.test-dist/src/extensions/dieline/renderBuilder.js +28 -12
- package/.test-dist/src/extensions/feature/FeatureTool.js +20 -3
- package/.test-dist/src/extensions/featureCoordinates.js +21 -0
- package/.test-dist/src/extensions/featurePlacement.js +46 -0
- package/.test-dist/src/extensions/ruler/RulerTool.js +24 -1
- package/.test-dist/tests/run.js +25 -0
- package/CHANGELOG.md +6 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +357 -197
- package/dist/index.mjs +357 -197
- package/package.json +1 -1
- package/src/extensions/dieline/DielineTool.ts +29 -9
- package/src/extensions/dieline/renderBuilder.ts +39 -13
- package/src/extensions/feature/FeatureTool.ts +23 -3
- package/src/extensions/featureCoordinates.ts +35 -0
- package/src/extensions/featurePlacement.ts +118 -0
- package/src/extensions/ruler/RulerTool.ts +24 -2
- package/tests/run.ts +37 -0
package/dist/index.mjs
CHANGED
|
@@ -4582,6 +4582,259 @@ function readDielineState(configService, fallback) {
|
|
|
4582
4582
|
};
|
|
4583
4583
|
}
|
|
4584
4584
|
|
|
4585
|
+
// src/extensions/constraints.ts
|
|
4586
|
+
var ConstraintRegistry = class {
|
|
4587
|
+
static register(type, handler) {
|
|
4588
|
+
this.handlers.set(type, handler);
|
|
4589
|
+
}
|
|
4590
|
+
static apply(x, y, feature, context, constraints) {
|
|
4591
|
+
const list = constraints || feature.constraints;
|
|
4592
|
+
if (!list || list.length === 0) {
|
|
4593
|
+
return { x, y };
|
|
4594
|
+
}
|
|
4595
|
+
let currentX = x;
|
|
4596
|
+
let currentY = y;
|
|
4597
|
+
for (const constraint of list) {
|
|
4598
|
+
const handler = this.handlers.get(constraint.type);
|
|
4599
|
+
if (handler) {
|
|
4600
|
+
const result = handler(currentX, currentY, feature, context, constraint.params || {});
|
|
4601
|
+
currentX = result.x;
|
|
4602
|
+
currentY = result.y;
|
|
4603
|
+
}
|
|
4604
|
+
}
|
|
4605
|
+
return { x: currentX, y: currentY };
|
|
4606
|
+
}
|
|
4607
|
+
};
|
|
4608
|
+
ConstraintRegistry.handlers = /* @__PURE__ */ new Map();
|
|
4609
|
+
var pathConstraint = (x, y, feature, context, params) => {
|
|
4610
|
+
const { dielineWidth, dielineHeight, geometry } = context;
|
|
4611
|
+
if (!geometry) return { x, y };
|
|
4612
|
+
const minX = geometry.x - geometry.width / 2;
|
|
4613
|
+
const minY = geometry.y - geometry.height / 2;
|
|
4614
|
+
const absX = minX + x * geometry.width;
|
|
4615
|
+
const absY = minY + y * geometry.height;
|
|
4616
|
+
const nearest = getNearestPointOnDieline(
|
|
4617
|
+
{ x: absX, y: absY },
|
|
4618
|
+
geometry
|
|
4619
|
+
);
|
|
4620
|
+
let finalX = nearest.x;
|
|
4621
|
+
let finalY = nearest.y;
|
|
4622
|
+
const hasOffsetParams = params.minOffset !== void 0 || params.maxOffset !== void 0;
|
|
4623
|
+
if (hasOffsetParams && nearest.normal) {
|
|
4624
|
+
const dx = absX - nearest.x;
|
|
4625
|
+
const dy = absY - nearest.y;
|
|
4626
|
+
const nx2 = nearest.normal.x;
|
|
4627
|
+
const ny2 = nearest.normal.y;
|
|
4628
|
+
const dist = dx * nx2 + dy * ny2;
|
|
4629
|
+
const scale = dielineWidth > 0 ? geometry.width / dielineWidth : 1;
|
|
4630
|
+
const rawMin = params.minOffset !== void 0 ? params.minOffset : 0;
|
|
4631
|
+
const rawMax = params.maxOffset !== void 0 ? params.maxOffset : 0;
|
|
4632
|
+
const minOffset = rawMin * scale;
|
|
4633
|
+
const maxOffset = rawMax * scale;
|
|
4634
|
+
const clampedDist = Math.max(minOffset, Math.min(dist, maxOffset));
|
|
4635
|
+
finalX = nearest.x + nx2 * clampedDist;
|
|
4636
|
+
finalY = nearest.y + ny2 * clampedDist;
|
|
4637
|
+
}
|
|
4638
|
+
const nx = geometry.width > 0 ? (finalX - minX) / geometry.width : 0.5;
|
|
4639
|
+
const ny = geometry.height > 0 ? (finalY - minY) / geometry.height : 0.5;
|
|
4640
|
+
return { x: nx, y: ny };
|
|
4641
|
+
};
|
|
4642
|
+
var edgeConstraint = (x, y, feature, context, params) => {
|
|
4643
|
+
const { dielineWidth, dielineHeight } = context;
|
|
4644
|
+
const allowedEdges = params.allowedEdges || [
|
|
4645
|
+
"top",
|
|
4646
|
+
"bottom",
|
|
4647
|
+
"left",
|
|
4648
|
+
"right"
|
|
4649
|
+
];
|
|
4650
|
+
const confine = params.confine || false;
|
|
4651
|
+
const offset = params.offset || 0;
|
|
4652
|
+
const distances = [];
|
|
4653
|
+
if (allowedEdges.includes("top"))
|
|
4654
|
+
distances.push({ edge: "top", dist: y * dielineHeight });
|
|
4655
|
+
if (allowedEdges.includes("bottom"))
|
|
4656
|
+
distances.push({ edge: "bottom", dist: (1 - y) * dielineHeight });
|
|
4657
|
+
if (allowedEdges.includes("left"))
|
|
4658
|
+
distances.push({ edge: "left", dist: x * dielineWidth });
|
|
4659
|
+
if (allowedEdges.includes("right"))
|
|
4660
|
+
distances.push({ edge: "right", dist: (1 - x) * dielineWidth });
|
|
4661
|
+
if (distances.length === 0) return { x, y };
|
|
4662
|
+
distances.sort((a, b) => a.dist - b.dist);
|
|
4663
|
+
const nearest = distances[0].edge;
|
|
4664
|
+
let newX = x;
|
|
4665
|
+
let newY = y;
|
|
4666
|
+
const fw = feature.width || 0;
|
|
4667
|
+
const fh = feature.height || 0;
|
|
4668
|
+
switch (nearest) {
|
|
4669
|
+
case "top":
|
|
4670
|
+
newY = 0 + offset / dielineHeight;
|
|
4671
|
+
if (confine) {
|
|
4672
|
+
const minX = fw / 2 / dielineWidth;
|
|
4673
|
+
const maxX = 1 - minX;
|
|
4674
|
+
newX = Math.max(minX, Math.min(newX, maxX));
|
|
4675
|
+
}
|
|
4676
|
+
break;
|
|
4677
|
+
case "bottom":
|
|
4678
|
+
newY = 1 - offset / dielineHeight;
|
|
4679
|
+
if (confine) {
|
|
4680
|
+
const minX = fw / 2 / dielineWidth;
|
|
4681
|
+
const maxX = 1 - minX;
|
|
4682
|
+
newX = Math.max(minX, Math.min(newX, maxX));
|
|
4683
|
+
}
|
|
4684
|
+
break;
|
|
4685
|
+
case "left":
|
|
4686
|
+
newX = 0 + offset / dielineWidth;
|
|
4687
|
+
if (confine) {
|
|
4688
|
+
const minY = fh / 2 / dielineHeight;
|
|
4689
|
+
const maxY = 1 - minY;
|
|
4690
|
+
newY = Math.max(minY, Math.min(newY, maxY));
|
|
4691
|
+
}
|
|
4692
|
+
break;
|
|
4693
|
+
case "right":
|
|
4694
|
+
newX = 1 - offset / dielineWidth;
|
|
4695
|
+
if (confine) {
|
|
4696
|
+
const minY = fh / 2 / dielineHeight;
|
|
4697
|
+
const maxY = 1 - minY;
|
|
4698
|
+
newY = Math.max(minY, Math.min(newY, maxY));
|
|
4699
|
+
}
|
|
4700
|
+
break;
|
|
4701
|
+
}
|
|
4702
|
+
return { x: newX, y: newY };
|
|
4703
|
+
};
|
|
4704
|
+
var internalConstraint = (x, y, feature, context, params) => {
|
|
4705
|
+
const { dielineWidth, dielineHeight } = context;
|
|
4706
|
+
const margin = params.margin || 0;
|
|
4707
|
+
const fw = feature.width || 0;
|
|
4708
|
+
const fh = feature.height || 0;
|
|
4709
|
+
const minX = (margin + fw / 2) / dielineWidth;
|
|
4710
|
+
const maxX = 1 - (margin + fw / 2) / dielineWidth;
|
|
4711
|
+
const minY = (margin + fh / 2) / dielineHeight;
|
|
4712
|
+
const maxY = 1 - (margin + fh / 2) / dielineHeight;
|
|
4713
|
+
const clampedX = minX > maxX ? 0.5 : Math.max(minX, Math.min(x, maxX));
|
|
4714
|
+
const clampedY = minY > maxY ? 0.5 : Math.max(minY, Math.min(y, maxY));
|
|
4715
|
+
return { x: clampedX, y: clampedY };
|
|
4716
|
+
};
|
|
4717
|
+
var tangentBottomConstraint = (x, y, feature, context, params) => {
|
|
4718
|
+
const { dielineWidth, dielineHeight } = context;
|
|
4719
|
+
const gap = params.gap || 0;
|
|
4720
|
+
const confineX = params.confineX !== false;
|
|
4721
|
+
const extentY = feature.shape === "circle" ? feature.radius || 0 : (feature.height || 0) / 2;
|
|
4722
|
+
const newY = 1 + (extentY + gap) / dielineHeight;
|
|
4723
|
+
let newX = x;
|
|
4724
|
+
if (confineX) {
|
|
4725
|
+
const extentX = feature.shape === "circle" ? feature.radius || 0 : (feature.width || 0) / 2;
|
|
4726
|
+
const minX = extentX / dielineWidth;
|
|
4727
|
+
const maxX = 1 - extentX / dielineWidth;
|
|
4728
|
+
newX = minX > maxX ? 0.5 : Math.max(minX, Math.min(newX, maxX));
|
|
4729
|
+
}
|
|
4730
|
+
return { x: newX, y: newY };
|
|
4731
|
+
};
|
|
4732
|
+
var lowestTangentConstraint = (x, y, feature, context, params) => {
|
|
4733
|
+
const { dielineWidth, dielineHeight, geometry } = context;
|
|
4734
|
+
if (!geometry) return { x, y };
|
|
4735
|
+
const lowest = getLowestPointOnDieline(geometry);
|
|
4736
|
+
const minY = geometry.y - geometry.height / 2;
|
|
4737
|
+
const normY = (lowest.y - minY) / geometry.height;
|
|
4738
|
+
const gap = params.gap || 0;
|
|
4739
|
+
const confineX = params.confineX !== false;
|
|
4740
|
+
const extentY = feature.shape === "circle" ? feature.radius || 0 : (feature.height || 0) / 2;
|
|
4741
|
+
const newY = normY + (extentY + gap) / dielineHeight;
|
|
4742
|
+
let newX = x;
|
|
4743
|
+
if (confineX) {
|
|
4744
|
+
const extentX = feature.shape === "circle" ? feature.radius || 0 : (feature.width || 0) / 2;
|
|
4745
|
+
const minX = extentX / dielineWidth;
|
|
4746
|
+
const maxX = 1 - extentX / dielineWidth;
|
|
4747
|
+
newX = minX > maxX ? 0.5 : Math.max(minX, Math.min(newX, maxX));
|
|
4748
|
+
}
|
|
4749
|
+
return { x: newX, y: newY };
|
|
4750
|
+
};
|
|
4751
|
+
ConstraintRegistry.register("path", pathConstraint);
|
|
4752
|
+
ConstraintRegistry.register("edge", edgeConstraint);
|
|
4753
|
+
ConstraintRegistry.register("internal", internalConstraint);
|
|
4754
|
+
ConstraintRegistry.register("tangent-bottom", tangentBottomConstraint);
|
|
4755
|
+
ConstraintRegistry.register("lowest-tangent", lowestTangentConstraint);
|
|
4756
|
+
|
|
4757
|
+
// src/extensions/featureCoordinates.ts
|
|
4758
|
+
function resolveFeaturePosition2(feature, geometry) {
|
|
4759
|
+
const { x, y, width, height } = geometry;
|
|
4760
|
+
const left = x - width / 2;
|
|
4761
|
+
const top = y - height / 2;
|
|
4762
|
+
return {
|
|
4763
|
+
x: left + feature.x * width,
|
|
4764
|
+
y: top + feature.y * height
|
|
4765
|
+
};
|
|
4766
|
+
}
|
|
4767
|
+
function normalizePointInGeometry(point, geometry) {
|
|
4768
|
+
const left = geometry.x - geometry.width / 2;
|
|
4769
|
+
const top = geometry.y - geometry.height / 2;
|
|
4770
|
+
return {
|
|
4771
|
+
x: geometry.width > 0 ? (point.x - left) / geometry.width : 0.5,
|
|
4772
|
+
y: geometry.height > 0 ? (point.y - top) / geometry.height : 0.5
|
|
4773
|
+
};
|
|
4774
|
+
}
|
|
4775
|
+
|
|
4776
|
+
// src/extensions/featurePlacement.ts
|
|
4777
|
+
function scaleFeatureForRender(feature, scale, x, y) {
|
|
4778
|
+
return {
|
|
4779
|
+
...feature,
|
|
4780
|
+
x,
|
|
4781
|
+
y,
|
|
4782
|
+
width: feature.width !== void 0 ? feature.width * scale : void 0,
|
|
4783
|
+
height: feature.height !== void 0 ? feature.height * scale : void 0,
|
|
4784
|
+
radius: feature.radius !== void 0 ? feature.radius * scale : void 0
|
|
4785
|
+
};
|
|
4786
|
+
}
|
|
4787
|
+
function resolveFeaturePlacements(features, geometry) {
|
|
4788
|
+
const dielineWidth = geometry.scale > 0 ? geometry.width / geometry.scale : geometry.width;
|
|
4789
|
+
const dielineHeight = geometry.scale > 0 ? geometry.height / geometry.scale : geometry.height;
|
|
4790
|
+
return (features || []).map((feature) => {
|
|
4791
|
+
var _a;
|
|
4792
|
+
const activeConstraints = (_a = feature.constraints) == null ? void 0 : _a.filter(
|
|
4793
|
+
(constraint) => !constraint.validateOnly
|
|
4794
|
+
);
|
|
4795
|
+
const constrained = ConstraintRegistry.apply(
|
|
4796
|
+
feature.x,
|
|
4797
|
+
feature.y,
|
|
4798
|
+
feature,
|
|
4799
|
+
{
|
|
4800
|
+
dielineWidth,
|
|
4801
|
+
dielineHeight,
|
|
4802
|
+
geometry
|
|
4803
|
+
},
|
|
4804
|
+
activeConstraints
|
|
4805
|
+
);
|
|
4806
|
+
const center = resolveFeaturePosition2(
|
|
4807
|
+
{
|
|
4808
|
+
...feature,
|
|
4809
|
+
x: constrained.x,
|
|
4810
|
+
y: constrained.y
|
|
4811
|
+
},
|
|
4812
|
+
geometry
|
|
4813
|
+
);
|
|
4814
|
+
return {
|
|
4815
|
+
feature,
|
|
4816
|
+
normalizedX: constrained.x,
|
|
4817
|
+
normalizedY: constrained.y,
|
|
4818
|
+
centerX: center.x,
|
|
4819
|
+
centerY: center.y
|
|
4820
|
+
};
|
|
4821
|
+
});
|
|
4822
|
+
}
|
|
4823
|
+
function projectPlacedFeatures(placements, geometry, scale) {
|
|
4824
|
+
return placements.map((placement) => {
|
|
4825
|
+
const normalized = normalizePointInGeometry(
|
|
4826
|
+
{ x: placement.centerX, y: placement.centerY },
|
|
4827
|
+
geometry
|
|
4828
|
+
);
|
|
4829
|
+
return scaleFeatureForRender(
|
|
4830
|
+
placement.feature,
|
|
4831
|
+
scale,
|
|
4832
|
+
normalized.x,
|
|
4833
|
+
normalized.y
|
|
4834
|
+
);
|
|
4835
|
+
});
|
|
4836
|
+
}
|
|
4837
|
+
|
|
4585
4838
|
// src/extensions/dieline/renderBuilder.ts
|
|
4586
4839
|
var DEFAULT_IDS = {
|
|
4587
4840
|
inside: "dieline.inside",
|
|
@@ -4591,16 +4844,6 @@ var DEFAULT_IDS = {
|
|
|
4591
4844
|
clip: "dieline.clip.image",
|
|
4592
4845
|
clipSource: "dieline.effect.clip-path"
|
|
4593
4846
|
};
|
|
4594
|
-
function scaleFeatures(state, scale) {
|
|
4595
|
-
return (state.features || []).map((feature) => ({
|
|
4596
|
-
...feature,
|
|
4597
|
-
x: feature.x,
|
|
4598
|
-
y: feature.y,
|
|
4599
|
-
width: (feature.width || 0) * scale,
|
|
4600
|
-
height: (feature.height || 0) * scale,
|
|
4601
|
-
radius: (feature.radius || 0) * scale
|
|
4602
|
-
}));
|
|
4603
|
-
}
|
|
4604
4847
|
function buildDielineRenderBundle(options) {
|
|
4605
4848
|
const ids = { ...DEFAULT_IDS, ...options.ids || {} };
|
|
4606
4849
|
const {
|
|
@@ -4625,8 +4868,41 @@ function buildDielineRenderBundle(options) {
|
|
|
4625
4868
|
const cutH = sceneLayout.cutRect.height;
|
|
4626
4869
|
const visualOffset = (cutW - visualWidth) / 2;
|
|
4627
4870
|
const cutR = visualRadius === 0 ? 0 : Math.max(0, visualRadius + visualOffset);
|
|
4628
|
-
const
|
|
4629
|
-
|
|
4871
|
+
const placements = resolveFeaturePlacements(state.features || [], {
|
|
4872
|
+
shape,
|
|
4873
|
+
shapeStyle,
|
|
4874
|
+
pathData: state.pathData,
|
|
4875
|
+
customSourceWidthPx: state.customSourceWidthPx,
|
|
4876
|
+
customSourceHeightPx: state.customSourceHeightPx,
|
|
4877
|
+
canvasWidth,
|
|
4878
|
+
canvasHeight,
|
|
4879
|
+
x: cx,
|
|
4880
|
+
y: cy,
|
|
4881
|
+
width: visualWidth,
|
|
4882
|
+
height: visualHeight,
|
|
4883
|
+
radius: visualRadius,
|
|
4884
|
+
scale
|
|
4885
|
+
});
|
|
4886
|
+
const absoluteFeatures = projectPlacedFeatures(
|
|
4887
|
+
placements,
|
|
4888
|
+
{
|
|
4889
|
+
x: cx,
|
|
4890
|
+
y: cy,
|
|
4891
|
+
width: visualWidth,
|
|
4892
|
+
height: visualHeight
|
|
4893
|
+
},
|
|
4894
|
+
scale
|
|
4895
|
+
);
|
|
4896
|
+
const cutFeatures = projectPlacedFeatures(
|
|
4897
|
+
placements.filter((placement) => !placement.feature.skipCut),
|
|
4898
|
+
{
|
|
4899
|
+
x: cx,
|
|
4900
|
+
y: cy,
|
|
4901
|
+
width: cutW,
|
|
4902
|
+
height: cutH
|
|
4903
|
+
},
|
|
4904
|
+
scale
|
|
4905
|
+
);
|
|
4630
4906
|
const common = {
|
|
4631
4907
|
shape,
|
|
4632
4908
|
shapeStyle,
|
|
@@ -5055,15 +5331,31 @@ var DielineTool = class {
|
|
|
5055
5331
|
const visualRadius = radius * scale;
|
|
5056
5332
|
const visualOffset = (cutW - sceneLayout.trimRect.width) / 2;
|
|
5057
5333
|
const cutR = visualRadius === 0 ? 0 : Math.max(0, visualRadius + visualOffset);
|
|
5058
|
-
const
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5334
|
+
const placements = resolveFeaturePlacements(features || [], {
|
|
5335
|
+
shape,
|
|
5336
|
+
shapeStyle,
|
|
5337
|
+
pathData,
|
|
5338
|
+
customSourceWidthPx: this.state.customSourceWidthPx,
|
|
5339
|
+
customSourceHeightPx: this.state.customSourceHeightPx,
|
|
5340
|
+
canvasWidth: canvasW,
|
|
5341
|
+
canvasHeight: canvasH,
|
|
5342
|
+
x: cx,
|
|
5343
|
+
y: cy,
|
|
5344
|
+
width: sceneLayout.trimRect.width,
|
|
5345
|
+
height: sceneLayout.trimRect.height,
|
|
5346
|
+
radius: visualRadius,
|
|
5347
|
+
scale
|
|
5348
|
+
});
|
|
5349
|
+
const cutFeatures = projectPlacedFeatures(
|
|
5350
|
+
placements.filter((placement) => !placement.feature.skipCut),
|
|
5351
|
+
{
|
|
5352
|
+
x: cx,
|
|
5353
|
+
y: cy,
|
|
5354
|
+
width: cutW,
|
|
5355
|
+
height: cutH
|
|
5356
|
+
},
|
|
5357
|
+
scale
|
|
5358
|
+
);
|
|
5067
5359
|
const generatedPathData = generateDielinePath({
|
|
5068
5360
|
shape,
|
|
5069
5361
|
width: cutW,
|
|
@@ -5189,178 +5481,6 @@ import {
|
|
|
5189
5481
|
} from "@pooder/core";
|
|
5190
5482
|
import { Pattern as Pattern3 } from "fabric";
|
|
5191
5483
|
|
|
5192
|
-
// src/extensions/constraints.ts
|
|
5193
|
-
var ConstraintRegistry = class {
|
|
5194
|
-
static register(type, handler) {
|
|
5195
|
-
this.handlers.set(type, handler);
|
|
5196
|
-
}
|
|
5197
|
-
static apply(x, y, feature, context, constraints) {
|
|
5198
|
-
const list = constraints || feature.constraints;
|
|
5199
|
-
if (!list || list.length === 0) {
|
|
5200
|
-
return { x, y };
|
|
5201
|
-
}
|
|
5202
|
-
let currentX = x;
|
|
5203
|
-
let currentY = y;
|
|
5204
|
-
for (const constraint of list) {
|
|
5205
|
-
const handler = this.handlers.get(constraint.type);
|
|
5206
|
-
if (handler) {
|
|
5207
|
-
const result = handler(currentX, currentY, feature, context, constraint.params || {});
|
|
5208
|
-
currentX = result.x;
|
|
5209
|
-
currentY = result.y;
|
|
5210
|
-
}
|
|
5211
|
-
}
|
|
5212
|
-
return { x: currentX, y: currentY };
|
|
5213
|
-
}
|
|
5214
|
-
};
|
|
5215
|
-
ConstraintRegistry.handlers = /* @__PURE__ */ new Map();
|
|
5216
|
-
var pathConstraint = (x, y, feature, context, params) => {
|
|
5217
|
-
const { dielineWidth, dielineHeight, geometry } = context;
|
|
5218
|
-
if (!geometry) return { x, y };
|
|
5219
|
-
const minX = geometry.x - geometry.width / 2;
|
|
5220
|
-
const minY = geometry.y - geometry.height / 2;
|
|
5221
|
-
const absX = minX + x * geometry.width;
|
|
5222
|
-
const absY = minY + y * geometry.height;
|
|
5223
|
-
const nearest = getNearestPointOnDieline(
|
|
5224
|
-
{ x: absX, y: absY },
|
|
5225
|
-
geometry
|
|
5226
|
-
);
|
|
5227
|
-
let finalX = nearest.x;
|
|
5228
|
-
let finalY = nearest.y;
|
|
5229
|
-
const hasOffsetParams = params.minOffset !== void 0 || params.maxOffset !== void 0;
|
|
5230
|
-
if (hasOffsetParams && nearest.normal) {
|
|
5231
|
-
const dx = absX - nearest.x;
|
|
5232
|
-
const dy = absY - nearest.y;
|
|
5233
|
-
const nx2 = nearest.normal.x;
|
|
5234
|
-
const ny2 = nearest.normal.y;
|
|
5235
|
-
const dist = dx * nx2 + dy * ny2;
|
|
5236
|
-
const scale = dielineWidth > 0 ? geometry.width / dielineWidth : 1;
|
|
5237
|
-
const rawMin = params.minOffset !== void 0 ? params.minOffset : 0;
|
|
5238
|
-
const rawMax = params.maxOffset !== void 0 ? params.maxOffset : 0;
|
|
5239
|
-
const minOffset = rawMin * scale;
|
|
5240
|
-
const maxOffset = rawMax * scale;
|
|
5241
|
-
const clampedDist = Math.max(minOffset, Math.min(dist, maxOffset));
|
|
5242
|
-
finalX = nearest.x + nx2 * clampedDist;
|
|
5243
|
-
finalY = nearest.y + ny2 * clampedDist;
|
|
5244
|
-
}
|
|
5245
|
-
const nx = geometry.width > 0 ? (finalX - minX) / geometry.width : 0.5;
|
|
5246
|
-
const ny = geometry.height > 0 ? (finalY - minY) / geometry.height : 0.5;
|
|
5247
|
-
return { x: nx, y: ny };
|
|
5248
|
-
};
|
|
5249
|
-
var edgeConstraint = (x, y, feature, context, params) => {
|
|
5250
|
-
const { dielineWidth, dielineHeight } = context;
|
|
5251
|
-
const allowedEdges = params.allowedEdges || [
|
|
5252
|
-
"top",
|
|
5253
|
-
"bottom",
|
|
5254
|
-
"left",
|
|
5255
|
-
"right"
|
|
5256
|
-
];
|
|
5257
|
-
const confine = params.confine || false;
|
|
5258
|
-
const offset = params.offset || 0;
|
|
5259
|
-
const distances = [];
|
|
5260
|
-
if (allowedEdges.includes("top"))
|
|
5261
|
-
distances.push({ edge: "top", dist: y * dielineHeight });
|
|
5262
|
-
if (allowedEdges.includes("bottom"))
|
|
5263
|
-
distances.push({ edge: "bottom", dist: (1 - y) * dielineHeight });
|
|
5264
|
-
if (allowedEdges.includes("left"))
|
|
5265
|
-
distances.push({ edge: "left", dist: x * dielineWidth });
|
|
5266
|
-
if (allowedEdges.includes("right"))
|
|
5267
|
-
distances.push({ edge: "right", dist: (1 - x) * dielineWidth });
|
|
5268
|
-
if (distances.length === 0) return { x, y };
|
|
5269
|
-
distances.sort((a, b) => a.dist - b.dist);
|
|
5270
|
-
const nearest = distances[0].edge;
|
|
5271
|
-
let newX = x;
|
|
5272
|
-
let newY = y;
|
|
5273
|
-
const fw = feature.width || 0;
|
|
5274
|
-
const fh = feature.height || 0;
|
|
5275
|
-
switch (nearest) {
|
|
5276
|
-
case "top":
|
|
5277
|
-
newY = 0 + offset / dielineHeight;
|
|
5278
|
-
if (confine) {
|
|
5279
|
-
const minX = fw / 2 / dielineWidth;
|
|
5280
|
-
const maxX = 1 - minX;
|
|
5281
|
-
newX = Math.max(minX, Math.min(newX, maxX));
|
|
5282
|
-
}
|
|
5283
|
-
break;
|
|
5284
|
-
case "bottom":
|
|
5285
|
-
newY = 1 - offset / dielineHeight;
|
|
5286
|
-
if (confine) {
|
|
5287
|
-
const minX = fw / 2 / dielineWidth;
|
|
5288
|
-
const maxX = 1 - minX;
|
|
5289
|
-
newX = Math.max(minX, Math.min(newX, maxX));
|
|
5290
|
-
}
|
|
5291
|
-
break;
|
|
5292
|
-
case "left":
|
|
5293
|
-
newX = 0 + offset / dielineWidth;
|
|
5294
|
-
if (confine) {
|
|
5295
|
-
const minY = fh / 2 / dielineHeight;
|
|
5296
|
-
const maxY = 1 - minY;
|
|
5297
|
-
newY = Math.max(minY, Math.min(newY, maxY));
|
|
5298
|
-
}
|
|
5299
|
-
break;
|
|
5300
|
-
case "right":
|
|
5301
|
-
newX = 1 - offset / dielineWidth;
|
|
5302
|
-
if (confine) {
|
|
5303
|
-
const minY = fh / 2 / dielineHeight;
|
|
5304
|
-
const maxY = 1 - minY;
|
|
5305
|
-
newY = Math.max(minY, Math.min(newY, maxY));
|
|
5306
|
-
}
|
|
5307
|
-
break;
|
|
5308
|
-
}
|
|
5309
|
-
return { x: newX, y: newY };
|
|
5310
|
-
};
|
|
5311
|
-
var internalConstraint = (x, y, feature, context, params) => {
|
|
5312
|
-
const { dielineWidth, dielineHeight } = context;
|
|
5313
|
-
const margin = params.margin || 0;
|
|
5314
|
-
const fw = feature.width || 0;
|
|
5315
|
-
const fh = feature.height || 0;
|
|
5316
|
-
const minX = (margin + fw / 2) / dielineWidth;
|
|
5317
|
-
const maxX = 1 - (margin + fw / 2) / dielineWidth;
|
|
5318
|
-
const minY = (margin + fh / 2) / dielineHeight;
|
|
5319
|
-
const maxY = 1 - (margin + fh / 2) / dielineHeight;
|
|
5320
|
-
const clampedX = minX > maxX ? 0.5 : Math.max(minX, Math.min(x, maxX));
|
|
5321
|
-
const clampedY = minY > maxY ? 0.5 : Math.max(minY, Math.min(y, maxY));
|
|
5322
|
-
return { x: clampedX, y: clampedY };
|
|
5323
|
-
};
|
|
5324
|
-
var tangentBottomConstraint = (x, y, feature, context, params) => {
|
|
5325
|
-
const { dielineWidth, dielineHeight } = context;
|
|
5326
|
-
const gap = params.gap || 0;
|
|
5327
|
-
const confineX = params.confineX !== false;
|
|
5328
|
-
const extentY = feature.shape === "circle" ? feature.radius || 0 : (feature.height || 0) / 2;
|
|
5329
|
-
const newY = 1 + (extentY + gap) / dielineHeight;
|
|
5330
|
-
let newX = x;
|
|
5331
|
-
if (confineX) {
|
|
5332
|
-
const extentX = feature.shape === "circle" ? feature.radius || 0 : (feature.width || 0) / 2;
|
|
5333
|
-
const minX = extentX / dielineWidth;
|
|
5334
|
-
const maxX = 1 - extentX / dielineWidth;
|
|
5335
|
-
newX = minX > maxX ? 0.5 : Math.max(minX, Math.min(newX, maxX));
|
|
5336
|
-
}
|
|
5337
|
-
return { x: newX, y: newY };
|
|
5338
|
-
};
|
|
5339
|
-
var lowestTangentConstraint = (x, y, feature, context, params) => {
|
|
5340
|
-
const { dielineWidth, dielineHeight, geometry } = context;
|
|
5341
|
-
if (!geometry) return { x, y };
|
|
5342
|
-
const lowest = getLowestPointOnDieline(geometry);
|
|
5343
|
-
const minY = geometry.y - geometry.height / 2;
|
|
5344
|
-
const normY = (lowest.y - minY) / geometry.height;
|
|
5345
|
-
const gap = params.gap || 0;
|
|
5346
|
-
const confineX = params.confineX !== false;
|
|
5347
|
-
const extentY = feature.shape === "circle" ? feature.radius || 0 : (feature.height || 0) / 2;
|
|
5348
|
-
const newY = normY + (extentY + gap) / dielineHeight;
|
|
5349
|
-
let newX = x;
|
|
5350
|
-
if (confineX) {
|
|
5351
|
-
const extentX = feature.shape === "circle" ? feature.radius || 0 : (feature.width || 0) / 2;
|
|
5352
|
-
const minX = extentX / dielineWidth;
|
|
5353
|
-
const maxX = 1 - extentX / dielineWidth;
|
|
5354
|
-
newX = minX > maxX ? 0.5 : Math.max(minX, Math.min(newX, maxX));
|
|
5355
|
-
}
|
|
5356
|
-
return { x: newX, y: newY };
|
|
5357
|
-
};
|
|
5358
|
-
ConstraintRegistry.register("path", pathConstraint);
|
|
5359
|
-
ConstraintRegistry.register("edge", edgeConstraint);
|
|
5360
|
-
ConstraintRegistry.register("internal", internalConstraint);
|
|
5361
|
-
ConstraintRegistry.register("tangent-bottom", tangentBottomConstraint);
|
|
5362
|
-
ConstraintRegistry.register("lowest-tangent", lowestTangentConstraint);
|
|
5363
|
-
|
|
5364
5484
|
// src/extensions/featureComplete.ts
|
|
5365
5485
|
function validateFeaturesStrict(features, context) {
|
|
5366
5486
|
const eps = 1e-6;
|
|
@@ -6136,9 +6256,29 @@ var FeatureTool = class {
|
|
|
6136
6256
|
}
|
|
6137
6257
|
const groups = /* @__PURE__ */ new Map();
|
|
6138
6258
|
const singles = [];
|
|
6139
|
-
|
|
6259
|
+
const placements = resolveFeaturePlacements(
|
|
6260
|
+
this.workingFeatures,
|
|
6261
|
+
{
|
|
6262
|
+
shape: this.currentGeometry.shape,
|
|
6263
|
+
shapeStyle: this.currentGeometry.shapeStyle,
|
|
6264
|
+
pathData: this.currentGeometry.pathData,
|
|
6265
|
+
customSourceWidthPx: this.currentGeometry.customSourceWidthPx,
|
|
6266
|
+
customSourceHeightPx: this.currentGeometry.customSourceHeightPx,
|
|
6267
|
+
x: this.currentGeometry.x,
|
|
6268
|
+
y: this.currentGeometry.y,
|
|
6269
|
+
width: this.currentGeometry.width,
|
|
6270
|
+
height: this.currentGeometry.height,
|
|
6271
|
+
radius: this.currentGeometry.radius,
|
|
6272
|
+
scale: this.currentGeometry.scale || 1
|
|
6273
|
+
}
|
|
6274
|
+
);
|
|
6275
|
+
placements.forEach((placement, index) => {
|
|
6276
|
+
const feature = placement.feature;
|
|
6140
6277
|
const geometry = this.getGeometryForFeature(this.currentGeometry, feature);
|
|
6141
|
-
const position =
|
|
6278
|
+
const position = {
|
|
6279
|
+
x: placement.centerX,
|
|
6280
|
+
y: placement.centerY
|
|
6281
|
+
};
|
|
6142
6282
|
const scale = geometry.scale || 1;
|
|
6143
6283
|
const marker = {
|
|
6144
6284
|
feature,
|
|
@@ -6715,11 +6855,12 @@ var EXTENSION_LINE_LENGTH = 5;
|
|
|
6715
6855
|
var MIN_ARROW_SIZE = 4;
|
|
6716
6856
|
var THICKNESS_TO_STROKE_WIDTH_RATIO = 20;
|
|
6717
6857
|
var DEFAULT_THICKNESS = 20;
|
|
6718
|
-
var DEFAULT_GAP =
|
|
6858
|
+
var DEFAULT_GAP = 65;
|
|
6719
6859
|
var DEFAULT_FONT_SIZE = 10;
|
|
6720
6860
|
var DEFAULT_BACKGROUND_COLOR = "#f0f0f0";
|
|
6721
6861
|
var DEFAULT_TEXT_COLOR = "#333333";
|
|
6722
6862
|
var DEFAULT_LINE_COLOR = "#999999";
|
|
6863
|
+
var RULER_DEBUG_KEY = "ruler.debug";
|
|
6723
6864
|
var RULER_THICKNESS_MIN = 10;
|
|
6724
6865
|
var RULER_THICKNESS_MAX = 100;
|
|
6725
6866
|
var RULER_GAP_MIN = 0;
|
|
@@ -6738,6 +6879,7 @@ var RulerTool = class {
|
|
|
6738
6879
|
this.textColor = DEFAULT_TEXT_COLOR;
|
|
6739
6880
|
this.lineColor = DEFAULT_LINE_COLOR;
|
|
6740
6881
|
this.fontSize = DEFAULT_FONT_SIZE;
|
|
6882
|
+
this.debugEnabled = false;
|
|
6741
6883
|
this.renderSeq = 0;
|
|
6742
6884
|
this.numericProps = /* @__PURE__ */ new Set(["thickness", "gap", "fontSize"]);
|
|
6743
6885
|
this.specs = [];
|
|
@@ -6786,7 +6928,14 @@ var RulerTool = class {
|
|
|
6786
6928
|
this.syncConfig(configService);
|
|
6787
6929
|
configService.onAnyChange((e) => {
|
|
6788
6930
|
let shouldUpdate = false;
|
|
6789
|
-
if (e.key
|
|
6931
|
+
if (e.key === RULER_DEBUG_KEY) {
|
|
6932
|
+
this.debugEnabled = e.value === true;
|
|
6933
|
+
this.log("config:update", {
|
|
6934
|
+
key: e.key,
|
|
6935
|
+
raw: e.value,
|
|
6936
|
+
normalized: this.debugEnabled
|
|
6937
|
+
});
|
|
6938
|
+
} else if (e.key.startsWith("ruler.")) {
|
|
6790
6939
|
const prop = e.key.split(".")[1];
|
|
6791
6940
|
if (prop && prop in this) {
|
|
6792
6941
|
if (this.numericProps.has(prop)) {
|
|
@@ -6873,6 +7022,12 @@ var RulerTool = class {
|
|
|
6873
7022
|
min: RULER_FONT_SIZE_MIN,
|
|
6874
7023
|
max: RULER_FONT_SIZE_MAX,
|
|
6875
7024
|
default: DEFAULT_FONT_SIZE
|
|
7025
|
+
},
|
|
7026
|
+
{
|
|
7027
|
+
id: RULER_DEBUG_KEY,
|
|
7028
|
+
type: "boolean",
|
|
7029
|
+
label: "Ruler Debug Log",
|
|
7030
|
+
default: false
|
|
6876
7031
|
}
|
|
6877
7032
|
],
|
|
6878
7033
|
[ContributionPointIds8.COMMANDS]: [
|
|
@@ -6909,7 +7064,11 @@ var RulerTool = class {
|
|
|
6909
7064
|
]
|
|
6910
7065
|
};
|
|
6911
7066
|
}
|
|
7067
|
+
isDebugEnabled() {
|
|
7068
|
+
return this.debugEnabled;
|
|
7069
|
+
}
|
|
6912
7070
|
log(step, payload) {
|
|
7071
|
+
if (!this.isDebugEnabled()) return;
|
|
6913
7072
|
if (payload) {
|
|
6914
7073
|
console.debug(`[RulerTool] ${step}`, payload);
|
|
6915
7074
|
return;
|
|
@@ -6938,6 +7097,7 @@ var RulerTool = class {
|
|
|
6938
7097
|
configService.get("ruler.fontSize", this.fontSize),
|
|
6939
7098
|
DEFAULT_FONT_SIZE
|
|
6940
7099
|
);
|
|
7100
|
+
this.debugEnabled = configService.get(RULER_DEBUG_KEY, this.debugEnabled) === true;
|
|
6941
7101
|
this.log("config:loaded", {
|
|
6942
7102
|
thickness: this.thickness,
|
|
6943
7103
|
gap: this.gap,
|
package/package.json
CHANGED
|
@@ -26,6 +26,10 @@ import {
|
|
|
26
26
|
readDielineState,
|
|
27
27
|
} from "./model";
|
|
28
28
|
import { buildDielineRenderBundle } from "./renderBuilder";
|
|
29
|
+
import {
|
|
30
|
+
projectPlacedFeatures,
|
|
31
|
+
resolveFeaturePlacements,
|
|
32
|
+
} from "../featurePlacement";
|
|
29
33
|
|
|
30
34
|
export class DielineTool implements Extension {
|
|
31
35
|
id = "pooder.kit.dieline";
|
|
@@ -320,15 +324,31 @@ export class DielineTool implements Extension {
|
|
|
320
324
|
const cutR =
|
|
321
325
|
visualRadius === 0 ? 0 : Math.max(0, visualRadius + visualOffset);
|
|
322
326
|
|
|
323
|
-
const
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
327
|
+
const placements = resolveFeaturePlacements(features || [], {
|
|
328
|
+
shape,
|
|
329
|
+
shapeStyle,
|
|
330
|
+
pathData,
|
|
331
|
+
customSourceWidthPx: this.state.customSourceWidthPx,
|
|
332
|
+
customSourceHeightPx: this.state.customSourceHeightPx,
|
|
333
|
+
canvasWidth: canvasW,
|
|
334
|
+
canvasHeight: canvasH,
|
|
335
|
+
x: cx,
|
|
336
|
+
y: cy,
|
|
337
|
+
width: sceneLayout.trimRect.width,
|
|
338
|
+
height: sceneLayout.trimRect.height,
|
|
339
|
+
radius: visualRadius,
|
|
340
|
+
scale,
|
|
341
|
+
});
|
|
342
|
+
const cutFeatures = projectPlacedFeatures(
|
|
343
|
+
placements.filter((placement) => !placement.feature.skipCut),
|
|
344
|
+
{
|
|
345
|
+
x: cx,
|
|
346
|
+
y: cy,
|
|
347
|
+
width: cutW,
|
|
348
|
+
height: cutH,
|
|
349
|
+
},
|
|
350
|
+
scale,
|
|
351
|
+
);
|
|
332
352
|
|
|
333
353
|
const generatedPathData = generateDielinePath({
|
|
334
354
|
shape,
|