@pooder/kit 4.2.0 → 4.3.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/CHANGELOG.md +12 -0
- package/dist/index.d.mts +12 -5
- package/dist/index.d.ts +12 -5
- package/dist/index.js +249 -77
- package/dist/index.mjs +249 -77
- package/package.json +1 -1
- package/src/CanvasService.ts +96 -96
- package/src/ViewportSystem.ts +92 -92
- package/src/background.ts +230 -230
- package/src/constraints.ts +157 -42
- package/src/coordinate.ts +106 -106
- package/src/dieline.ts +973 -955
- package/src/feature.ts +88 -68
- package/src/featureComplete.ts +3 -2
- package/src/film.ts +194 -194
- package/src/geometry.ts +157 -30
- package/src/image.ts +512 -512
- package/src/index.ts +10 -10
- package/src/mirror.ts +128 -128
- package/src/ruler.ts +508 -508
- package/src/tracer.ts +570 -570
- package/src/units.ts +27 -27
- package/src/white-ink.ts +373 -373
- package/tsconfig.test.json +15 -15
- package/.test-dist/src/CanvasService.js +0 -83
- package/.test-dist/src/ViewportSystem.js +0 -75
- package/.test-dist/src/background.js +0 -203
- package/.test-dist/src/constraints.js +0 -153
- package/.test-dist/src/coordinate.js +0 -74
- package/.test-dist/src/dieline.js +0 -758
- package/.test-dist/src/feature.js +0 -687
- package/.test-dist/src/featureComplete.js +0 -31
- package/.test-dist/src/featureDraft.js +0 -31
- package/.test-dist/src/film.js +0 -167
- package/.test-dist/src/geometry.js +0 -292
- package/.test-dist/src/image.js +0 -421
- package/.test-dist/src/index.js +0 -31
- package/.test-dist/src/mirror.js +0 -104
- package/.test-dist/src/ruler.js +0 -383
- package/.test-dist/src/tracer.js +0 -448
- package/.test-dist/src/units.js +0 -30
- package/.test-dist/src/white-ink.js +0 -310
- package/.test-dist/tests/run.js +0 -60
- package/tests/run.ts +0 -81
package/CHANGELOG.md
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -53,13 +53,12 @@ interface DielineFeature {
|
|
|
53
53
|
height?: number;
|
|
54
54
|
radius?: number;
|
|
55
55
|
rotation?: number;
|
|
56
|
-
|
|
56
|
+
renderBehavior?: "edge" | "surface";
|
|
57
57
|
color?: string;
|
|
58
58
|
strokeDash?: number[];
|
|
59
59
|
skipCut?: boolean;
|
|
60
|
-
|
|
61
|
-
type:
|
|
62
|
-
params?: any;
|
|
60
|
+
bridge?: {
|
|
61
|
+
type: "vertical";
|
|
63
62
|
};
|
|
64
63
|
}
|
|
65
64
|
|
|
@@ -147,6 +146,14 @@ declare class FilmTool implements Extension {
|
|
|
147
146
|
private updateFilm;
|
|
148
147
|
}
|
|
149
148
|
|
|
149
|
+
interface ConstraintFeature extends DielineFeature {
|
|
150
|
+
constraints?: Array<{
|
|
151
|
+
type: string;
|
|
152
|
+
params?: any;
|
|
153
|
+
validateOnly?: boolean;
|
|
154
|
+
}>;
|
|
155
|
+
}
|
|
156
|
+
|
|
150
157
|
declare class FeatureTool implements Extension {
|
|
151
158
|
id: string;
|
|
152
159
|
metadata: {
|
|
@@ -162,7 +169,7 @@ declare class FeatureTool implements Extension {
|
|
|
162
169
|
private handleDielineChange;
|
|
163
170
|
private currentGeometry;
|
|
164
171
|
constructor(options?: Partial<{
|
|
165
|
-
features:
|
|
172
|
+
features: ConstraintFeature[];
|
|
166
173
|
}>);
|
|
167
174
|
activate(context: ExtensionContext): void;
|
|
168
175
|
deactivate(context: ExtensionContext): void;
|
package/dist/index.d.ts
CHANGED
|
@@ -53,13 +53,12 @@ interface DielineFeature {
|
|
|
53
53
|
height?: number;
|
|
54
54
|
radius?: number;
|
|
55
55
|
rotation?: number;
|
|
56
|
-
|
|
56
|
+
renderBehavior?: "edge" | "surface";
|
|
57
57
|
color?: string;
|
|
58
58
|
strokeDash?: number[];
|
|
59
59
|
skipCut?: boolean;
|
|
60
|
-
|
|
61
|
-
type:
|
|
62
|
-
params?: any;
|
|
60
|
+
bridge?: {
|
|
61
|
+
type: "vertical";
|
|
63
62
|
};
|
|
64
63
|
}
|
|
65
64
|
|
|
@@ -147,6 +146,14 @@ declare class FilmTool implements Extension {
|
|
|
147
146
|
private updateFilm;
|
|
148
147
|
}
|
|
149
148
|
|
|
149
|
+
interface ConstraintFeature extends DielineFeature {
|
|
150
|
+
constraints?: Array<{
|
|
151
|
+
type: string;
|
|
152
|
+
params?: any;
|
|
153
|
+
validateOnly?: boolean;
|
|
154
|
+
}>;
|
|
155
|
+
}
|
|
156
|
+
|
|
150
157
|
declare class FeatureTool implements Extension {
|
|
151
158
|
id: string;
|
|
152
159
|
metadata: {
|
|
@@ -162,7 +169,7 @@ declare class FeatureTool implements Extension {
|
|
|
162
169
|
private handleDielineChange;
|
|
163
170
|
private currentGeometry;
|
|
164
171
|
constructor(options?: Partial<{
|
|
165
|
-
features:
|
|
172
|
+
features: ConstraintFeature[];
|
|
166
173
|
}>);
|
|
167
174
|
activate(context: ExtensionContext): void;
|
|
168
175
|
deactivate(context: ExtensionContext): void;
|
package/dist/index.js
CHANGED
|
@@ -823,7 +823,7 @@ function getPerimeterShape(options) {
|
|
|
823
823
|
const { features } = options;
|
|
824
824
|
if (features && features.length > 0) {
|
|
825
825
|
const edgeFeatures = features.filter(
|
|
826
|
-
(f) => !f.
|
|
826
|
+
(f) => !f.renderBehavior || f.renderBehavior === "edge"
|
|
827
827
|
);
|
|
828
828
|
const adds = [];
|
|
829
829
|
const subtracts = [];
|
|
@@ -831,10 +831,74 @@ function getPerimeterShape(options) {
|
|
|
831
831
|
const pos = resolveFeaturePosition(f, options);
|
|
832
832
|
const center = new import_paper2.default.Point(pos.x, pos.y);
|
|
833
833
|
const item = createFeatureItem(f, center);
|
|
834
|
-
if (f.
|
|
835
|
-
|
|
834
|
+
if (f.bridge && f.bridge.type === "vertical") {
|
|
835
|
+
const itemBounds = item.bounds;
|
|
836
|
+
const mainBounds = mainShape.bounds;
|
|
837
|
+
const bridgeTop = mainBounds.top;
|
|
838
|
+
const bridgeBottom = itemBounds.top;
|
|
839
|
+
if (bridgeBottom > bridgeTop) {
|
|
840
|
+
const centerX = itemBounds.center.x;
|
|
841
|
+
const ray = new import_paper2.default.Path.Line({
|
|
842
|
+
from: [centerX, bridgeBottom],
|
|
843
|
+
to: [centerX, bridgeTop - 10],
|
|
844
|
+
// Extend slightly past top to ensure intersection
|
|
845
|
+
insert: false
|
|
846
|
+
});
|
|
847
|
+
const intersections = mainShape.getIntersections(ray);
|
|
848
|
+
let targetY = bridgeTop;
|
|
849
|
+
let found = false;
|
|
850
|
+
if (intersections && intersections.length > 0) {
|
|
851
|
+
const validHits = intersections.filter((i) => i.point.y < bridgeBottom - 0.1);
|
|
852
|
+
if (validHits.length > 0) {
|
|
853
|
+
validHits.sort((a, b) => b.point.y - a.point.y);
|
|
854
|
+
targetY = validHits[0].point.y;
|
|
855
|
+
found = true;
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
ray.remove();
|
|
859
|
+
const overlap = 2;
|
|
860
|
+
const rectBottom = bridgeBottom;
|
|
861
|
+
let rectTop = found ? targetY + overlap : bridgeTop;
|
|
862
|
+
if (!found) {
|
|
863
|
+
if (mainBounds.bottom < bridgeBottom) {
|
|
864
|
+
targetY = mainBounds.bottom;
|
|
865
|
+
rectTop = targetY - overlap;
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
if (rectTop < rectBottom) {
|
|
869
|
+
const bridgeRect = new import_paper2.default.Path.Rectangle({
|
|
870
|
+
from: [itemBounds.left, rectTop],
|
|
871
|
+
to: [itemBounds.right, rectBottom],
|
|
872
|
+
insert: false
|
|
873
|
+
});
|
|
874
|
+
const unitedItem = item.unite(bridgeRect);
|
|
875
|
+
item.remove();
|
|
876
|
+
bridgeRect.remove();
|
|
877
|
+
if (f.operation === "add") {
|
|
878
|
+
adds.push(unitedItem);
|
|
879
|
+
} else {
|
|
880
|
+
subtracts.push(unitedItem);
|
|
881
|
+
}
|
|
882
|
+
} else {
|
|
883
|
+
if (f.operation === "add") {
|
|
884
|
+
adds.push(item);
|
|
885
|
+
} else {
|
|
886
|
+
subtracts.push(item);
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
} else {
|
|
890
|
+
if (f.operation === "add") {
|
|
891
|
+
adds.push(item);
|
|
892
|
+
} else {
|
|
893
|
+
subtracts.push(item);
|
|
894
|
+
}
|
|
895
|
+
}
|
|
836
896
|
} else {
|
|
837
|
-
|
|
897
|
+
if (f.operation === "add") {
|
|
898
|
+
adds.push(item);
|
|
899
|
+
} else {
|
|
900
|
+
subtracts.push(item);
|
|
901
|
+
}
|
|
838
902
|
}
|
|
839
903
|
});
|
|
840
904
|
if (adds.length > 0) {
|
|
@@ -867,10 +931,12 @@ function getPerimeterShape(options) {
|
|
|
867
931
|
return mainShape;
|
|
868
932
|
}
|
|
869
933
|
function applySurfaceFeatures(shape, features, options) {
|
|
870
|
-
const
|
|
871
|
-
|
|
934
|
+
const surfaceFeatures = features.filter(
|
|
935
|
+
(f) => f.renderBehavior === "surface"
|
|
936
|
+
);
|
|
937
|
+
if (surfaceFeatures.length === 0) return shape;
|
|
872
938
|
let result = shape;
|
|
873
|
-
for (const f of
|
|
939
|
+
for (const f of surfaceFeatures) {
|
|
874
940
|
const pos = resolveFeaturePosition(f, options);
|
|
875
941
|
const center = new import_paper2.default.Point(pos.x, pos.y);
|
|
876
942
|
const item = createFeatureItem(f, center);
|
|
@@ -927,9 +993,17 @@ function generateBleedZonePath(originalOptions, offsetOptions, offset) {
|
|
|
927
993
|
ensurePaper(paperWidth, paperHeight);
|
|
928
994
|
import_paper2.default.project.activeLayer.removeChildren();
|
|
929
995
|
const pOriginal = getPerimeterShape(originalOptions);
|
|
930
|
-
const shapeOriginal = applySurfaceFeatures(
|
|
996
|
+
const shapeOriginal = applySurfaceFeatures(
|
|
997
|
+
pOriginal,
|
|
998
|
+
originalOptions.features,
|
|
999
|
+
originalOptions
|
|
1000
|
+
);
|
|
931
1001
|
const pOffset = getPerimeterShape(offsetOptions);
|
|
932
|
-
const shapeOffset = applySurfaceFeatures(
|
|
1002
|
+
const shapeOffset = applySurfaceFeatures(
|
|
1003
|
+
pOffset,
|
|
1004
|
+
offsetOptions.features,
|
|
1005
|
+
offsetOptions
|
|
1006
|
+
);
|
|
933
1007
|
let bleedZone;
|
|
934
1008
|
if (offset > 0) {
|
|
935
1009
|
bleedZone = shapeOffset.subtract(shapeOriginal);
|
|
@@ -942,13 +1016,29 @@ function generateBleedZonePath(originalOptions, offsetOptions, offset) {
|
|
|
942
1016
|
bleedZone.remove();
|
|
943
1017
|
return pathData;
|
|
944
1018
|
}
|
|
1019
|
+
function getLowestPointOnDieline(options) {
|
|
1020
|
+
ensurePaper(options.width * 2, options.height * 2);
|
|
1021
|
+
import_paper2.default.project.activeLayer.removeChildren();
|
|
1022
|
+
const shape = createBaseShape(options);
|
|
1023
|
+
const bounds = shape.bounds;
|
|
1024
|
+
const result = {
|
|
1025
|
+
x: bounds.center.x,
|
|
1026
|
+
y: bounds.bottom
|
|
1027
|
+
};
|
|
1028
|
+
shape.remove();
|
|
1029
|
+
return result;
|
|
1030
|
+
}
|
|
945
1031
|
function getNearestPointOnDieline(point, options) {
|
|
946
1032
|
ensurePaper(options.width * 2, options.height * 2);
|
|
947
1033
|
import_paper2.default.project.activeLayer.removeChildren();
|
|
948
1034
|
const shape = createBaseShape(options);
|
|
949
1035
|
const p = new import_paper2.default.Point(point.x, point.y);
|
|
950
|
-
const
|
|
951
|
-
const result = {
|
|
1036
|
+
const location = shape.getNearestLocation(p);
|
|
1037
|
+
const result = {
|
|
1038
|
+
x: location.point.x,
|
|
1039
|
+
y: location.point.y,
|
|
1040
|
+
normal: location.normal ? { x: location.normal.x, y: location.normal.y } : void 0
|
|
1041
|
+
};
|
|
952
1042
|
shape.remove();
|
|
953
1043
|
return result;
|
|
954
1044
|
}
|
|
@@ -1341,7 +1431,19 @@ var DielineTool = class {
|
|
|
1341
1431
|
title: "Detect Edge from Image",
|
|
1342
1432
|
handler: async (imageUrl, options) => {
|
|
1343
1433
|
try {
|
|
1344
|
-
const
|
|
1434
|
+
const loadImage = (url) => {
|
|
1435
|
+
return new Promise((resolve, reject) => {
|
|
1436
|
+
const img2 = new Image();
|
|
1437
|
+
img2.crossOrigin = "Anonymous";
|
|
1438
|
+
img2.onload = () => resolve(img2);
|
|
1439
|
+
img2.onerror = (e) => reject(e);
|
|
1440
|
+
img2.src = url;
|
|
1441
|
+
});
|
|
1442
|
+
};
|
|
1443
|
+
const [img, pathData] = await Promise.all([
|
|
1444
|
+
loadImage(imageUrl),
|
|
1445
|
+
ImageTracer.trace(imageUrl, options)
|
|
1446
|
+
]);
|
|
1345
1447
|
const bounds = getPathBounds(pathData);
|
|
1346
1448
|
const currentMax = Math.max(s.width, s.height);
|
|
1347
1449
|
const scale = currentMax / Math.max(bounds.width, bounds.height);
|
|
@@ -1350,7 +1452,10 @@ var DielineTool = class {
|
|
|
1350
1452
|
return {
|
|
1351
1453
|
pathData,
|
|
1352
1454
|
width: newWidth,
|
|
1353
|
-
height: newHeight
|
|
1455
|
+
height: newHeight,
|
|
1456
|
+
rawBounds: bounds,
|
|
1457
|
+
imageWidth: img.width,
|
|
1458
|
+
imageHeight: img.height
|
|
1354
1459
|
};
|
|
1355
1460
|
} catch (e) {
|
|
1356
1461
|
console.error("Edge detection failed", e);
|
|
@@ -1898,22 +2003,60 @@ var ConstraintRegistry = class {
|
|
|
1898
2003
|
static register(type, handler) {
|
|
1899
2004
|
this.handlers.set(type, handler);
|
|
1900
2005
|
}
|
|
1901
|
-
static apply(x, y, feature, context) {
|
|
1902
|
-
|
|
2006
|
+
static apply(x, y, feature, context, constraints) {
|
|
2007
|
+
const list = constraints || feature.constraints;
|
|
2008
|
+
if (!list || list.length === 0) {
|
|
1903
2009
|
return { x, y };
|
|
1904
2010
|
}
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
2011
|
+
let currentX = x;
|
|
2012
|
+
let currentY = y;
|
|
2013
|
+
for (const constraint of list) {
|
|
2014
|
+
const handler = this.handlers.get(constraint.type);
|
|
2015
|
+
if (handler) {
|
|
2016
|
+
const result = handler(currentX, currentY, feature, context, constraint.params || {});
|
|
2017
|
+
currentX = result.x;
|
|
2018
|
+
currentY = result.y;
|
|
2019
|
+
}
|
|
1908
2020
|
}
|
|
1909
|
-
return { x, y };
|
|
2021
|
+
return { x: currentX, y: currentY };
|
|
1910
2022
|
}
|
|
1911
2023
|
};
|
|
1912
2024
|
ConstraintRegistry.handlers = /* @__PURE__ */ new Map();
|
|
1913
|
-
var
|
|
1914
|
-
|
|
2025
|
+
var pathConstraint = (x, y, feature, context, params) => {
|
|
2026
|
+
const { dielineWidth, dielineHeight, geometry } = context;
|
|
2027
|
+
if (!geometry) return { x, y };
|
|
2028
|
+
const minX = geometry.x - geometry.width / 2;
|
|
2029
|
+
const minY = geometry.y - geometry.height / 2;
|
|
2030
|
+
const absX = minX + x * geometry.width;
|
|
2031
|
+
const absY = minY + y * geometry.height;
|
|
2032
|
+
const nearest = getNearestPointOnDieline(
|
|
2033
|
+
{ x: absX, y: absY },
|
|
2034
|
+
geometry
|
|
2035
|
+
);
|
|
2036
|
+
let finalX = nearest.x;
|
|
2037
|
+
let finalY = nearest.y;
|
|
2038
|
+
const hasOffsetParams = params.minOffset !== void 0 || params.maxOffset !== void 0;
|
|
2039
|
+
if (hasOffsetParams && nearest.normal) {
|
|
2040
|
+
const dx = absX - nearest.x;
|
|
2041
|
+
const dy = absY - nearest.y;
|
|
2042
|
+
const nx2 = nearest.normal.x;
|
|
2043
|
+
const ny2 = nearest.normal.y;
|
|
2044
|
+
const dist = dx * nx2 + dy * ny2;
|
|
2045
|
+
const scale = dielineWidth > 0 ? geometry.width / dielineWidth : 1;
|
|
2046
|
+
const rawMin = params.minOffset !== void 0 ? params.minOffset : 0;
|
|
2047
|
+
const rawMax = params.maxOffset !== void 0 ? params.maxOffset : 0;
|
|
2048
|
+
const minOffset = rawMin * scale;
|
|
2049
|
+
const maxOffset = rawMax * scale;
|
|
2050
|
+
const clampedDist = Math.max(minOffset, Math.min(dist, maxOffset));
|
|
2051
|
+
finalX = nearest.x + nx2 * clampedDist;
|
|
2052
|
+
finalY = nearest.y + ny2 * clampedDist;
|
|
2053
|
+
}
|
|
2054
|
+
const nx = geometry.width > 0 ? (finalX - minX) / geometry.width : 0.5;
|
|
2055
|
+
const ny = geometry.height > 0 ? (finalY - minY) / geometry.height : 0.5;
|
|
2056
|
+
return { x: nx, y: ny };
|
|
2057
|
+
};
|
|
2058
|
+
var edgeConstraint = (x, y, feature, context, params) => {
|
|
1915
2059
|
const { dielineWidth, dielineHeight } = context;
|
|
1916
|
-
const params = ((_a = feature.constraints) == null ? void 0 : _a.params) || {};
|
|
1917
2060
|
const allowedEdges = params.allowedEdges || [
|
|
1918
2061
|
"top",
|
|
1919
2062
|
"bottom",
|
|
@@ -1974,10 +2117,8 @@ var edgeConstraint = (x, y, feature, context) => {
|
|
|
1974
2117
|
}
|
|
1975
2118
|
return { x: newX, y: newY };
|
|
1976
2119
|
};
|
|
1977
|
-
var internalConstraint = (x, y, feature, context) => {
|
|
1978
|
-
var _a;
|
|
2120
|
+
var internalConstraint = (x, y, feature, context, params) => {
|
|
1979
2121
|
const { dielineWidth, dielineHeight } = context;
|
|
1980
|
-
const params = ((_a = feature.constraints) == null ? void 0 : _a.params) || {};
|
|
1981
2122
|
const margin = params.margin || 0;
|
|
1982
2123
|
const fw = feature.width || 0;
|
|
1983
2124
|
const fh = feature.height || 0;
|
|
@@ -1989,10 +2130,8 @@ var internalConstraint = (x, y, feature, context) => {
|
|
|
1989
2130
|
const clampedY = minY > maxY ? 0.5 : Math.max(minY, Math.min(y, maxY));
|
|
1990
2131
|
return { x: clampedX, y: clampedY };
|
|
1991
2132
|
};
|
|
1992
|
-
var tangentBottomConstraint = (x, y, feature, context) => {
|
|
1993
|
-
var _a;
|
|
2133
|
+
var tangentBottomConstraint = (x, y, feature, context, params) => {
|
|
1994
2134
|
const { dielineWidth, dielineHeight } = context;
|
|
1995
|
-
const params = ((_a = feature.constraints) == null ? void 0 : _a.params) || {};
|
|
1996
2135
|
const gap = params.gap || 0;
|
|
1997
2136
|
const confineX = params.confineX !== false;
|
|
1998
2137
|
const extentY = feature.shape === "circle" ? feature.radius || 0 : (feature.height || 0) / 2;
|
|
@@ -2006,18 +2145,38 @@ var tangentBottomConstraint = (x, y, feature, context) => {
|
|
|
2006
2145
|
}
|
|
2007
2146
|
return { x: newX, y: newY };
|
|
2008
2147
|
};
|
|
2148
|
+
var lowestTangentConstraint = (x, y, feature, context, params) => {
|
|
2149
|
+
const { dielineWidth, dielineHeight, geometry } = context;
|
|
2150
|
+
if (!geometry) return { x, y };
|
|
2151
|
+
const lowest = getLowestPointOnDieline(geometry);
|
|
2152
|
+
const minY = geometry.y - geometry.height / 2;
|
|
2153
|
+
const normY = (lowest.y - minY) / geometry.height;
|
|
2154
|
+
const gap = params.gap || 0;
|
|
2155
|
+
const confineX = params.confineX !== false;
|
|
2156
|
+
const extentY = feature.shape === "circle" ? feature.radius || 0 : (feature.height || 0) / 2;
|
|
2157
|
+
const newY = normY + (extentY + gap) / dielineHeight;
|
|
2158
|
+
let newX = x;
|
|
2159
|
+
if (confineX) {
|
|
2160
|
+
const extentX = feature.shape === "circle" ? feature.radius || 0 : (feature.width || 0) / 2;
|
|
2161
|
+
const minX = extentX / dielineWidth;
|
|
2162
|
+
const maxX = 1 - extentX / dielineWidth;
|
|
2163
|
+
newX = minX > maxX ? 0.5 : Math.max(minX, Math.min(newX, maxX));
|
|
2164
|
+
}
|
|
2165
|
+
return { x: newX, y: newY };
|
|
2166
|
+
};
|
|
2167
|
+
ConstraintRegistry.register("path", pathConstraint);
|
|
2009
2168
|
ConstraintRegistry.register("edge", edgeConstraint);
|
|
2010
2169
|
ConstraintRegistry.register("internal", internalConstraint);
|
|
2011
2170
|
ConstraintRegistry.register("tangent-bottom", tangentBottomConstraint);
|
|
2171
|
+
ConstraintRegistry.register("lowest-tangent", lowestTangentConstraint);
|
|
2012
2172
|
|
|
2013
2173
|
// src/featureComplete.ts
|
|
2014
2174
|
function validateFeaturesStrict(features, context) {
|
|
2015
|
-
var _a;
|
|
2016
2175
|
const eps = 1e-6;
|
|
2017
2176
|
const issues = [];
|
|
2018
2177
|
for (const f of features) {
|
|
2019
|
-
if (!
|
|
2020
|
-
const constrained = ConstraintRegistry.apply(f.x, f.y, f, context);
|
|
2178
|
+
if (!f.constraints || f.constraints.length === 0) continue;
|
|
2179
|
+
const constrained = ConstraintRegistry.apply(f.x, f.y, f, context, f.constraints);
|
|
2021
2180
|
if (Math.abs(constrained.x - f.x) > eps || Math.abs(constrained.y - f.y) > eps) {
|
|
2022
2181
|
issues.push({
|
|
2023
2182
|
featureId: f.id,
|
|
@@ -2283,14 +2442,16 @@ var FeatureTool = class {
|
|
|
2283
2442
|
const newFeature = {
|
|
2284
2443
|
id: Date.now().toString(),
|
|
2285
2444
|
operation: type,
|
|
2286
|
-
placement: "edge",
|
|
2287
2445
|
shape: "rect",
|
|
2288
2446
|
x: 0.5,
|
|
2289
2447
|
y: 0,
|
|
2290
2448
|
// Top edge
|
|
2291
2449
|
width: 10,
|
|
2292
2450
|
height: 10,
|
|
2293
|
-
rotation: 0
|
|
2451
|
+
rotation: 0,
|
|
2452
|
+
renderBehavior: "edge",
|
|
2453
|
+
// Default constraint: path (snap to edge)
|
|
2454
|
+
constraints: [{ type: "path" }]
|
|
2294
2455
|
};
|
|
2295
2456
|
this.setWorkingFeatures([...this.workingFeatures || [], newFeature]);
|
|
2296
2457
|
this.redraw();
|
|
@@ -2306,22 +2467,24 @@ var FeatureTool = class {
|
|
|
2306
2467
|
groupId,
|
|
2307
2468
|
operation: "add",
|
|
2308
2469
|
shape: "circle",
|
|
2309
|
-
placement: "edge",
|
|
2310
2470
|
x: 0.5,
|
|
2311
2471
|
y: 0,
|
|
2312
2472
|
radius: 20,
|
|
2313
|
-
rotation: 0
|
|
2473
|
+
rotation: 0,
|
|
2474
|
+
renderBehavior: "edge",
|
|
2475
|
+
constraints: [{ type: "path" }]
|
|
2314
2476
|
};
|
|
2315
2477
|
const hole = {
|
|
2316
2478
|
id: `${timestamp}-hole`,
|
|
2317
2479
|
groupId,
|
|
2318
2480
|
operation: "subtract",
|
|
2319
2481
|
shape: "circle",
|
|
2320
|
-
placement: "edge",
|
|
2321
2482
|
x: 0.5,
|
|
2322
2483
|
y: 0,
|
|
2323
2484
|
radius: 15,
|
|
2324
|
-
rotation: 0
|
|
2485
|
+
rotation: 0,
|
|
2486
|
+
renderBehavior: "edge",
|
|
2487
|
+
constraints: [{ type: "path" }]
|
|
2325
2488
|
};
|
|
2326
2489
|
this.setWorkingFeatures([...this.workingFeatures || [], lug, hole]);
|
|
2327
2490
|
this.redraw();
|
|
@@ -2460,50 +2623,32 @@ var FeatureTool = class {
|
|
|
2460
2623
|
this.canvasService.requestRenderAll();
|
|
2461
2624
|
}
|
|
2462
2625
|
constrainPosition(p, geometry, limit, feature) {
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
const nx = geometry.width > 0 ? (p.x - minX) / geometry.width : 0.5;
|
|
2467
|
-
const ny = geometry.height > 0 ? (p.y - minY) / geometry.height : 0.5;
|
|
2468
|
-
const scale2 = geometry.scale || 1;
|
|
2469
|
-
const dielineWidth = geometry.width / scale2;
|
|
2470
|
-
const dielineHeight = geometry.height / scale2;
|
|
2471
|
-
const constrained = ConstraintRegistry.apply(nx, ny, feature, {
|
|
2472
|
-
dielineWidth,
|
|
2473
|
-
dielineHeight
|
|
2474
|
-
});
|
|
2475
|
-
return {
|
|
2476
|
-
x: minX + constrained.x * geometry.width,
|
|
2477
|
-
y: minY + constrained.y * geometry.height
|
|
2478
|
-
};
|
|
2479
|
-
}
|
|
2480
|
-
if (feature && feature.placement === "internal") {
|
|
2481
|
-
const minX = geometry.x - geometry.width / 2;
|
|
2482
|
-
const maxX = geometry.x + geometry.width / 2;
|
|
2483
|
-
const minY = geometry.y - geometry.height / 2;
|
|
2484
|
-
const maxY = geometry.y + geometry.height / 2;
|
|
2485
|
-
return {
|
|
2486
|
-
x: Math.max(minX, Math.min(maxX, p.x)),
|
|
2487
|
-
y: Math.max(minY, Math.min(maxY, p.y))
|
|
2488
|
-
};
|
|
2626
|
+
var _a;
|
|
2627
|
+
if (!feature) {
|
|
2628
|
+
return { x: p.x, y: p.y };
|
|
2489
2629
|
}
|
|
2490
|
-
const
|
|
2491
|
-
|
|
2630
|
+
const minX = geometry.x - geometry.width / 2;
|
|
2631
|
+
const minY = geometry.y - geometry.height / 2;
|
|
2632
|
+
const nx = geometry.width > 0 ? (p.x - minX) / geometry.width : 0.5;
|
|
2633
|
+
const ny = geometry.height > 0 ? (p.y - minY) / geometry.height : 0.5;
|
|
2634
|
+
const scale = geometry.scale || 1;
|
|
2635
|
+
const dielineWidth = geometry.width / scale;
|
|
2636
|
+
const dielineHeight = geometry.height / scale;
|
|
2637
|
+
const activeConstraints = (_a = feature.constraints) == null ? void 0 : _a.filter((c) => !c.validateOnly);
|
|
2638
|
+
const constrained = ConstraintRegistry.apply(
|
|
2639
|
+
nx,
|
|
2640
|
+
ny,
|
|
2641
|
+
feature,
|
|
2492
2642
|
{
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2643
|
+
dielineWidth,
|
|
2644
|
+
dielineHeight,
|
|
2645
|
+
geometry
|
|
2646
|
+
},
|
|
2647
|
+
activeConstraints
|
|
2496
2648
|
);
|
|
2497
|
-
const dx = p.x - nearest.x;
|
|
2498
|
-
const dy = p.y - nearest.y;
|
|
2499
|
-
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
2500
|
-
if (dist <= limit) {
|
|
2501
|
-
return { x: p.x, y: p.y };
|
|
2502
|
-
}
|
|
2503
|
-
const scale = limit / dist;
|
|
2504
2649
|
return {
|
|
2505
|
-
x:
|
|
2506
|
-
y:
|
|
2650
|
+
x: minX + constrained.x * geometry.width,
|
|
2651
|
+
y: minY + constrained.y * geometry.height
|
|
2507
2652
|
};
|
|
2508
2653
|
}
|
|
2509
2654
|
syncFeatureFromCanvas(target) {
|
|
@@ -2594,6 +2739,33 @@ var FeatureTool = class {
|
|
|
2594
2739
|
if (feature.rotation) {
|
|
2595
2740
|
shape.rotate(feature.rotation);
|
|
2596
2741
|
}
|
|
2742
|
+
if (feature.bridge && feature.bridge.type === "vertical") {
|
|
2743
|
+
const bridgeIndicator = new import_fabric4.Rect({
|
|
2744
|
+
width: visualWidth,
|
|
2745
|
+
height: 100 * featureScale,
|
|
2746
|
+
// Arbitrary long length to show direction
|
|
2747
|
+
fill: "transparent",
|
|
2748
|
+
stroke: "#888",
|
|
2749
|
+
strokeWidth: 1,
|
|
2750
|
+
strokeDashArray: [2, 2],
|
|
2751
|
+
originX: "center",
|
|
2752
|
+
originY: "bottom",
|
|
2753
|
+
// Anchor at bottom so it extends up
|
|
2754
|
+
left: pos.x,
|
|
2755
|
+
top: pos.y - visualHeight / 2,
|
|
2756
|
+
// Start from top of feature
|
|
2757
|
+
opacity: 0.5,
|
|
2758
|
+
selectable: false,
|
|
2759
|
+
evented: false
|
|
2760
|
+
});
|
|
2761
|
+
const group = new import_fabric4.Group([bridgeIndicator, shape], {
|
|
2762
|
+
originX: "center",
|
|
2763
|
+
originY: "center",
|
|
2764
|
+
left: pos.x,
|
|
2765
|
+
top: pos.y
|
|
2766
|
+
});
|
|
2767
|
+
return group;
|
|
2768
|
+
}
|
|
2597
2769
|
return shape;
|
|
2598
2770
|
};
|
|
2599
2771
|
singles.forEach(({ feature, index }) => {
|