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.
Files changed (44) hide show
  1. package/dist/components/LayerControls.svelte +5 -6
  2. package/dist/components/MockupEditor.svelte +14 -17
  3. package/dist/components/MockupEditorRenderer.svelte +2 -2
  4. package/dist/components/MockupEditorRenderer.svelte.d.ts +2 -2
  5. package/dist/components/MockupRenderer.svelte +2 -2
  6. package/dist/components/MockupRenderer.svelte.d.ts +2 -2
  7. package/dist/components/sections/SizingSection.svelte +1 -1
  8. package/dist/components/sections/TransformSection.svelte +5 -2
  9. package/dist/components/sections/WarpSection.svelte +23 -23
  10. package/dist/image-transformations/index.js +2 -2
  11. package/dist/image-transformations/position.js +2 -2
  12. package/dist/image-transformations/resize.js +1 -1
  13. package/dist/image-transformations/types.d.ts +3 -3
  14. package/dist/image-transformations/warp/cylinder.js +5 -5
  15. package/dist/image-transformations/warp/plane.js +7 -7
  16. package/dist/image-transformations/warp/sphere.js +4 -4
  17. package/dist/image-transformations/warp/types.d.ts +20 -20
  18. package/dist/mockup-normalize.d.ts +3 -3
  19. package/dist/mockup-normalize.js +52 -102
  20. package/dist/renderMockupCanvas.js +15 -19
  21. package/dist/renderWorkerInline.d.ts +1 -1
  22. package/dist/renderWorkerInline.js +1 -1
  23. package/dist/types.d.ts +64 -5
  24. package/package.json +2 -2
  25. package/src/lib/components/LayerControls.svelte +5 -6
  26. package/src/lib/components/MockupEditor.svelte +14 -17
  27. package/src/lib/components/MockupEditorRenderer.svelte +2 -2
  28. package/src/lib/components/MockupRenderer.svelte +2 -2
  29. package/src/lib/components/sections/SizingSection.svelte +1 -1
  30. package/src/lib/components/sections/TransformSection.svelte +5 -2
  31. package/src/lib/components/sections/WarpSection.svelte +23 -23
  32. package/src/lib/image-transformations/index.ts +2 -2
  33. package/src/lib/image-transformations/position.ts +2 -2
  34. package/src/lib/image-transformations/resize.ts +1 -1
  35. package/src/lib/image-transformations/types.ts +3 -3
  36. package/src/lib/image-transformations/warp/cylinder.ts +5 -5
  37. package/src/lib/image-transformations/warp/plane.ts +7 -7
  38. package/src/lib/image-transformations/warp/sphere.ts +4 -4
  39. package/src/lib/image-transformations/warp/types.ts +20 -20
  40. package/src/lib/mockup-normalize.ts +75 -144
  41. package/src/lib/renderMockupCanvas.ts +15 -19
  42. package/src/lib/renderWorkerInline.ts +1 -1
  43. package/src/lib/types.ts +51 -5
  44. 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(!(layer.visible ?? true))}
125
- aria-label={(layer.visible ?? true) ? 'Hide layer' : 'Show layer'}
123
+ onclick={() => onVisibilityChange(!layer.visible)}
124
+ aria-label={layer.visible ? 'Hide layer' : 'Show layer'}
126
125
  >
127
- {#if layer.visible ?? true}
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 ?? defaultTransform}
178
- opacity={layer.opacity ?? 1}
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
- {#if selectedLayerId}
450
- {@const selectedLayer = mockup.composition.layers.find((l) => l.id === selectedLayerId)}
451
- {#if selectedLayer}
452
- <LayerControls
453
- layer={selectedLayer}
454
- canvasWidth={mockup.composition.width}
455
- canvasHeight={mockup.composition.height}
456
- onTransformChange={(transform) => handleLayerTransform(selectedLayer.id, transform)}
457
- onOpacityChange={(opacity) => handleLayerOpacityChange(selectedLayer.id, opacity)}
458
- onVisibilityChange={(visible) => handleLayerVisibilityChange(selectedLayer.id, visible)}
459
- onEffectsChange={(effects) => handleLayerEffectsChange(selectedLayer.id, effects)}
460
- onNameChange={(name) => handleLayerNameChange(selectedLayer.id, name)}
461
- onSrcChange={(src) => handleLayerSrcChange(selectedLayer.id, src)}
462
- onDelete={() => handleDeleteLayer(selectedLayer.id)}
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
  }
@@ -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
  }
@@ -80,7 +80,7 @@
80
80
 
81
81
  <ControlGroup label="Mode" onReset={() => updateResize({ mode: defaultResizeEffect.mode })}>
82
82
  <select
83
- value={resizeEffect.mode ?? 'contain'}
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
- bind:value={transform.rotation}
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
- bind:value={transform.rotation}
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 ?? 1}
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 ?? 1}
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 ?? 0}
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 ?? 0}
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 ?? 0}
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 ?? 0}
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 ?? 0}
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 ?? 0}
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 ?? 0}
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 ?? 0}
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 ?? 0}
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 ?? 0}
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 ?? 1}
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 ?? 1}
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 ?? 0}
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 ?? 0}
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 ?? 0}
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 ?? 0}
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 ?? 0}
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 ?? 0}
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 ?? false}
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 ?? false}
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 ?? false}
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 ?? 'contain'})`;
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 ?? 'contain'})`;
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 ?? 'center'),
24
- offsetY: calculateVerticalOffset(imageHeight, effect.containerHeight, effect.vertical ?? 'middle')
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 ((effect.mode ?? 'contain') === 'contain') {
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?: ResizeMode;
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?: HorizontalAnchor;
31
- vertical?: VerticalAnchor;
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 = (rotationX ?? 0) * Math.PI / 180;
145
- const rotZR = (rotationZ ?? 0) * Math.PI / 180;
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 ?? 1;
152
- const imgTransX = imageTransform.translateX ?? 0;
153
- const imgTransY = imageTransform.translateY ?? 0;
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 = (rotationX ?? 0) * (Math.PI / 180);
5
- const rotY = (rotationY ?? 0) * (Math.PI / 180);
6
- const rotZ = (rotationZ ?? 0) * (Math.PI / 180);
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 ?? 1;
9
- const imgTransX = imageTransform.translateX ?? 0;
10
- const imgTransY = imageTransform.translateY ?? 0;
11
- const perspectiveFactor = (depthScale ?? 1) * 0.001;
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 = (rotationZ ?? 0) * (Math.PI / 180);
48
- const imgScale = imageTransform.scale ?? 1;
49
- const imgTransX = imageTransform.translateX ?? 0;
50
- const imgTransY = imageTransform.translateY ?? 0;
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?: number;
4
- translateX?: number;
5
- translateY?: number;
3
+ scale: number;
4
+ translateX: number;
5
+ translateY: number;
6
6
  }
7
7
  export interface WarpDebug {
8
- showBounds?: boolean;
9
- showDepthVis?: boolean;
10
- showCrosshair?: boolean;
8
+ showBounds: boolean;
9
+ showDepthVis: boolean;
10
+ showCrosshair: boolean;
11
11
  }
12
12
  export interface CylinderWarpParams {
13
13
  diameter: number;
14
- pitch?: number;
15
- perspective?: number;
16
- rotationX?: number;
17
- rotationY?: number;
18
- rotationZ?: number;
14
+ pitch: number;
15
+ perspective: number;
16
+ rotationX: number;
17
+ rotationY: number;
18
+ rotationZ: number;
19
19
  }
20
20
  export interface PlaneWarpParams {
21
- rotationX?: number;
22
- rotationY?: number;
23
- rotationZ?: number;
24
- depthScale?: number;
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?: number;
29
- rotationY?: number;
30
- rotationZ?: number;
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: number;
41
- image: ImageOnSurface;
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<Mockup>;
3
- export declare function denormalizeMockup(mockup: Partial<Mockup>): Mockup;
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;