svelte-product-mockup 1.0.10 → 1.0.12
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/components/LayerControls.svelte +5 -6
- package/dist/components/MockupEditor.svelte +14 -17
- package/dist/components/MockupEditorRenderer.svelte +2 -2
- package/dist/components/MockupEditorRenderer.svelte.d.ts +2 -2
- package/dist/components/MockupRenderer.svelte +2 -2
- package/dist/components/MockupRenderer.svelte.d.ts +2 -2
- package/dist/components/sections/SizingSection.svelte +1 -1
- package/dist/components/sections/TransformSection.svelte +5 -2
- package/dist/components/sections/WarpSection.svelte +23 -23
- package/dist/image-transformations/index.js +2 -2
- package/dist/image-transformations/position.js +2 -2
- package/dist/image-transformations/resize.js +1 -1
- package/dist/image-transformations/types.d.ts +3 -3
- package/dist/image-transformations/warp/cylinder.js +5 -5
- package/dist/image-transformations/warp/plane.js +7 -7
- package/dist/image-transformations/warp/sphere.js +4 -4
- package/dist/image-transformations/warp/types.d.ts +20 -20
- package/dist/mockup-normalize.d.ts +3 -3
- package/dist/mockup-normalize.js +52 -102
- package/dist/renderMockupCanvas.js +15 -19
- package/dist/renderWorkerInline.d.ts +1 -1
- package/dist/renderWorkerInline.js +1 -1
- package/dist/types.d.ts +64 -5
- package/package.json +2 -2
- package/src/lib/components/LayerControls.svelte +5 -6
- package/src/lib/components/MockupEditor.svelte +14 -17
- package/src/lib/components/MockupEditorRenderer.svelte +2 -2
- package/src/lib/components/MockupRenderer.svelte +2 -2
- package/src/lib/components/sections/SizingSection.svelte +1 -1
- package/src/lib/components/sections/TransformSection.svelte +5 -2
- package/src/lib/components/sections/WarpSection.svelte +23 -23
- package/src/lib/image-transformations/index.ts +2 -2
- package/src/lib/image-transformations/position.ts +2 -2
- package/src/lib/image-transformations/resize.ts +1 -1
- package/src/lib/image-transformations/types.ts +3 -3
- package/src/lib/image-transformations/warp/cylinder.ts +5 -5
- package/src/lib/image-transformations/warp/plane.ts +7 -7
- package/src/lib/image-transformations/warp/sphere.ts +4 -4
- package/src/lib/image-transformations/warp/types.ts +20 -20
- package/src/lib/mockup-normalize.ts +75 -144
- package/src/lib/renderMockupCanvas.ts +15 -19
- package/src/lib/renderWorkerInline.ts +1 -1
- package/src/lib/types.ts +51 -5
- package/src/routes/test/+page.svelte +2 -0
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { Layer, LayerTransform } from '../types';
|
|
3
|
-
import { defaultTransform } from '../types';
|
|
4
3
|
import type { ResizeEffect, PositionEffect, WarpEffect, RestyleEffect, SvgEffect } from '../image-transformations/types';
|
|
5
4
|
import { defaultRestyleEffect } from '../image-transformations/types';
|
|
6
5
|
import { verifySvgSource } from '../image-transformations/svg';
|
|
@@ -121,10 +120,10 @@
|
|
|
121
120
|
<div class="layer-header">
|
|
122
121
|
<button
|
|
123
122
|
class="visibility-toggle"
|
|
124
|
-
onclick={() => onVisibilityChange(!
|
|
125
|
-
aria-label={
|
|
123
|
+
onclick={() => onVisibilityChange(!layer.visible)}
|
|
124
|
+
aria-label={layer.visible ? 'Hide layer' : 'Show layer'}
|
|
126
125
|
>
|
|
127
|
-
{#if layer.visible
|
|
126
|
+
{#if layer.visible}
|
|
128
127
|
<EyeOpenIcon />
|
|
129
128
|
{:else}
|
|
130
129
|
<EyeClosedIcon />
|
|
@@ -174,8 +173,8 @@
|
|
|
174
173
|
/>
|
|
175
174
|
<RestyleSection effect={restyleEffect} onChange={handleRestyleChange} />
|
|
176
175
|
<TransformSection
|
|
177
|
-
transform={layer.transform
|
|
178
|
-
opacity={layer.opacity
|
|
176
|
+
transform={layer.transform}
|
|
177
|
+
opacity={layer.opacity}
|
|
179
178
|
{canvasWidth}
|
|
180
179
|
{canvasHeight}
|
|
181
180
|
onTransformChange={onTransformChange}
|
|
@@ -446,23 +446,20 @@
|
|
|
446
446
|
{/if}
|
|
447
447
|
</div>
|
|
448
448
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
{
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
/>
|
|
464
|
-
{/if}
|
|
465
|
-
{/if}
|
|
449
|
+
{#if selectedLayer}
|
|
450
|
+
<LayerControls
|
|
451
|
+
layer={selectedLayer}
|
|
452
|
+
canvasWidth={mockup.composition.width}
|
|
453
|
+
canvasHeight={mockup.composition.height}
|
|
454
|
+
onTransformChange={(transform) => handleLayerTransform(selectedLayer.id, transform)}
|
|
455
|
+
onOpacityChange={(opacity) => handleLayerOpacityChange(selectedLayer.id, opacity)}
|
|
456
|
+
onVisibilityChange={(visible) => handleLayerVisibilityChange(selectedLayer.id, visible)}
|
|
457
|
+
onEffectsChange={(effects) => handleLayerEffectsChange(selectedLayer.id, effects)}
|
|
458
|
+
onNameChange={(name) => handleLayerNameChange(selectedLayer.id, name)}
|
|
459
|
+
onSrcChange={(src) => handleLayerSrcChange(selectedLayer.id, src)}
|
|
460
|
+
onDelete={() => handleDeleteLayer(selectedLayer.id)}
|
|
461
|
+
/>
|
|
462
|
+
{/if}
|
|
466
463
|
</div>
|
|
467
464
|
</sidebar>
|
|
468
465
|
<div class="output-canvas">
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { onMount, onDestroy, tick, untrack } from 'svelte';
|
|
3
|
-
import type { Mockup } from '../types';
|
|
3
|
+
import type { Mockup, MockupConfig } from '../types';
|
|
4
4
|
import { renderMockupCanvasEditor, type RenderController } from '../renderMockupCanvasEditor';
|
|
5
5
|
import { denormalizeMockup } from '../mockup-normalize';
|
|
6
6
|
|
|
7
7
|
interface Props {
|
|
8
|
-
mockup: Mockup;
|
|
8
|
+
mockup: Mockup | MockupConfig;
|
|
9
9
|
imageOverrides?: Record<string, string>;
|
|
10
10
|
objectFit?: 'contain' | 'cover';
|
|
11
11
|
debounceMs?: number;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { Mockup } from '../types';
|
|
1
|
+
import type { Mockup, MockupConfig } from '../types';
|
|
2
2
|
interface Props {
|
|
3
|
-
mockup: Mockup;
|
|
3
|
+
mockup: Mockup | MockupConfig;
|
|
4
4
|
imageOverrides?: Record<string, string>;
|
|
5
5
|
objectFit?: 'contain' | 'cover';
|
|
6
6
|
debounceMs?: number;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { onMount } from 'svelte';
|
|
3
|
-
import type { Mockup } from '../types';
|
|
3
|
+
import type { Mockup, MockupConfig } from '../types';
|
|
4
4
|
import { renderMockupCanvas } from '../renderMockupCanvas';
|
|
5
5
|
import { denormalizeMockup } from '../mockup-normalize';
|
|
6
6
|
|
|
7
7
|
interface Props {
|
|
8
|
-
mockup: Mockup;
|
|
8
|
+
mockup: Mockup | MockupConfig;
|
|
9
9
|
imageOverrides?: Record<string, string>;
|
|
10
10
|
objectFit?: 'contain' | 'cover';
|
|
11
11
|
}
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
|
|
81
81
|
<ControlGroup label="Mode" onReset={() => updateResize({ mode: defaultResizeEffect.mode })}>
|
|
82
82
|
<select
|
|
83
|
-
value={resizeEffect.mode
|
|
83
|
+
value={resizeEffect.mode}
|
|
84
84
|
onchange={(e) => updateResize({ mode: e.currentTarget.value as 'contain' | 'cover' })}
|
|
85
85
|
class="mode-select"
|
|
86
86
|
>
|
|
@@ -25,10 +25,13 @@
|
|
|
25
25
|
const defaults = {
|
|
26
26
|
x: 0,
|
|
27
27
|
y: 0,
|
|
28
|
+
scale: 1,
|
|
28
29
|
rotation: 0,
|
|
29
30
|
opacity: 1
|
|
30
31
|
};
|
|
31
32
|
|
|
33
|
+
const rotationValue = $derived(transform.rotation ?? defaults.rotation);
|
|
34
|
+
|
|
32
35
|
function updateTransform(updates: Partial<LayerTransform>) {
|
|
33
36
|
onTransformChange({ ...transform, ...updates });
|
|
34
37
|
}
|
|
@@ -92,14 +95,14 @@
|
|
|
92
95
|
step="1"
|
|
93
96
|
min="-360"
|
|
94
97
|
max="360"
|
|
95
|
-
|
|
98
|
+
value={rotationValue}
|
|
96
99
|
oninput={(e) => updateTransform({ rotation: Number(e.currentTarget.value) })}
|
|
97
100
|
/>
|
|
98
101
|
<input
|
|
99
102
|
type="number"
|
|
100
103
|
class="manual-input"
|
|
101
104
|
step="1"
|
|
102
|
-
|
|
105
|
+
value={rotationValue}
|
|
103
106
|
oninput={(e) => updateTransform({ rotation: Number(e.currentTarget.value) })}
|
|
104
107
|
/>
|
|
105
108
|
</ControlGroup>
|
|
@@ -138,7 +138,7 @@
|
|
|
138
138
|
step="0.05"
|
|
139
139
|
min="0.1"
|
|
140
140
|
max="5"
|
|
141
|
-
value={imageSettings.scale
|
|
141
|
+
value={imageSettings.scale}
|
|
142
142
|
oninput={(e) => updateImage({ scale: Number(e.currentTarget.value) })}
|
|
143
143
|
/>
|
|
144
144
|
<input
|
|
@@ -147,7 +147,7 @@
|
|
|
147
147
|
step="0.05"
|
|
148
148
|
min="0.1"
|
|
149
149
|
max="5"
|
|
150
|
-
value={imageSettings.scale
|
|
150
|
+
value={imageSettings.scale}
|
|
151
151
|
oninput={(e) => updateImage({ scale: Number(e.currentTarget.value) })}
|
|
152
152
|
/>
|
|
153
153
|
</ControlGroup>
|
|
@@ -157,7 +157,7 @@
|
|
|
157
157
|
step="0.01"
|
|
158
158
|
min="-1"
|
|
159
159
|
max="1"
|
|
160
|
-
value={imageSettings.translateX
|
|
160
|
+
value={imageSettings.translateX}
|
|
161
161
|
oninput={(e) => updateImage({ translateX: Number(e.currentTarget.value) })}
|
|
162
162
|
/>
|
|
163
163
|
<input
|
|
@@ -166,7 +166,7 @@
|
|
|
166
166
|
step="0.01"
|
|
167
167
|
min="-1"
|
|
168
168
|
max="1"
|
|
169
|
-
value={imageSettings.translateX
|
|
169
|
+
value={imageSettings.translateX}
|
|
170
170
|
oninput={(e) => updateImage({ translateX: Number(e.currentTarget.value) })}
|
|
171
171
|
/>
|
|
172
172
|
</ControlGroup>
|
|
@@ -176,7 +176,7 @@
|
|
|
176
176
|
step="0.01"
|
|
177
177
|
min="-1"
|
|
178
178
|
max="1"
|
|
179
|
-
value={imageSettings.translateY
|
|
179
|
+
value={imageSettings.translateY}
|
|
180
180
|
oninput={(e) => updateImage({ translateY: Number(e.currentTarget.value) })}
|
|
181
181
|
/>
|
|
182
182
|
<input
|
|
@@ -185,7 +185,7 @@
|
|
|
185
185
|
step="0.01"
|
|
186
186
|
min="-1"
|
|
187
187
|
max="1"
|
|
188
|
-
value={imageSettings.translateY
|
|
188
|
+
value={imageSettings.translateY}
|
|
189
189
|
oninput={(e) => updateImage({ translateY: Number(e.currentTarget.value) })}
|
|
190
190
|
/>
|
|
191
191
|
</ControlGroup>
|
|
@@ -204,7 +204,7 @@
|
|
|
204
204
|
step="1"
|
|
205
205
|
min="-90"
|
|
206
206
|
max="90"
|
|
207
|
-
value={plane.rotationX
|
|
207
|
+
value={plane.rotationX}
|
|
208
208
|
oninput={(e) => updatePlane({ rotationX: Number(e.currentTarget.value) })}
|
|
209
209
|
/>
|
|
210
210
|
<input
|
|
@@ -213,7 +213,7 @@
|
|
|
213
213
|
step="1"
|
|
214
214
|
min="-90"
|
|
215
215
|
max="90"
|
|
216
|
-
value={plane.rotationX
|
|
216
|
+
value={plane.rotationX}
|
|
217
217
|
oninput={(e) => updatePlane({ rotationX: Number(e.currentTarget.value) })}
|
|
218
218
|
/>
|
|
219
219
|
</ControlGroup>
|
|
@@ -223,7 +223,7 @@
|
|
|
223
223
|
step="1"
|
|
224
224
|
min="-90"
|
|
225
225
|
max="90"
|
|
226
|
-
value={plane.rotationY
|
|
226
|
+
value={plane.rotationY}
|
|
227
227
|
oninput={(e) => updatePlane({ rotationY: Number(e.currentTarget.value) })}
|
|
228
228
|
/>
|
|
229
229
|
<input
|
|
@@ -232,7 +232,7 @@
|
|
|
232
232
|
step="1"
|
|
233
233
|
min="-90"
|
|
234
234
|
max="90"
|
|
235
|
-
value={plane.rotationY
|
|
235
|
+
value={plane.rotationY}
|
|
236
236
|
oninput={(e) => updatePlane({ rotationY: Number(e.currentTarget.value) })}
|
|
237
237
|
/>
|
|
238
238
|
</ControlGroup>
|
|
@@ -242,7 +242,7 @@
|
|
|
242
242
|
step="1"
|
|
243
243
|
min="-180"
|
|
244
244
|
max="180"
|
|
245
|
-
value={plane.rotationZ
|
|
245
|
+
value={plane.rotationZ}
|
|
246
246
|
oninput={(e) => updatePlane({ rotationZ: Number(e.currentTarget.value) })}
|
|
247
247
|
/>
|
|
248
248
|
<input
|
|
@@ -251,7 +251,7 @@
|
|
|
251
251
|
step="1"
|
|
252
252
|
min="-180"
|
|
253
253
|
max="180"
|
|
254
|
-
value={plane.rotationZ
|
|
254
|
+
value={plane.rotationZ}
|
|
255
255
|
oninput={(e) => updatePlane({ rotationZ: Number(e.currentTarget.value) })}
|
|
256
256
|
/>
|
|
257
257
|
</ControlGroup>
|
|
@@ -261,7 +261,7 @@
|
|
|
261
261
|
step="0.1"
|
|
262
262
|
min="0"
|
|
263
263
|
max="3"
|
|
264
|
-
value={plane.depthScale
|
|
264
|
+
value={plane.depthScale}
|
|
265
265
|
oninput={(e) => updatePlane({ depthScale: Number(e.currentTarget.value) })}
|
|
266
266
|
/>
|
|
267
267
|
<input
|
|
@@ -270,7 +270,7 @@
|
|
|
270
270
|
step="0.1"
|
|
271
271
|
min="0"
|
|
272
272
|
max="3"
|
|
273
|
-
value={plane.depthScale
|
|
273
|
+
value={plane.depthScale}
|
|
274
274
|
oninput={(e) => updatePlane({ depthScale: Number(e.currentTarget.value) })}
|
|
275
275
|
/>
|
|
276
276
|
</ControlGroup>
|
|
@@ -327,7 +327,7 @@
|
|
|
327
327
|
step="1"
|
|
328
328
|
min="-90"
|
|
329
329
|
max="90"
|
|
330
|
-
value={cylinder.rotationX
|
|
330
|
+
value={cylinder.rotationX}
|
|
331
331
|
oninput={(e) => updateCylinder({ rotationX: Number(e.currentTarget.value) })}
|
|
332
332
|
/>
|
|
333
333
|
<input
|
|
@@ -336,7 +336,7 @@
|
|
|
336
336
|
step="1"
|
|
337
337
|
min="-90"
|
|
338
338
|
max="90"
|
|
339
|
-
value={cylinder.rotationX
|
|
339
|
+
value={cylinder.rotationX}
|
|
340
340
|
oninput={(e) => updateCylinder({ rotationX: Number(e.currentTarget.value) })}
|
|
341
341
|
/>
|
|
342
342
|
</ControlGroup>
|
|
@@ -347,7 +347,7 @@
|
|
|
347
347
|
step="1"
|
|
348
348
|
min="-180"
|
|
349
349
|
max="180"
|
|
350
|
-
value={cylinder.rotationZ
|
|
350
|
+
value={cylinder.rotationZ}
|
|
351
351
|
oninput={(e) => updateCylinder({ rotationZ: Number(e.currentTarget.value) })}
|
|
352
352
|
/>
|
|
353
353
|
<input
|
|
@@ -356,7 +356,7 @@
|
|
|
356
356
|
step="1"
|
|
357
357
|
min="-180"
|
|
358
358
|
max="180"
|
|
359
|
-
value={cylinder.rotationZ
|
|
359
|
+
value={cylinder.rotationZ}
|
|
360
360
|
oninput={(e) => updateCylinder({ rotationZ: Number(e.currentTarget.value) })}
|
|
361
361
|
/>
|
|
362
362
|
</ControlGroup>
|
|
@@ -395,7 +395,7 @@
|
|
|
395
395
|
step="1"
|
|
396
396
|
min="-180"
|
|
397
397
|
max="180"
|
|
398
|
-
value={sphere.rotationZ
|
|
398
|
+
value={sphere.rotationZ}
|
|
399
399
|
oninput={(e) => updateSphere({ rotationZ: Number(e.currentTarget.value) })}
|
|
400
400
|
/>
|
|
401
401
|
<input
|
|
@@ -404,7 +404,7 @@
|
|
|
404
404
|
step="1"
|
|
405
405
|
min="-180"
|
|
406
406
|
max="180"
|
|
407
|
-
value={sphere.rotationZ
|
|
407
|
+
value={sphere.rotationZ}
|
|
408
408
|
oninput={(e) => updateSphere({ rotationZ: Number(e.currentTarget.value) })}
|
|
409
409
|
/>
|
|
410
410
|
</ControlGroup>
|
|
@@ -419,21 +419,21 @@
|
|
|
419
419
|
<ControlGroup label="Show Grid/Bounds" onReset={() => updateDebug({ showBounds: defaultWarpDebug.showBounds })}>
|
|
420
420
|
<input
|
|
421
421
|
type="checkbox"
|
|
422
|
-
checked={debugSettings.showBounds
|
|
422
|
+
checked={debugSettings.showBounds}
|
|
423
423
|
onchange={(e) => updateDebug({ showBounds: e.currentTarget.checked })}
|
|
424
424
|
/>
|
|
425
425
|
</ControlGroup>
|
|
426
426
|
<ControlGroup label="Depth Shader" onReset={() => updateDebug({ showDepthVis: defaultWarpDebug.showDepthVis })}>
|
|
427
427
|
<input
|
|
428
428
|
type="checkbox"
|
|
429
|
-
checked={debugSettings.showDepthVis
|
|
429
|
+
checked={debugSettings.showDepthVis}
|
|
430
430
|
onchange={(e) => updateDebug({ showDepthVis: e.currentTarget.checked })}
|
|
431
431
|
/>
|
|
432
432
|
</ControlGroup>
|
|
433
433
|
<ControlGroup label="Crosshair" onReset={() => updateDebug({ showCrosshair: defaultWarpDebug.showCrosshair })}>
|
|
434
434
|
<input
|
|
435
435
|
type="checkbox"
|
|
436
|
-
checked={debugSettings.showCrosshair
|
|
436
|
+
checked={debugSettings.showCrosshair}
|
|
437
437
|
onchange={(e) => updateDebug({ showCrosshair: e.currentTarget.checked })}
|
|
438
438
|
/>
|
|
439
439
|
</ControlGroup>
|
|
@@ -59,7 +59,7 @@ export function processLayerImage(image, effects) {
|
|
|
59
59
|
ctx.setLineDash([]);
|
|
60
60
|
ctx.font = '12px monospace';
|
|
61
61
|
ctx.fillStyle = 'rgba(0,0,0,0.7)';
|
|
62
|
-
const labelText = `Resize: ${Math.round(containerWidth)}x${Math.round(containerHeight)} (${resizeEffect.mode
|
|
62
|
+
const labelText = `Resize: ${Math.round(containerWidth)}x${Math.round(containerHeight)} (${resizeEffect.mode})`;
|
|
63
63
|
const labelWidth = ctx.measureText(labelText).width + 8;
|
|
64
64
|
ctx.fillRect(0, -22, labelWidth, 20);
|
|
65
65
|
ctx.fillStyle = '#00aa00';
|
|
@@ -99,7 +99,7 @@ export function processLayerImage(image, effects) {
|
|
|
99
99
|
ctx.setLineDash([]);
|
|
100
100
|
ctx.font = '12px monospace';
|
|
101
101
|
ctx.fillStyle = 'rgba(0,0,0,0.7)';
|
|
102
|
-
const resizeLabelText = `Resize: ${Math.round(resizeEffect.width)}x${Math.round(resizeEffect.height)} (${resizeEffect.mode
|
|
102
|
+
const resizeLabelText = `Resize: ${Math.round(resizeEffect.width)}x${Math.round(resizeEffect.height)} (${resizeEffect.mode})`;
|
|
103
103
|
const resizeLabelWidth = ctx.measureText(resizeLabelText).width + 8;
|
|
104
104
|
ctx.fillRect(0, -22, resizeLabelWidth, 20);
|
|
105
105
|
ctx.fillStyle = '#00aa00';
|
|
@@ -20,7 +20,7 @@ function calculateVerticalOffset(imageHeight, containerHeight, anchor) {
|
|
|
20
20
|
}
|
|
21
21
|
export function calculatePosition(imageWidth, imageHeight, effect) {
|
|
22
22
|
return {
|
|
23
|
-
offsetX: calculateHorizontalOffset(imageWidth, effect.containerWidth, effect.horizontal
|
|
24
|
-
offsetY: calculateVerticalOffset(imageHeight, effect.containerHeight, effect.vertical
|
|
23
|
+
offsetX: calculateHorizontalOffset(imageWidth, effect.containerWidth, effect.horizontal),
|
|
24
|
+
offsetY: calculateVerticalOffset(imageHeight, effect.containerHeight, effect.vertical)
|
|
25
25
|
};
|
|
26
26
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export function calculateResize(originalWidth, originalHeight, effect) {
|
|
2
2
|
const targetWidth = effect.width;
|
|
3
3
|
const targetHeight = effect.height;
|
|
4
|
-
if (
|
|
4
|
+
if (effect.mode === 'contain') {
|
|
5
5
|
// Fit inside target dimensions, maintain aspect ratio
|
|
6
6
|
const scale = Math.min(targetWidth / originalWidth, targetHeight / originalHeight);
|
|
7
7
|
return {
|
|
@@ -18,7 +18,7 @@ export interface ResizeEffect {
|
|
|
18
18
|
type: 'resize';
|
|
19
19
|
width: number;
|
|
20
20
|
height: number;
|
|
21
|
-
mode
|
|
21
|
+
mode: ResizeMode;
|
|
22
22
|
debug?: boolean;
|
|
23
23
|
}
|
|
24
24
|
export type HorizontalAnchor = 'left' | 'center' | 'right';
|
|
@@ -27,8 +27,8 @@ export interface PositionEffect {
|
|
|
27
27
|
type: 'position';
|
|
28
28
|
containerWidth: number;
|
|
29
29
|
containerHeight: number;
|
|
30
|
-
horizontal
|
|
31
|
-
vertical
|
|
30
|
+
horizontal: HorizontalAnchor;
|
|
31
|
+
vertical: VerticalAnchor;
|
|
32
32
|
debug?: boolean;
|
|
33
33
|
}
|
|
34
34
|
export { type WarpShape, type ImageOnSurface, type WarpDebug, type CylinderWarpParams, type PlaneWarpParams, type SphereWarpParams, type CustomWarpParams, type WarpEffect, defaultImageOnSurface, defaultWarpDebug, defaultCylinderWarp, defaultPlaneWarp, defaultSphereWarp, defaultWarpEffect } from './warp';
|
|
@@ -141,16 +141,16 @@ function drawCylinderBounds(ctx, centerX, centerY, radius, height, rotX, rotZ, f
|
|
|
141
141
|
export function drawCylinderWarp(ctx, img, centerX, centerY, width, height, params, imageTransform, quality, warpDebug) {
|
|
142
142
|
const { diameter, perspective, rotationX, rotationZ } = params;
|
|
143
143
|
const radius = diameter / 2;
|
|
144
|
-
const rotXR =
|
|
145
|
-
const rotZR =
|
|
144
|
+
const rotXR = rotationX * Math.PI / 180;
|
|
145
|
+
const rotZR = rotationZ * Math.PI / 180;
|
|
146
146
|
const hasXRotation = Math.abs(rotXR) > 0.001;
|
|
147
147
|
const perspectiveStrength = perspective ?? 1;
|
|
148
148
|
const focalLength = perspectiveStrength > 0.01
|
|
149
149
|
? radius * (10 / perspectiveStrength)
|
|
150
150
|
: radius * 1000;
|
|
151
|
-
const imgScale = imageTransform.scale
|
|
152
|
-
const imgTransX = imageTransform.translateX
|
|
153
|
-
const imgTransY = imageTransform.translateY
|
|
151
|
+
const imgScale = imageTransform.scale;
|
|
152
|
+
const imgTransX = imageTransform.translateX;
|
|
153
|
+
const imgTransY = imageTransform.translateY;
|
|
154
154
|
// Output bounds are the UNTRANSFORMED cylinder area
|
|
155
155
|
// The transformation is handled entirely by the inverse transform
|
|
156
156
|
// We just need enough area to cover the transformed result
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { drawWarpPerPixel, drawCrosshair, rotate3D, rotate3DInverse, drawPlaneGrid } from './helpers';
|
|
2
2
|
export function drawPlaneWarp(ctx, img, centerX, centerY, width, height, params, imageTransform, quality, warpDebug) {
|
|
3
3
|
const { rotationX, rotationY, rotationZ, depthScale } = params;
|
|
4
|
-
const rotX =
|
|
5
|
-
const rotY =
|
|
6
|
-
const rotZ =
|
|
4
|
+
const rotX = rotationX * (Math.PI / 180);
|
|
5
|
+
const rotY = rotationY * (Math.PI / 180);
|
|
6
|
+
const rotZ = rotationZ * (Math.PI / 180);
|
|
7
7
|
const hasRotation = Math.abs(rotX) > 0.001 || Math.abs(rotY) > 0.001 || Math.abs(rotZ) > 0.001;
|
|
8
|
-
const imgScale = imageTransform.scale
|
|
9
|
-
const imgTransX = imageTransform.translateX
|
|
10
|
-
const imgTransY = imageTransform.translateY
|
|
11
|
-
const perspectiveFactor =
|
|
8
|
+
const imgScale = imageTransform.scale;
|
|
9
|
+
const imgTransX = imageTransform.translateX;
|
|
10
|
+
const imgTransY = imageTransform.translateY;
|
|
11
|
+
const perspectiveFactor = depthScale * 0.001;
|
|
12
12
|
const focalLength = perspectiveFactor > 0.0001 ? 1 / perspectiveFactor : 10000;
|
|
13
13
|
const forwardTransform = (localX, localY) => {
|
|
14
14
|
const [x3d, y3d, z3d] = rotate3D(localX, localY, 0, rotX, rotY, rotZ);
|
|
@@ -44,10 +44,10 @@ function drawSphereBounds(ctx, centerX, centerY, radius, rotZ) {
|
|
|
44
44
|
}
|
|
45
45
|
export function drawSphereWarp(ctx, img, centerX, centerY, width, height, params, imageTransform, quality, warpDebug) {
|
|
46
46
|
const { radius, rotationZ } = params;
|
|
47
|
-
const rotZ =
|
|
48
|
-
const imgScale = imageTransform.scale
|
|
49
|
-
const imgTransX = imageTransform.translateX
|
|
50
|
-
const imgTransY = imageTransform.translateY
|
|
47
|
+
const rotZ = rotationZ * (Math.PI / 180);
|
|
48
|
+
const imgScale = imageTransform.scale;
|
|
49
|
+
const imgTransX = imageTransform.translateX;
|
|
50
|
+
const imgTransY = imageTransform.translateY;
|
|
51
51
|
const outputBounds = {
|
|
52
52
|
minX: -radius,
|
|
53
53
|
minY: -radius,
|
|
@@ -1,33 +1,33 @@
|
|
|
1
1
|
export type WarpShape = 'none' | 'cylinder' | 'plane' | 'sphere' | 'custom';
|
|
2
2
|
export interface ImageOnSurface {
|
|
3
|
-
scale
|
|
4
|
-
translateX
|
|
5
|
-
translateY
|
|
3
|
+
scale: number;
|
|
4
|
+
translateX: number;
|
|
5
|
+
translateY: number;
|
|
6
6
|
}
|
|
7
7
|
export interface WarpDebug {
|
|
8
|
-
showBounds
|
|
9
|
-
showDepthVis
|
|
10
|
-
showCrosshair
|
|
8
|
+
showBounds: boolean;
|
|
9
|
+
showDepthVis: boolean;
|
|
10
|
+
showCrosshair: boolean;
|
|
11
11
|
}
|
|
12
12
|
export interface CylinderWarpParams {
|
|
13
13
|
diameter: number;
|
|
14
|
-
pitch
|
|
15
|
-
perspective
|
|
16
|
-
rotationX
|
|
17
|
-
rotationY
|
|
18
|
-
rotationZ
|
|
14
|
+
pitch: number;
|
|
15
|
+
perspective: number;
|
|
16
|
+
rotationX: number;
|
|
17
|
+
rotationY: number;
|
|
18
|
+
rotationZ: number;
|
|
19
19
|
}
|
|
20
20
|
export interface PlaneWarpParams {
|
|
21
|
-
rotationX
|
|
22
|
-
rotationY
|
|
23
|
-
rotationZ
|
|
24
|
-
depthScale
|
|
21
|
+
rotationX: number;
|
|
22
|
+
rotationY: number;
|
|
23
|
+
rotationZ: number;
|
|
24
|
+
depthScale: number;
|
|
25
25
|
}
|
|
26
26
|
export interface SphereWarpParams {
|
|
27
27
|
radius: number;
|
|
28
|
-
rotationX
|
|
29
|
-
rotationY
|
|
30
|
-
rotationZ
|
|
28
|
+
rotationX: number;
|
|
29
|
+
rotationY: number;
|
|
30
|
+
rotationZ: number;
|
|
31
31
|
}
|
|
32
32
|
export interface CustomWarpParams {
|
|
33
33
|
formula: string;
|
|
@@ -37,8 +37,8 @@ export interface WarpEffect {
|
|
|
37
37
|
type: 'warp';
|
|
38
38
|
shape: WarpShape;
|
|
39
39
|
enabled: boolean;
|
|
40
|
-
quality
|
|
41
|
-
image
|
|
40
|
+
quality?: number;
|
|
41
|
+
image?: ImageOnSurface;
|
|
42
42
|
debug?: WarpDebug;
|
|
43
43
|
cylinder?: CylinderWarpParams;
|
|
44
44
|
plane?: PlaneWarpParams;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type { Mockup } from './types';
|
|
2
|
-
export declare function normalizeMockup(mockup: Mockup, clean?: boolean): Partial<
|
|
3
|
-
export declare function denormalizeMockup(mockup: Partial<
|
|
1
|
+
import type { Mockup, MockupConfig, DenormalizedMockup } from './types';
|
|
2
|
+
export declare function normalizeMockup(mockup: Mockup, clean?: boolean): Partial<MockupConfig>;
|
|
3
|
+
export declare function denormalizeMockup(mockup: Partial<MockupConfig>): DenormalizedMockup;
|