@pooder/kit 6.1.2 → 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.
Files changed (30) hide show
  1. package/.test-dist/src/extensions/background/BackgroundTool.js +177 -5
  2. package/.test-dist/src/extensions/constraintUtils.js +44 -0
  3. package/.test-dist/src/extensions/dieline/DielineTool.js +52 -409
  4. package/.test-dist/src/extensions/dieline/featureResolution.js +29 -0
  5. package/.test-dist/src/extensions/dieline/model.js +83 -0
  6. package/.test-dist/src/extensions/dieline/renderBuilder.js +227 -0
  7. package/.test-dist/src/extensions/feature/FeatureTool.js +156 -45
  8. package/.test-dist/src/extensions/featureCoordinates.js +21 -0
  9. package/.test-dist/src/extensions/featurePlacement.js +46 -0
  10. package/.test-dist/src/extensions/image/ImageTool.js +281 -25
  11. package/.test-dist/src/extensions/ruler/RulerTool.js +24 -1
  12. package/.test-dist/src/shared/constants/layers.js +3 -1
  13. package/.test-dist/tests/run.js +25 -0
  14. package/CHANGELOG.md +12 -0
  15. package/dist/index.d.mts +47 -13
  16. package/dist/index.d.ts +47 -13
  17. package/dist/index.js +1325 -977
  18. package/dist/index.mjs +1311 -966
  19. package/package.json +1 -1
  20. package/src/extensions/background/BackgroundTool.ts +264 -4
  21. package/src/extensions/dieline/DielineTool.ts +67 -548
  22. package/src/extensions/dieline/model.ts +165 -1
  23. package/src/extensions/dieline/renderBuilder.ts +301 -0
  24. package/src/extensions/feature/FeatureTool.ts +190 -47
  25. package/src/extensions/featureCoordinates.ts +35 -0
  26. package/src/extensions/featurePlacement.ts +118 -0
  27. package/src/extensions/image/ImageTool.ts +139 -157
  28. package/src/extensions/ruler/RulerTool.ts +24 -2
  29. package/src/shared/constants/layers.ts +2 -0
  30. package/tests/run.ts +37 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pooder/kit",
3
- "version": "6.1.2",
3
+ "version": "6.2.1",
4
4
  "description": "Standard plugins for Pooder editor",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -29,6 +29,27 @@ interface Rect {
29
29
 
30
30
  export type BackgroundLayerKind = "color" | "image";
31
31
  export type BackgroundFitMode = "cover" | "contain" | "stretch";
32
+ export type BackgroundRegionUnit = "normalized" | "px";
33
+ export type BackgroundRegistrationFrame =
34
+ | "trim"
35
+ | "cut"
36
+ | "bleed"
37
+ | "focus"
38
+ | "viewport";
39
+
40
+ export interface BackgroundRegistrationRegion {
41
+ left: number;
42
+ top: number;
43
+ width: number;
44
+ height: number;
45
+ unit: BackgroundRegionUnit;
46
+ }
47
+
48
+ export interface BackgroundRegistration {
49
+ sourceRegion?: BackgroundRegistrationRegion;
50
+ targetFrame?: BackgroundRegistrationFrame;
51
+ fit?: BackgroundFitMode;
52
+ }
32
53
 
33
54
  export interface BackgroundLayer {
34
55
  id: string;
@@ -41,6 +62,7 @@ export interface BackgroundLayer {
41
62
  exportable: boolean;
42
63
  color?: string;
43
64
  src?: string;
65
+ registration?: BackgroundRegistration;
44
66
  }
45
67
 
46
68
  export interface BackgroundConfig {
@@ -98,6 +120,32 @@ function normalizeFitMode(
98
120
  return fallback;
99
121
  }
100
122
 
123
+ function normalizeRegionUnit(
124
+ value: unknown,
125
+ fallback: BackgroundRegionUnit,
126
+ ): BackgroundRegionUnit {
127
+ if (value === "px" || value === "normalized") {
128
+ return value;
129
+ }
130
+ return fallback;
131
+ }
132
+
133
+ function normalizeRegistrationFrame(
134
+ value: unknown,
135
+ fallback: BackgroundRegistrationFrame,
136
+ ): BackgroundRegistrationFrame {
137
+ if (
138
+ value === "trim" ||
139
+ value === "cut" ||
140
+ value === "bleed" ||
141
+ value === "focus" ||
142
+ value === "viewport"
143
+ ) {
144
+ return value;
145
+ }
146
+ return fallback;
147
+ }
148
+
101
149
  function normalizeAnchor(value: unknown, fallback: string): string {
102
150
  if (typeof value !== "string") return fallback;
103
151
  const trimmed = value.trim();
@@ -110,6 +158,86 @@ function normalizeOrder(value: unknown, fallback: number): number {
110
158
  return numeric;
111
159
  }
112
160
 
161
+ function normalizeRegionValue(value: unknown, fallback: number): number {
162
+ const numeric = Number(value);
163
+ return Number.isFinite(numeric) ? numeric : fallback;
164
+ }
165
+
166
+ function normalizeRegistrationRegion(
167
+ raw: unknown,
168
+ fallback?: BackgroundRegistrationRegion,
169
+ ): BackgroundRegistrationRegion | undefined {
170
+ if (!raw || typeof raw !== "object") {
171
+ return fallback ? { ...fallback } : undefined;
172
+ }
173
+
174
+ const input = raw as Partial<BackgroundRegistrationRegion>;
175
+ const base = fallback || {
176
+ left: 0,
177
+ top: 0,
178
+ width: 1,
179
+ height: 1,
180
+ unit: "normalized" as BackgroundRegionUnit,
181
+ };
182
+
183
+ return {
184
+ left: normalizeRegionValue(input.left, base.left),
185
+ top: normalizeRegionValue(input.top, base.top),
186
+ width: normalizeRegionValue(input.width, base.width),
187
+ height: normalizeRegionValue(input.height, base.height),
188
+ unit: normalizeRegionUnit(input.unit, base.unit),
189
+ };
190
+ }
191
+
192
+ function normalizeRegistration(
193
+ raw: unknown,
194
+ fallback?: BackgroundRegistration,
195
+ ): BackgroundRegistration | undefined {
196
+ if (!raw || typeof raw !== "object") {
197
+ return fallback
198
+ ? {
199
+ sourceRegion: fallback.sourceRegion
200
+ ? { ...fallback.sourceRegion }
201
+ : undefined,
202
+ targetFrame: fallback.targetFrame,
203
+ fit: fallback.fit,
204
+ }
205
+ : undefined;
206
+ }
207
+
208
+ const input = raw as Partial<BackgroundRegistration>;
209
+ const normalized: BackgroundRegistration = {
210
+ sourceRegion: normalizeRegistrationRegion(
211
+ input.sourceRegion,
212
+ fallback?.sourceRegion,
213
+ ),
214
+ targetFrame: normalizeRegistrationFrame(
215
+ input.targetFrame,
216
+ fallback?.targetFrame || "trim",
217
+ ),
218
+ fit: normalizeFitMode(input.fit, fallback?.fit || "stretch"),
219
+ };
220
+
221
+ if (!normalized.sourceRegion) {
222
+ return undefined;
223
+ }
224
+
225
+ return normalized;
226
+ }
227
+
228
+ function cloneRegistration(
229
+ registration?: BackgroundRegistration,
230
+ ): BackgroundRegistration | undefined {
231
+ if (!registration) return undefined;
232
+ return {
233
+ sourceRegion: registration.sourceRegion
234
+ ? { ...registration.sourceRegion }
235
+ : undefined,
236
+ targetFrame: registration.targetFrame,
237
+ fit: registration.fit,
238
+ };
239
+ }
240
+
113
241
  function normalizeLayer(
114
242
  raw: unknown,
115
243
  index: number,
@@ -128,7 +256,10 @@ function normalizeLayer(
128
256
  };
129
257
 
130
258
  if (!raw || typeof raw !== "object") {
131
- return { ...fallbackLayer };
259
+ return {
260
+ ...fallbackLayer,
261
+ registration: cloneRegistration(fallbackLayer.registration),
262
+ };
132
263
  }
133
264
 
134
265
  const input = raw as Partial<BackgroundLayer>;
@@ -167,6 +298,10 @@ function normalizeLayer(
167
298
  ? fallbackLayer.src
168
299
  : ""
169
300
  : undefined,
301
+ registration:
302
+ kind === "image"
303
+ ? normalizeRegistration(input.registration, fallbackLayer.registration)
304
+ : undefined,
170
305
  };
171
306
  }
172
307
 
@@ -207,7 +342,10 @@ function normalizeConfig(raw: unknown): BackgroundConfig {
207
342
  function cloneConfig(config: BackgroundConfig): BackgroundConfig {
208
343
  return {
209
344
  version: config.version,
210
- layers: (config.layers || []).map((layer) => ({ ...layer })),
345
+ layers: (config.layers || []).map((layer) => ({
346
+ ...layer,
347
+ registration: cloneRegistration(layer.registration),
348
+ })),
211
349
  };
212
350
  }
213
351
 
@@ -501,6 +639,44 @@ export class BackgroundTool implements Extension {
501
639
  };
502
640
  }
503
641
 
642
+ private resolveTargetFrameRect(frame: BackgroundRegistrationFrame): Rect | null {
643
+ if (frame === "viewport") {
644
+ return this.getViewportRect();
645
+ }
646
+
647
+ const layout = this.resolveSceneLayout();
648
+ if (!layout) {
649
+ return frame === "focus" ? this.getViewportRect() : null;
650
+ }
651
+
652
+ switch (frame) {
653
+ case "trim":
654
+ case "focus":
655
+ return {
656
+ left: layout.trimRect.left,
657
+ top: layout.trimRect.top,
658
+ width: layout.trimRect.width,
659
+ height: layout.trimRect.height,
660
+ };
661
+ case "cut":
662
+ return {
663
+ left: layout.cutRect.left,
664
+ top: layout.cutRect.top,
665
+ width: layout.cutRect.width,
666
+ height: layout.cutRect.height,
667
+ };
668
+ case "bleed":
669
+ return {
670
+ left: layout.bleedRect.left,
671
+ top: layout.bleedRect.top,
672
+ width: layout.bleedRect.width,
673
+ height: layout.bleedRect.height,
674
+ };
675
+ default:
676
+ return null;
677
+ }
678
+ }
679
+
504
680
  private resolveAnchorRect(anchor: string): Rect {
505
681
  if (anchor === "focus") {
506
682
  return this.resolveFocusRect() || this.getViewportRect();
@@ -548,6 +724,85 @@ export class BackgroundTool implements Extension {
548
724
  };
549
725
  }
550
726
 
727
+ private resolveRegistrationRegion(
728
+ region: BackgroundRegistrationRegion,
729
+ sourceSize: SourceSize,
730
+ ): Rect | null {
731
+ const sourceWidth = Math.max(1, Number(sourceSize.width || 0));
732
+ const sourceHeight = Math.max(1, Number(sourceSize.height || 0));
733
+ const width =
734
+ region.unit === "normalized" ? region.width * sourceWidth : region.width;
735
+ const height =
736
+ region.unit === "normalized"
737
+ ? region.height * sourceHeight
738
+ : region.height;
739
+ const left =
740
+ region.unit === "normalized" ? region.left * sourceWidth : region.left;
741
+ const top =
742
+ region.unit === "normalized" ? region.top * sourceHeight : region.top;
743
+
744
+ if (
745
+ !Number.isFinite(left) ||
746
+ !Number.isFinite(top) ||
747
+ !Number.isFinite(width) ||
748
+ !Number.isFinite(height) ||
749
+ width <= 0 ||
750
+ height <= 0
751
+ ) {
752
+ return null;
753
+ }
754
+
755
+ return { left, top, width, height };
756
+ }
757
+
758
+ private resolveRegistrationPlacement(
759
+ layer: BackgroundLayer,
760
+ sourceSize: SourceSize,
761
+ ): { left: number; top: number; scaleX: number; scaleY: number } | null {
762
+ const registration = layer.registration;
763
+ if (!registration?.sourceRegion) return null;
764
+
765
+ const targetRect = this.resolveTargetFrameRect(
766
+ registration.targetFrame || "trim",
767
+ );
768
+ if (!targetRect) return null;
769
+
770
+ const sourceRegion = this.resolveRegistrationRegion(
771
+ registration.sourceRegion,
772
+ sourceSize,
773
+ );
774
+ if (!sourceRegion) return null;
775
+
776
+ const fit = registration.fit || "stretch";
777
+ const baseScaleX = targetRect.width / sourceRegion.width;
778
+ const baseScaleY = targetRect.height / sourceRegion.height;
779
+
780
+ if (fit === "stretch") {
781
+ return {
782
+ left: targetRect.left - sourceRegion.left * baseScaleX,
783
+ top: targetRect.top - sourceRegion.top * baseScaleY,
784
+ scaleX: baseScaleX,
785
+ scaleY: baseScaleY,
786
+ };
787
+ }
788
+
789
+ const uniformScale =
790
+ fit === "contain"
791
+ ? Math.min(baseScaleX, baseScaleY)
792
+ : Math.max(baseScaleX, baseScaleY);
793
+ const alignedWidth = sourceRegion.width * uniformScale;
794
+ const alignedHeight = sourceRegion.height * uniformScale;
795
+ const offsetLeft = targetRect.left + (targetRect.width - alignedWidth) / 2;
796
+ const offsetTop = targetRect.top + (targetRect.height - alignedHeight) / 2;
797
+
798
+ return {
799
+ left: offsetLeft - sourceRegion.left * uniformScale,
800
+ top: offsetTop - sourceRegion.top * uniformScale,
801
+ scaleX: uniformScale,
802
+ scaleY: uniformScale,
803
+ };
804
+ }
805
+
551
806
  private buildColorLayerSpec(layer: BackgroundLayer): RenderObjectSpec {
552
807
  const rect = this.resolveAnchorRect(layer.anchor);
553
808
 
@@ -585,8 +840,13 @@ export class BackgroundTool implements Extension {
585
840
  const sourceSize = this.sourceSizeCache.getSourceSize(src);
586
841
  if (!sourceSize) return [];
587
842
 
588
- const rect = this.resolveAnchorRect(layer.anchor);
589
- const placement = this.resolveImagePlacement(rect, sourceSize, layer.fit);
843
+ const placement =
844
+ this.resolveRegistrationPlacement(layer, sourceSize) ||
845
+ this.resolveImagePlacement(
846
+ this.resolveAnchorRect(layer.anchor),
847
+ sourceSize,
848
+ layer.fit,
849
+ );
590
850
 
591
851
  return [
592
852
  {