@overtone-art/canvas-editor-core 0.2.3 → 0.2.6
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/index.d.mts +43 -18
- package/dist/index.d.ts +43 -18
- package/dist/index.js +82 -15
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +79 -14
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -95,11 +95,29 @@ interface PatternConfig {
|
|
|
95
95
|
angle: number;
|
|
96
96
|
/** Brick row/column shift, as % of tile size. */
|
|
97
97
|
horizontalOffset: number;
|
|
98
|
+
/**
|
|
99
|
+
* Phase shift of the whole tile grid inside the print area, as % of tile
|
|
100
|
+
* width. The pattern object itself is pinned to the print area (it can't be
|
|
101
|
+
* dragged — that would expose bare edges), so this is how the tiling is
|
|
102
|
+
* nudged into alignment. Clamped to ±100% so coverage is never broken.
|
|
103
|
+
*/
|
|
104
|
+
offsetX: number;
|
|
105
|
+
/** Phase shift of the tile grid, as % of tile height. See {@link offsetX}. */
|
|
106
|
+
offsetY: number;
|
|
98
107
|
/** Added rotation per horizontal step, in degrees. */
|
|
99
108
|
rotationStepH: number;
|
|
100
109
|
/** Added rotation per vertical step, in degrees. */
|
|
101
110
|
rotationStepV: number;
|
|
102
111
|
}
|
|
112
|
+
/** Fabric interaction flags frozen while a layer is rendered as a pattern. */
|
|
113
|
+
interface PatternLocks {
|
|
114
|
+
lockMovementX: boolean;
|
|
115
|
+
lockMovementY: boolean;
|
|
116
|
+
lockScalingX: boolean;
|
|
117
|
+
lockScalingY: boolean;
|
|
118
|
+
lockRotation: boolean;
|
|
119
|
+
hasControls: boolean;
|
|
120
|
+
}
|
|
103
121
|
/** Persisted on a layer's meta while it is rendered as a repeating pattern. */
|
|
104
122
|
interface PatternState {
|
|
105
123
|
config: PatternConfig;
|
|
@@ -113,6 +131,13 @@ interface PatternState {
|
|
|
113
131
|
* serialization / reload / undo.
|
|
114
132
|
*/
|
|
115
133
|
originalClip?: Record<string, unknown> | null;
|
|
134
|
+
/**
|
|
135
|
+
* Interaction flags the object carried before it became a pattern. A pattern
|
|
136
|
+
* fills the whole print area, so while it is on the object is locked in place
|
|
137
|
+
* (a drag/resize would slide the tiled bitmap off the area and leave a bare
|
|
138
|
+
* band); the captured flags are restored when the pattern is turned off.
|
|
139
|
+
*/
|
|
140
|
+
originalLocks?: PatternLocks;
|
|
116
141
|
/** Original fabric transform, restored when the pattern is turned off. */
|
|
117
142
|
original: {
|
|
118
143
|
left: number;
|
|
@@ -338,31 +363,31 @@ declare class PatternManager {
|
|
|
338
363
|
getConfig(layerId: string): PatternConfig | null;
|
|
339
364
|
/** Turn a plain image layer into a pattern, or update an existing one. */
|
|
340
365
|
apply(layerId: string, config: PatternConfig): Promise<void>;
|
|
366
|
+
/**
|
|
367
|
+
* Stretch every restored pattern layer back over the full print area and
|
|
368
|
+
* re-freeze it, without re-rasterising: the baked bitmap keeps whatever size
|
|
369
|
+
* it was saved at, so it is scaled (not re-tiled) to the current canvas. Used
|
|
370
|
+
* after a state restore, where the canvas may be a different display size
|
|
371
|
+
* than when the pattern was baked — and to repair states saved while a
|
|
372
|
+
* pattern could still be dragged out of the print area.
|
|
373
|
+
*/
|
|
374
|
+
repinAll(): void;
|
|
341
375
|
/** Restore the original image and drop the pattern. */
|
|
342
376
|
disable(layerId: string): Promise<void>;
|
|
343
377
|
/** Run `task` after any in-flight work for this layer, regardless of outcome. */
|
|
344
378
|
private enqueue;
|
|
345
379
|
private renderLayer;
|
|
346
380
|
}
|
|
381
|
+
/** Interaction flags to restore when a pattern is turned off. */
|
|
382
|
+
declare function captureLocks(obj: FabricObject): PatternLocks;
|
|
383
|
+
/** Pin a pattern object: no move, scale or rotate while the pattern is on. */
|
|
384
|
+
declare function applyPatternLocks(obj: FabricObject): void;
|
|
347
385
|
/**
|
|
348
|
-
*
|
|
349
|
-
*
|
|
350
|
-
*
|
|
351
|
-
* it fills the whole area. A later config update (scale/angle/spacing slider)
|
|
352
|
-
* preserves the object's current placement — otherwise tweaking a slider would
|
|
353
|
-
* snap a pattern the user had dragged/rotated back to the top-left corner.
|
|
354
|
-
*
|
|
355
|
-
* Exported for unit testing.
|
|
386
|
+
* Restore pre-pattern interaction flags. States saved before locking existed
|
|
387
|
+
* carry no snapshot — fall back to a fully interactive object rather than
|
|
388
|
+
* leaving it frozen forever.
|
|
356
389
|
*/
|
|
357
|
-
declare function
|
|
358
|
-
left?: number;
|
|
359
|
-
top?: number;
|
|
360
|
-
angle?: number;
|
|
361
|
-
}): {
|
|
362
|
-
left: number;
|
|
363
|
-
top: number;
|
|
364
|
-
angle: number;
|
|
365
|
-
};
|
|
390
|
+
declare function restoreLocks(obj: FabricObject, locks: PatternLocks | undefined): void;
|
|
366
391
|
/** Clear the decoded-image cache (e.g. on teardown). Exported for completeness. */
|
|
367
392
|
declare function clearPatternImageCache(): void;
|
|
368
393
|
/**
|
|
@@ -493,4 +518,4 @@ declare function round2(v: number): number;
|
|
|
493
518
|
declare function serializeEditor(editor: CanvasEditor): EditorState;
|
|
494
519
|
declare function deserializeEditor(editor: CanvasEditor, state: EditorState): Promise<void>;
|
|
495
520
|
|
|
496
|
-
export { CanvasEditor, CropController, DEFAULT_PATTERN_CONFIG, type EditorConfig, type EditorEvents, type EditorState, EventEmitter, type ExportFormat, type FileAdapter, HistoryManager, type ImageProvider, type ImageProviderResult, Layer, type LayerData, LayerManager, type LayerMeta, type LayerType, type MockupConfig, type MockupPrintArea, type PatternConfig, PatternManager, type PatternState, type PngExportOptions, type PrintifyPositioning, type SerializedLayer, type ShapePlugin, SnapManager, type TemplateDefinition, type TemplateParameter, type TileMode, type TilePlacement, type Unit, UnitConverter, buildPatternDataURL, clamp, clearPatternImageCache, computeTilePositions, deserializeEditor, drawTiles, exportDataURL, exportPNG, exportSVG, generateId,
|
|
521
|
+
export { CanvasEditor, CropController, DEFAULT_PATTERN_CONFIG, type EditorConfig, type EditorEvents, type EditorState, EventEmitter, type ExportFormat, type FileAdapter, HistoryManager, type ImageProvider, type ImageProviderResult, Layer, type LayerData, LayerManager, type LayerMeta, type LayerType, type MockupConfig, type MockupPrintArea, type PatternConfig, type PatternLocks, PatternManager, type PatternState, type PngExportOptions, type PrintifyPositioning, type SerializedLayer, type ShapePlugin, SnapManager, type TemplateDefinition, type TemplateParameter, type TileMode, type TilePlacement, type Unit, UnitConverter, applyPatternLocks, buildPatternDataURL, captureLocks, clamp, clearPatternImageCache, computeTilePositions, deserializeEditor, drawTiles, exportDataURL, exportPNG, exportSVG, generateId, restoreLocks, round2, serializeEditor };
|
package/dist/index.d.ts
CHANGED
|
@@ -95,11 +95,29 @@ interface PatternConfig {
|
|
|
95
95
|
angle: number;
|
|
96
96
|
/** Brick row/column shift, as % of tile size. */
|
|
97
97
|
horizontalOffset: number;
|
|
98
|
+
/**
|
|
99
|
+
* Phase shift of the whole tile grid inside the print area, as % of tile
|
|
100
|
+
* width. The pattern object itself is pinned to the print area (it can't be
|
|
101
|
+
* dragged — that would expose bare edges), so this is how the tiling is
|
|
102
|
+
* nudged into alignment. Clamped to ±100% so coverage is never broken.
|
|
103
|
+
*/
|
|
104
|
+
offsetX: number;
|
|
105
|
+
/** Phase shift of the tile grid, as % of tile height. See {@link offsetX}. */
|
|
106
|
+
offsetY: number;
|
|
98
107
|
/** Added rotation per horizontal step, in degrees. */
|
|
99
108
|
rotationStepH: number;
|
|
100
109
|
/** Added rotation per vertical step, in degrees. */
|
|
101
110
|
rotationStepV: number;
|
|
102
111
|
}
|
|
112
|
+
/** Fabric interaction flags frozen while a layer is rendered as a pattern. */
|
|
113
|
+
interface PatternLocks {
|
|
114
|
+
lockMovementX: boolean;
|
|
115
|
+
lockMovementY: boolean;
|
|
116
|
+
lockScalingX: boolean;
|
|
117
|
+
lockScalingY: boolean;
|
|
118
|
+
lockRotation: boolean;
|
|
119
|
+
hasControls: boolean;
|
|
120
|
+
}
|
|
103
121
|
/** Persisted on a layer's meta while it is rendered as a repeating pattern. */
|
|
104
122
|
interface PatternState {
|
|
105
123
|
config: PatternConfig;
|
|
@@ -113,6 +131,13 @@ interface PatternState {
|
|
|
113
131
|
* serialization / reload / undo.
|
|
114
132
|
*/
|
|
115
133
|
originalClip?: Record<string, unknown> | null;
|
|
134
|
+
/**
|
|
135
|
+
* Interaction flags the object carried before it became a pattern. A pattern
|
|
136
|
+
* fills the whole print area, so while it is on the object is locked in place
|
|
137
|
+
* (a drag/resize would slide the tiled bitmap off the area and leave a bare
|
|
138
|
+
* band); the captured flags are restored when the pattern is turned off.
|
|
139
|
+
*/
|
|
140
|
+
originalLocks?: PatternLocks;
|
|
116
141
|
/** Original fabric transform, restored when the pattern is turned off. */
|
|
117
142
|
original: {
|
|
118
143
|
left: number;
|
|
@@ -338,31 +363,31 @@ declare class PatternManager {
|
|
|
338
363
|
getConfig(layerId: string): PatternConfig | null;
|
|
339
364
|
/** Turn a plain image layer into a pattern, or update an existing one. */
|
|
340
365
|
apply(layerId: string, config: PatternConfig): Promise<void>;
|
|
366
|
+
/**
|
|
367
|
+
* Stretch every restored pattern layer back over the full print area and
|
|
368
|
+
* re-freeze it, without re-rasterising: the baked bitmap keeps whatever size
|
|
369
|
+
* it was saved at, so it is scaled (not re-tiled) to the current canvas. Used
|
|
370
|
+
* after a state restore, where the canvas may be a different display size
|
|
371
|
+
* than when the pattern was baked — and to repair states saved while a
|
|
372
|
+
* pattern could still be dragged out of the print area.
|
|
373
|
+
*/
|
|
374
|
+
repinAll(): void;
|
|
341
375
|
/** Restore the original image and drop the pattern. */
|
|
342
376
|
disable(layerId: string): Promise<void>;
|
|
343
377
|
/** Run `task` after any in-flight work for this layer, regardless of outcome. */
|
|
344
378
|
private enqueue;
|
|
345
379
|
private renderLayer;
|
|
346
380
|
}
|
|
381
|
+
/** Interaction flags to restore when a pattern is turned off. */
|
|
382
|
+
declare function captureLocks(obj: FabricObject): PatternLocks;
|
|
383
|
+
/** Pin a pattern object: no move, scale or rotate while the pattern is on. */
|
|
384
|
+
declare function applyPatternLocks(obj: FabricObject): void;
|
|
347
385
|
/**
|
|
348
|
-
*
|
|
349
|
-
*
|
|
350
|
-
*
|
|
351
|
-
* it fills the whole area. A later config update (scale/angle/spacing slider)
|
|
352
|
-
* preserves the object's current placement — otherwise tweaking a slider would
|
|
353
|
-
* snap a pattern the user had dragged/rotated back to the top-left corner.
|
|
354
|
-
*
|
|
355
|
-
* Exported for unit testing.
|
|
386
|
+
* Restore pre-pattern interaction flags. States saved before locking existed
|
|
387
|
+
* carry no snapshot — fall back to a fully interactive object rather than
|
|
388
|
+
* leaving it frozen forever.
|
|
356
389
|
*/
|
|
357
|
-
declare function
|
|
358
|
-
left?: number;
|
|
359
|
-
top?: number;
|
|
360
|
-
angle?: number;
|
|
361
|
-
}): {
|
|
362
|
-
left: number;
|
|
363
|
-
top: number;
|
|
364
|
-
angle: number;
|
|
365
|
-
};
|
|
390
|
+
declare function restoreLocks(obj: FabricObject, locks: PatternLocks | undefined): void;
|
|
366
391
|
/** Clear the decoded-image cache (e.g. on teardown). Exported for completeness. */
|
|
367
392
|
declare function clearPatternImageCache(): void;
|
|
368
393
|
/**
|
|
@@ -493,4 +518,4 @@ declare function round2(v: number): number;
|
|
|
493
518
|
declare function serializeEditor(editor: CanvasEditor): EditorState;
|
|
494
519
|
declare function deserializeEditor(editor: CanvasEditor, state: EditorState): Promise<void>;
|
|
495
520
|
|
|
496
|
-
export { CanvasEditor, CropController, DEFAULT_PATTERN_CONFIG, type EditorConfig, type EditorEvents, type EditorState, EventEmitter, type ExportFormat, type FileAdapter, HistoryManager, type ImageProvider, type ImageProviderResult, Layer, type LayerData, LayerManager, type LayerMeta, type LayerType, type MockupConfig, type MockupPrintArea, type PatternConfig, PatternManager, type PatternState, type PngExportOptions, type PrintifyPositioning, type SerializedLayer, type ShapePlugin, SnapManager, type TemplateDefinition, type TemplateParameter, type TileMode, type TilePlacement, type Unit, UnitConverter, buildPatternDataURL, clamp, clearPatternImageCache, computeTilePositions, deserializeEditor, drawTiles, exportDataURL, exportPNG, exportSVG, generateId,
|
|
521
|
+
export { CanvasEditor, CropController, DEFAULT_PATTERN_CONFIG, type EditorConfig, type EditorEvents, type EditorState, EventEmitter, type ExportFormat, type FileAdapter, HistoryManager, type ImageProvider, type ImageProviderResult, Layer, type LayerData, LayerManager, type LayerMeta, type LayerType, type MockupConfig, type MockupPrintArea, type PatternConfig, type PatternLocks, PatternManager, type PatternState, type PngExportOptions, type PrintifyPositioning, type SerializedLayer, type ShapePlugin, SnapManager, type TemplateDefinition, type TemplateParameter, type TileMode, type TilePlacement, type Unit, UnitConverter, applyPatternLocks, buildPatternDataURL, captureLocks, clamp, clearPatternImageCache, computeTilePositions, deserializeEditor, drawTiles, exportDataURL, exportPNG, exportSVG, generateId, restoreLocks, round2, serializeEditor };
|
package/dist/index.js
CHANGED
|
@@ -40,7 +40,9 @@ __export(index_exports, {
|
|
|
40
40
|
PatternManager: () => PatternManager,
|
|
41
41
|
SnapManager: () => SnapManager,
|
|
42
42
|
UnitConverter: () => UnitConverter,
|
|
43
|
+
applyPatternLocks: () => applyPatternLocks,
|
|
43
44
|
buildPatternDataURL: () => buildPatternDataURL,
|
|
45
|
+
captureLocks: () => captureLocks,
|
|
44
46
|
clamp: () => clamp,
|
|
45
47
|
clearPatternImageCache: () => clearPatternImageCache,
|
|
46
48
|
computeTilePositions: () => computeTilePositions,
|
|
@@ -50,7 +52,7 @@ __export(index_exports, {
|
|
|
50
52
|
exportPNG: () => exportPNG,
|
|
51
53
|
exportSVG: () => exportSVG,
|
|
52
54
|
generateId: () => generateId,
|
|
53
|
-
|
|
55
|
+
restoreLocks: () => restoreLocks,
|
|
54
56
|
round2: () => round2,
|
|
55
57
|
serializeEditor: () => serializeEditor
|
|
56
58
|
});
|
|
@@ -634,6 +636,7 @@ var PatternManager = class {
|
|
|
634
636
|
config,
|
|
635
637
|
originalSrc: elementToDataURL(image) ?? image.getSrc(),
|
|
636
638
|
originalClip: clip ? clip.toObject() : null,
|
|
639
|
+
originalLocks: captureLocks(image),
|
|
637
640
|
original: {
|
|
638
641
|
left: image.left ?? 0,
|
|
639
642
|
top: image.top ?? 0,
|
|
@@ -650,7 +653,7 @@ var PatternManager = class {
|
|
|
650
653
|
layer.meta.pattern.config = config;
|
|
651
654
|
}
|
|
652
655
|
try {
|
|
653
|
-
await this.renderLayer(layer
|
|
656
|
+
await this.renderLayer(layer);
|
|
654
657
|
} catch (err) {
|
|
655
658
|
if (firstEnable) delete layer.meta.pattern;
|
|
656
659
|
throw err;
|
|
@@ -658,6 +661,34 @@ var PatternManager = class {
|
|
|
658
661
|
this.history.save();
|
|
659
662
|
});
|
|
660
663
|
}
|
|
664
|
+
/**
|
|
665
|
+
* Stretch every restored pattern layer back over the full print area and
|
|
666
|
+
* re-freeze it, without re-rasterising: the baked bitmap keeps whatever size
|
|
667
|
+
* it was saved at, so it is scaled (not re-tiled) to the current canvas. Used
|
|
668
|
+
* after a state restore, where the canvas may be a different display size
|
|
669
|
+
* than when the pattern was baked — and to repair states saved while a
|
|
670
|
+
* pattern could still be dragged out of the print area.
|
|
671
|
+
*/
|
|
672
|
+
repinAll() {
|
|
673
|
+
const cw = this.canvas.getWidth();
|
|
674
|
+
const ch = this.canvas.getHeight();
|
|
675
|
+
let changed = false;
|
|
676
|
+
for (const layer of this.layers.getAll()) {
|
|
677
|
+
if (!layer.meta.pattern || layer.type !== "image") continue;
|
|
678
|
+
const image = layer.fabricObject;
|
|
679
|
+
image.set({
|
|
680
|
+
left: 0,
|
|
681
|
+
top: 0,
|
|
682
|
+
angle: 0,
|
|
683
|
+
scaleX: cw / (image.width || cw),
|
|
684
|
+
scaleY: ch / (image.height || ch)
|
|
685
|
+
});
|
|
686
|
+
applyPatternLocks(image);
|
|
687
|
+
image.setCoords();
|
|
688
|
+
changed = true;
|
|
689
|
+
}
|
|
690
|
+
if (changed) this.canvas.requestRenderAll();
|
|
691
|
+
}
|
|
661
692
|
/** Restore the original image and drop the pattern. */
|
|
662
693
|
disable(layerId) {
|
|
663
694
|
return this.enqueue(layerId, async () => {
|
|
@@ -678,6 +709,7 @@ var PatternManager = class {
|
|
|
678
709
|
angle: state.original.angle
|
|
679
710
|
});
|
|
680
711
|
image.clipPath = state.originalClip ? (await import_fabric2.util.enlivenObjects([state.originalClip]))[0] : void 0;
|
|
712
|
+
restoreLocks(image, state.originalLocks);
|
|
681
713
|
image.setCoords();
|
|
682
714
|
delete layer.meta.pattern;
|
|
683
715
|
this.canvas.requestRenderAll();
|
|
@@ -694,13 +726,12 @@ var PatternManager = class {
|
|
|
694
726
|
);
|
|
695
727
|
return next;
|
|
696
728
|
}
|
|
697
|
-
async renderLayer(layer
|
|
729
|
+
async renderLayer(layer) {
|
|
698
730
|
const state = layer.meta.pattern;
|
|
699
731
|
if (!state) return;
|
|
700
732
|
const image = layer.fabricObject;
|
|
701
733
|
const cw = this.canvas.getWidth();
|
|
702
734
|
const ch = this.canvas.getHeight();
|
|
703
|
-
const { left, top, angle } = resolvePatternPlacement(pin, image);
|
|
704
735
|
const scale = Math.max(1, state.config.scale ?? 100) / 100;
|
|
705
736
|
const diag = Math.sqrt(cw * cw + ch * ch);
|
|
706
737
|
const minTile = Math.max(2, diag / MAX_TILES_PER_AXIS);
|
|
@@ -716,29 +747,54 @@ var PatternManager = class {
|
|
|
716
747
|
);
|
|
717
748
|
await image.setSrc(dataUrl);
|
|
718
749
|
image.set({
|
|
719
|
-
left,
|
|
720
|
-
top,
|
|
750
|
+
left: 0,
|
|
751
|
+
top: 0,
|
|
721
752
|
scaleX: 1,
|
|
722
753
|
scaleY: 1,
|
|
723
754
|
width: cw,
|
|
724
755
|
height: ch,
|
|
725
756
|
cropX: 0,
|
|
726
757
|
cropY: 0,
|
|
727
|
-
angle
|
|
758
|
+
angle: 0
|
|
728
759
|
});
|
|
729
760
|
image.clipPath = void 0;
|
|
761
|
+
applyPatternLocks(image);
|
|
730
762
|
image.setCoords();
|
|
731
763
|
this.canvas.requestRenderAll();
|
|
732
764
|
}
|
|
733
765
|
};
|
|
734
|
-
function
|
|
735
|
-
if (pin) return { left: 0, top: 0, angle: 0 };
|
|
766
|
+
function captureLocks(obj) {
|
|
736
767
|
return {
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
768
|
+
lockMovementX: obj.lockMovementX ?? false,
|
|
769
|
+
lockMovementY: obj.lockMovementY ?? false,
|
|
770
|
+
lockScalingX: obj.lockScalingX ?? false,
|
|
771
|
+
lockScalingY: obj.lockScalingY ?? false,
|
|
772
|
+
lockRotation: obj.lockRotation ?? false,
|
|
773
|
+
hasControls: obj.hasControls ?? true
|
|
740
774
|
};
|
|
741
775
|
}
|
|
776
|
+
function applyPatternLocks(obj) {
|
|
777
|
+
obj.set({
|
|
778
|
+
lockMovementX: true,
|
|
779
|
+
lockMovementY: true,
|
|
780
|
+
lockScalingX: true,
|
|
781
|
+
lockScalingY: true,
|
|
782
|
+
lockRotation: true,
|
|
783
|
+
hasControls: false
|
|
784
|
+
});
|
|
785
|
+
}
|
|
786
|
+
function restoreLocks(obj, locks) {
|
|
787
|
+
obj.set(
|
|
788
|
+
locks ?? {
|
|
789
|
+
lockMovementX: false,
|
|
790
|
+
lockMovementY: false,
|
|
791
|
+
lockScalingX: false,
|
|
792
|
+
lockScalingY: false,
|
|
793
|
+
lockRotation: false,
|
|
794
|
+
hasControls: true
|
|
795
|
+
}
|
|
796
|
+
);
|
|
797
|
+
}
|
|
742
798
|
function elementToDataURL(image) {
|
|
743
799
|
try {
|
|
744
800
|
const el = image.getElement();
|
|
@@ -807,11 +863,13 @@ function computeTilePositions(config, targetW, targetH, baseW, baseH) {
|
|
|
807
863
|
const rows = Math.ceil(diag / tileH) + 2;
|
|
808
864
|
const halfCols = Math.ceil(cols / 2);
|
|
809
865
|
const halfRows = Math.ceil(rows / 2);
|
|
866
|
+
const shiftX = tileW * (clampOffset(config.offsetX) / 100);
|
|
867
|
+
const shiftY = tileH * (clampOffset(config.offsetY) / 100);
|
|
810
868
|
const placements = [];
|
|
811
869
|
for (let j = -halfRows; j <= halfRows; j++) {
|
|
812
870
|
for (let i = -halfCols; i <= halfCols; i++) {
|
|
813
|
-
let x = i * tileW;
|
|
814
|
-
let y = j * tileH;
|
|
871
|
+
let x = i * tileW + shiftX;
|
|
872
|
+
let y = j * tileH + shiftY;
|
|
815
873
|
if (config.mode === "brick-horizontal" && mod2(j) === 1) {
|
|
816
874
|
x += tileW * (config.horizontalOffset / 100);
|
|
817
875
|
} else if (config.mode === "brick-vertical" && mod2(i) === 1) {
|
|
@@ -836,6 +894,10 @@ function drawTiles(ctx, img, config, targetW, targetH, baseW, baseH) {
|
|
|
836
894
|
}
|
|
837
895
|
ctx.restore();
|
|
838
896
|
}
|
|
897
|
+
function clampOffset(value) {
|
|
898
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return 0;
|
|
899
|
+
return Math.max(-100, Math.min(100, value));
|
|
900
|
+
}
|
|
839
901
|
function mod2(n) {
|
|
840
902
|
return (n % 2 + 2) % 2;
|
|
841
903
|
}
|
|
@@ -1097,6 +1159,7 @@ var CanvasEditor = class {
|
|
|
1097
1159
|
}
|
|
1098
1160
|
async fromJSON(state) {
|
|
1099
1161
|
await deserializeEditor(this, state);
|
|
1162
|
+
this.patterns.repinAll();
|
|
1100
1163
|
}
|
|
1101
1164
|
// ─── Export ──────────────────────────────────────────
|
|
1102
1165
|
async toPNG(options) {
|
|
@@ -1291,6 +1354,8 @@ var DEFAULT_PATTERN_CONFIG = {
|
|
|
1291
1354
|
verticalSpacing: 0,
|
|
1292
1355
|
angle: 0,
|
|
1293
1356
|
horizontalOffset: 0,
|
|
1357
|
+
offsetX: 0,
|
|
1358
|
+
offsetY: 0,
|
|
1294
1359
|
rotationStepH: 0,
|
|
1295
1360
|
rotationStepV: 0
|
|
1296
1361
|
};
|
|
@@ -1306,7 +1371,9 @@ var DEFAULT_PATTERN_CONFIG = {
|
|
|
1306
1371
|
PatternManager,
|
|
1307
1372
|
SnapManager,
|
|
1308
1373
|
UnitConverter,
|
|
1374
|
+
applyPatternLocks,
|
|
1309
1375
|
buildPatternDataURL,
|
|
1376
|
+
captureLocks,
|
|
1310
1377
|
clamp,
|
|
1311
1378
|
clearPatternImageCache,
|
|
1312
1379
|
computeTilePositions,
|
|
@@ -1316,7 +1383,7 @@ var DEFAULT_PATTERN_CONFIG = {
|
|
|
1316
1383
|
exportPNG,
|
|
1317
1384
|
exportSVG,
|
|
1318
1385
|
generateId,
|
|
1319
|
-
|
|
1386
|
+
restoreLocks,
|
|
1320
1387
|
round2,
|
|
1321
1388
|
serializeEditor
|
|
1322
1389
|
});
|