@gradio/imageeditor 0.14.4 → 0.15.0
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 +21 -0
- package/InteractiveImageEditor.svelte +4 -7
- package/dist/InteractiveImageEditor.svelte +4 -3
- package/dist/InteractiveImageEditor.svelte.d.ts +1 -1
- package/dist/shared/Controls.svelte +32 -1
- package/dist/shared/Controls.svelte.d.ts +2 -0
- package/dist/shared/ImageEditor.svelte +23 -1
- package/dist/shared/ImageEditor.svelte.d.ts +2 -1
- package/dist/shared/brush/brush-cursor.js +0 -42
- package/dist/shared/brush/brush-textures.d.ts +19 -2
- package/dist/shared/brush/brush-textures.js +120 -82
- package/dist/shared/brush/brush.js +0 -52
- package/dist/shared/{utils → core}/commands.d.ts +19 -58
- package/dist/shared/core/commands.js +70 -0
- package/dist/shared/core/editor.d.ts +4 -91
- package/dist/shared/core/editor.js +18 -708
- package/dist/shared/core/layers.d.ts +143 -0
- package/dist/shared/core/layers.js +774 -0
- package/dist/shared/image/image.d.ts +14 -4
- package/dist/shared/image/image.js +91 -18
- package/dist/shared/resize/resize.d.ts +31 -0
- package/dist/shared/resize/resize.js +127 -290
- package/dist/shared/zoom/zoom.js +0 -51
- package/package.json +7 -7
- package/shared/Controls.svelte +33 -1
- package/shared/ImageEditor.svelte +25 -9
- package/shared/Toolbar.svelte +1 -4
- package/shared/brush/ColorSwatch.svelte +0 -3
- package/shared/brush/brush-cursor.ts +0 -42
- package/shared/brush/brush-textures.ts +166 -83
- package/shared/brush/brush.ts +0 -53
- package/shared/core/commands.ts +118 -0
- package/shared/core/editor.ts +33 -957
- package/shared/core/layers.ts +1068 -0
- package/shared/image/image.ts +139 -21
- package/shared/resize/resize.ts +160 -299
- package/shared/zoom/zoom.ts +0 -51
- package/dist/shared/utils/commands.js +0 -73
- package/shared/utils/commands.ts +0 -174
package/shared/resize/resize.ts
CHANGED
|
@@ -8,6 +8,66 @@ import {
|
|
|
8
8
|
} from "pixi.js";
|
|
9
9
|
import { type ImageEditorContext, type Tool } from "../core/editor";
|
|
10
10
|
import { type Tool as ToolbarTool, type Subtool } from "../Toolbar.svelte";
|
|
11
|
+
import { type Command } from "../core/commands";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Command that handles resizing the image and supports undo/redo
|
|
15
|
+
*/
|
|
16
|
+
export class ResizeCommand implements Command {
|
|
17
|
+
private original_width: number;
|
|
18
|
+
private original_height: number;
|
|
19
|
+
private original_x: number;
|
|
20
|
+
private original_y: number;
|
|
21
|
+
|
|
22
|
+
private new_width: number;
|
|
23
|
+
private new_height: number;
|
|
24
|
+
private new_x: number;
|
|
25
|
+
private new_y: number;
|
|
26
|
+
private reset_ui: () => void;
|
|
27
|
+
name: string;
|
|
28
|
+
|
|
29
|
+
constructor(
|
|
30
|
+
private context: ImageEditorContext,
|
|
31
|
+
original_state: { width: number; height: number; x: number; y: number },
|
|
32
|
+
new_state: { width: number; height: number; x: number; y: number },
|
|
33
|
+
reset_ui: () => void
|
|
34
|
+
) {
|
|
35
|
+
this.name = "Resize";
|
|
36
|
+
this.original_width = original_state.width;
|
|
37
|
+
this.original_height = original_state.height;
|
|
38
|
+
this.original_x = original_state.x;
|
|
39
|
+
this.original_y = original_state.y;
|
|
40
|
+
|
|
41
|
+
this.new_width = new_state.width;
|
|
42
|
+
this.new_height = new_state.height;
|
|
43
|
+
this.new_x = new_state.x;
|
|
44
|
+
this.new_y = new_state.y;
|
|
45
|
+
this.reset_ui = reset_ui;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async execute(): Promise<void> {
|
|
49
|
+
if (!this.context.background_image) return;
|
|
50
|
+
|
|
51
|
+
this.context.background_image.width = this.new_width;
|
|
52
|
+
this.context.background_image.height = this.new_height;
|
|
53
|
+
this.context.background_image.position.set(this.new_x, this.new_y);
|
|
54
|
+
|
|
55
|
+
this.reset_ui();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async undo(): Promise<void> {
|
|
59
|
+
if (!this.context.background_image) return;
|
|
60
|
+
|
|
61
|
+
this.context.background_image.width = this.original_width;
|
|
62
|
+
this.context.background_image.height = this.original_height;
|
|
63
|
+
this.context.background_image.position.set(
|
|
64
|
+
this.original_x,
|
|
65
|
+
this.original_y
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
this.reset_ui();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
11
71
|
|
|
12
72
|
/**
|
|
13
73
|
* @class ResizeTool
|
|
@@ -20,15 +80,12 @@ export class ResizeTool implements Tool {
|
|
|
20
80
|
current_tool: ToolbarTool = "image";
|
|
21
81
|
current_subtool: Subtool = "size";
|
|
22
82
|
private current_cursor: FederatedOptions["cursor"] = "unset";
|
|
23
|
-
// Constants
|
|
24
83
|
private readonly CORNER_SIZE = 25;
|
|
25
84
|
private readonly LINE_THICKNESS = 5;
|
|
26
85
|
private readonly HANDLE_COLOR = 0x000000;
|
|
27
86
|
private readonly HIT_AREA_SIZE = 40;
|
|
28
|
-
// Border region for outpainting
|
|
29
87
|
private borderRegion = 0;
|
|
30
88
|
|
|
31
|
-
// State
|
|
32
89
|
private is_dragging = false;
|
|
33
90
|
private selected_handle: Container | null = null;
|
|
34
91
|
private active_corner_index = -1;
|
|
@@ -48,6 +105,13 @@ export class ResizeTool implements Tool {
|
|
|
48
105
|
private event_callbacks: Map<string, (() => void)[]> = new Map();
|
|
49
106
|
private last_scale = 1;
|
|
50
107
|
|
|
108
|
+
private original_state: {
|
|
109
|
+
width: number;
|
|
110
|
+
height: number;
|
|
111
|
+
x: number;
|
|
112
|
+
y: number;
|
|
113
|
+
} | null = null;
|
|
114
|
+
|
|
51
115
|
/**
|
|
52
116
|
* @method setup
|
|
53
117
|
* @async
|
|
@@ -66,29 +130,21 @@ export class ResizeTool implements Tool {
|
|
|
66
130
|
this.current_tool = tool;
|
|
67
131
|
this.current_subtool = subtool;
|
|
68
132
|
|
|
69
|
-
// Get the border region from context if it exists
|
|
70
133
|
if (context.background_image) {
|
|
71
|
-
// First try to read from a custom property on the background image
|
|
72
|
-
// This is important as the position might have been adjusted
|
|
73
134
|
if ((context.background_image as any).borderRegion !== undefined) {
|
|
74
135
|
this.borderRegion = (context.background_image as any).borderRegion;
|
|
75
136
|
} else {
|
|
76
|
-
// Fall back to the position-based detection as before
|
|
77
|
-
// But add additional checks to make sure it's not just centering
|
|
78
137
|
const bg = context.background_image;
|
|
79
138
|
const container = context.image_container;
|
|
80
139
|
|
|
81
|
-
// Check if the image position seems intentional (not just centered)
|
|
82
140
|
const isHorizontallyCentered =
|
|
83
141
|
Math.abs(bg.position.x - (container.width - bg.width) / 2) < 2;
|
|
84
142
|
const isVerticallyCentered =
|
|
85
143
|
Math.abs(bg.position.y - (container.height - bg.height) / 2) < 2;
|
|
86
144
|
|
|
87
145
|
if (!isHorizontallyCentered || !isVerticallyCentered) {
|
|
88
|
-
// If not centered, this is likely an intentional border
|
|
89
146
|
this.borderRegion = Math.max(bg.position.x, bg.position.y);
|
|
90
147
|
|
|
91
|
-
// Store it for future reference
|
|
92
148
|
(context.background_image as any).borderRegion = this.borderRegion;
|
|
93
149
|
}
|
|
94
150
|
}
|
|
@@ -96,7 +152,6 @@ export class ResizeTool implements Tool {
|
|
|
96
152
|
|
|
97
153
|
context.dimensions.subscribe((dimensions) => {
|
|
98
154
|
this.dimensions = dimensions;
|
|
99
|
-
// Force UI update if it exists
|
|
100
155
|
if (this.resize_ui_container) {
|
|
101
156
|
this.update_resize_ui();
|
|
102
157
|
}
|
|
@@ -104,7 +159,6 @@ export class ResizeTool implements Tool {
|
|
|
104
159
|
|
|
105
160
|
context.position.subscribe((position) => {
|
|
106
161
|
this.position = position;
|
|
107
|
-
// Update resize UI immediately when position changes to avoid lag
|
|
108
162
|
if (
|
|
109
163
|
this.resize_ui_container &&
|
|
110
164
|
this.current_tool === "image" &&
|
|
@@ -116,7 +170,6 @@ export class ResizeTool implements Tool {
|
|
|
116
170
|
|
|
117
171
|
context.scale.subscribe((scale) => {
|
|
118
172
|
this.scale = scale;
|
|
119
|
-
// Update resize UI immediately when scale changes to avoid lag
|
|
120
173
|
if (
|
|
121
174
|
this.resize_ui_container &&
|
|
122
175
|
this.current_tool === "image" &&
|
|
@@ -126,11 +179,9 @@ export class ResizeTool implements Tool {
|
|
|
126
179
|
}
|
|
127
180
|
});
|
|
128
181
|
|
|
129
|
-
// Initialize resize UI
|
|
130
182
|
await this.init_resize_ui();
|
|
131
183
|
this.setup_event_listeners();
|
|
132
184
|
|
|
133
|
-
// Apply boundary constraints only if the image is outside the container boundaries
|
|
134
185
|
if (
|
|
135
186
|
this.current_subtool === "size" &&
|
|
136
187
|
this.image_editor_context.background_image
|
|
@@ -144,7 +195,6 @@ export class ResizeTool implements Tool {
|
|
|
144
195
|
const container_width = container_bounds.width;
|
|
145
196
|
const container_height = container_bounds.height;
|
|
146
197
|
|
|
147
|
-
// Check if the image exceeds the container boundaries
|
|
148
198
|
const min_x = -(bg_width - container_width);
|
|
149
199
|
const min_y = -(bg_height - container_height);
|
|
150
200
|
const max_x = 0;
|
|
@@ -157,7 +207,6 @@ export class ResizeTool implements Tool {
|
|
|
157
207
|
bg_height > container_height &&
|
|
158
208
|
(bg.position.y < min_y || bg.position.y > max_y);
|
|
159
209
|
|
|
160
|
-
// Only apply constraints if the image is out of bounds
|
|
161
210
|
if (x_out_of_bounds || y_out_of_bounds) {
|
|
162
211
|
this.apply_boundary_constraints();
|
|
163
212
|
}
|
|
@@ -173,7 +222,6 @@ export class ResizeTool implements Tool {
|
|
|
173
222
|
if (this.resize_ui_container) {
|
|
174
223
|
this.image_editor_context.app.stage.removeChild(this.resize_ui_container);
|
|
175
224
|
}
|
|
176
|
-
// Remove event listeners
|
|
177
225
|
this.cleanup_event_listeners();
|
|
178
226
|
}
|
|
179
227
|
|
|
@@ -189,21 +237,17 @@ export class ResizeTool implements Tool {
|
|
|
189
237
|
this.current_subtool = subtool;
|
|
190
238
|
|
|
191
239
|
if (tool === "image" && subtool === "size") {
|
|
192
|
-
// Check if there's a stored border region on the background image
|
|
193
240
|
if (this.image_editor_context.background_image) {
|
|
194
241
|
const storedBorderRegion = (
|
|
195
242
|
this.image_editor_context.background_image as any
|
|
196
243
|
).borderRegion;
|
|
197
244
|
if (typeof storedBorderRegion === "number" && storedBorderRegion > 0) {
|
|
198
|
-
// Use the stored border region
|
|
199
245
|
this.borderRegion = storedBorderRegion;
|
|
200
246
|
}
|
|
201
247
|
}
|
|
202
248
|
|
|
203
249
|
this.show_resize_ui();
|
|
204
250
|
|
|
205
|
-
// Apply boundary constraints when switching to the resize tool
|
|
206
|
-
// but only if the image is outside the container boundaries
|
|
207
251
|
if (this.image_editor_context.background_image) {
|
|
208
252
|
const bg = this.image_editor_context.background_image;
|
|
209
253
|
const container = this.image_editor_context.image_container;
|
|
@@ -214,11 +258,9 @@ export class ResizeTool implements Tool {
|
|
|
214
258
|
const container_width = container_bounds.width;
|
|
215
259
|
const container_height = container_bounds.height;
|
|
216
260
|
|
|
217
|
-
// Apply effective width/height calculations with border region
|
|
218
261
|
const effectiveWidth = container_width - this.borderRegion * 2;
|
|
219
262
|
const effectiveHeight = container_height - this.borderRegion * 2;
|
|
220
263
|
|
|
221
|
-
// Check if the image exceeds the effective container area or intrudes on border
|
|
222
264
|
const min_x =
|
|
223
265
|
bg_width > effectiveWidth
|
|
224
266
|
? -(bg_width - effectiveWidth) + this.borderRegion
|
|
@@ -237,11 +279,9 @@ export class ResizeTool implements Tool {
|
|
|
237
279
|
? this.borderRegion
|
|
238
280
|
: container_height - bg_height - this.borderRegion;
|
|
239
281
|
|
|
240
|
-
// Check if any constraints are violated
|
|
241
282
|
const x_out_of_bounds = bg.position.x < min_x || bg.position.x > max_x;
|
|
242
283
|
const y_out_of_bounds = bg.position.y < min_y || bg.position.y > max_y;
|
|
243
284
|
|
|
244
|
-
// Only apply constraints if the image is out of bounds
|
|
245
285
|
if (x_out_of_bounds || y_out_of_bounds) {
|
|
246
286
|
this.apply_boundary_constraints();
|
|
247
287
|
}
|
|
@@ -259,23 +299,16 @@ export class ResizeTool implements Tool {
|
|
|
259
299
|
* @returns {Promise<void>}
|
|
260
300
|
*/
|
|
261
301
|
private async init_resize_ui(): Promise<void> {
|
|
262
|
-
// Create resize UI with scaled dimensions
|
|
263
302
|
this.resize_ui_container = this.make_resize_ui(
|
|
264
303
|
this.dimensions.width * this.scale,
|
|
265
304
|
this.dimensions.height * this.scale
|
|
266
305
|
);
|
|
267
306
|
|
|
268
|
-
// Position the resize UI container at the image position
|
|
269
307
|
this.resize_ui_container.position.set(this.position.x, this.position.y);
|
|
270
308
|
|
|
271
|
-
// Initially hide the resize UI until explicitly enabled by set_tool
|
|
272
309
|
this.resize_ui_container.visible = false;
|
|
273
310
|
|
|
274
311
|
this.image_editor_context.app.stage.addChild(this.resize_ui_container);
|
|
275
|
-
|
|
276
|
-
// Instead of using the ticker for continuous updates, we'll use immediate updates
|
|
277
|
-
// on scale and position changes, which we've added in the setup method
|
|
278
|
-
// This will eliminate lag during zoom operations
|
|
279
312
|
}
|
|
280
313
|
|
|
281
314
|
/**
|
|
@@ -291,7 +324,6 @@ export class ResizeTool implements Tool {
|
|
|
291
324
|
resize_container.eventMode = "static";
|
|
292
325
|
resize_container.interactiveChildren = true;
|
|
293
326
|
|
|
294
|
-
// Create solid outline (will be mostly hidden by the dotted outline)
|
|
295
327
|
const outline = new Graphics().rect(0, 0, width, height).stroke({
|
|
296
328
|
width: 1,
|
|
297
329
|
color: 0x000000,
|
|
@@ -299,10 +331,8 @@ export class ResizeTool implements Tool {
|
|
|
299
331
|
alpha: 0.3
|
|
300
332
|
});
|
|
301
333
|
|
|
302
|
-
// Add a second graphics object for the dotted effect
|
|
303
334
|
const dotted_outline = new Graphics();
|
|
304
335
|
|
|
305
|
-
// Use pixelLine for crisp rendering
|
|
306
336
|
dotted_outline.rect(0, 0, width, height).stroke({
|
|
307
337
|
width: 1,
|
|
308
338
|
color: 0x000000,
|
|
@@ -310,34 +340,25 @@ export class ResizeTool implements Tool {
|
|
|
310
340
|
pixelLine: true
|
|
311
341
|
});
|
|
312
342
|
|
|
313
|
-
// Create dashed line effect by drawing small rectangles along the border
|
|
314
343
|
const dash_length = 5;
|
|
315
344
|
const gap_length = 5;
|
|
316
345
|
const total_length = dash_length + gap_length;
|
|
317
346
|
|
|
318
|
-
// Draw dashed horizontal lines (top and bottom) - draw fewer dashes for performance
|
|
319
347
|
for (let x = 0; x < width; x += total_length * 2) {
|
|
320
|
-
// Skip every other dash
|
|
321
|
-
// Top dash
|
|
322
348
|
dotted_outline
|
|
323
349
|
.rect(x, 0, Math.min(dash_length, width - x), 1)
|
|
324
350
|
.fill(0x000000);
|
|
325
351
|
|
|
326
|
-
// Bottom dash
|
|
327
352
|
dotted_outline
|
|
328
353
|
.rect(x, height - 1, Math.min(dash_length, width - x), 1)
|
|
329
354
|
.fill(0x000000);
|
|
330
355
|
}
|
|
331
356
|
|
|
332
|
-
// Draw dashed vertical lines (left and right) - draw fewer dashes for performance
|
|
333
357
|
for (let y = 0; y < height; y += total_length * 2) {
|
|
334
|
-
// Skip every other dash
|
|
335
|
-
// Left dash
|
|
336
358
|
dotted_outline
|
|
337
359
|
.rect(0, y, 1, Math.min(dash_length, height - y))
|
|
338
360
|
.fill(0x000000);
|
|
339
361
|
|
|
340
|
-
// Right dash
|
|
341
362
|
dotted_outline
|
|
342
363
|
.rect(width - 1, y, 1, Math.min(dash_length, height - y))
|
|
343
364
|
.fill(0x000000);
|
|
@@ -346,18 +367,13 @@ export class ResizeTool implements Tool {
|
|
|
346
367
|
resize_container.addChild(outline);
|
|
347
368
|
resize_container.addChild(dotted_outline);
|
|
348
369
|
|
|
349
|
-
// Create a transparent interactive area for the move cursor
|
|
350
370
|
const move_area = new Graphics()
|
|
351
371
|
.rect(0, 0, width, height)
|
|
352
|
-
.fill(0xffffff, 0);
|
|
353
|
-
|
|
372
|
+
.fill(0xffffff, 0);
|
|
354
373
|
move_area.eventMode = "static";
|
|
355
|
-
move_area.cursor = "move";
|
|
356
|
-
|
|
357
|
-
// Add the move area before the handles so it doesn't interfere with handle interactions
|
|
374
|
+
move_area.cursor = "move";
|
|
358
375
|
resize_container.addChild(move_area);
|
|
359
376
|
|
|
360
|
-
// Create handles
|
|
361
377
|
this.create_corner_handles(resize_container, width, height);
|
|
362
378
|
this.create_edge_handles(resize_container, width, height);
|
|
363
379
|
|
|
@@ -377,17 +393,13 @@ export class ResizeTool implements Tool {
|
|
|
377
393
|
handle.cursor = "pointer";
|
|
378
394
|
|
|
379
395
|
const handle_graphics = new Graphics();
|
|
380
|
-
const handle_size = is_edge ? 8 : 10;
|
|
381
|
-
|
|
382
|
-
// Create square handles
|
|
396
|
+
const handle_size = is_edge ? 8 : 10;
|
|
383
397
|
handle_graphics
|
|
384
398
|
.rect(-handle_size / 2, -handle_size / 2, handle_size, handle_size)
|
|
385
|
-
.fill(0xffffff)
|
|
386
|
-
.stroke({ width: 1, color: this.HANDLE_COLOR });
|
|
387
|
-
|
|
399
|
+
.fill(0xffffff)
|
|
400
|
+
.stroke({ width: 1, color: this.HANDLE_COLOR });
|
|
388
401
|
handle.addChild(handle_graphics);
|
|
389
402
|
|
|
390
|
-
// Adjust hit area based on handle type
|
|
391
403
|
const hit_size = is_edge ? this.HIT_AREA_SIZE * 1.5 : this.HIT_AREA_SIZE;
|
|
392
404
|
handle.hitArea = new Rectangle(
|
|
393
405
|
-hit_size / 2,
|
|
@@ -413,16 +425,14 @@ export class ResizeTool implements Tool {
|
|
|
413
425
|
width: number,
|
|
414
426
|
height: number
|
|
415
427
|
): void {
|
|
416
|
-
// Horizontal handles at top and bottom
|
|
417
428
|
[-1, 1].forEach((i, index) => {
|
|
418
|
-
const handle = this.create_handle(true);
|
|
429
|
+
const handle = this.create_handle(true);
|
|
419
430
|
handle.rotation = 0;
|
|
420
431
|
|
|
421
432
|
handle.on("pointerdown", (event: FederatedPointerEvent) => {
|
|
422
|
-
this.handle_pointer_down(event, handle, -1, index);
|
|
433
|
+
this.handle_pointer_down(event, handle, -1, index);
|
|
423
434
|
});
|
|
424
435
|
|
|
425
|
-
// Center the handle
|
|
426
436
|
handle.x = width / 2;
|
|
427
437
|
handle.y = i < 0 ? 0 : height;
|
|
428
438
|
handle.cursor = "ns-resize";
|
|
@@ -430,16 +440,13 @@ export class ResizeTool implements Tool {
|
|
|
430
440
|
container.addChild(handle);
|
|
431
441
|
});
|
|
432
442
|
|
|
433
|
-
// Vertical handles at left and right
|
|
434
443
|
[-1, 1].forEach((i, index) => {
|
|
435
|
-
const handle = this.create_handle(true);
|
|
436
|
-
handle.rotation = 0;
|
|
437
|
-
|
|
444
|
+
const handle = this.create_handle(true);
|
|
445
|
+
handle.rotation = 0;
|
|
438
446
|
handle.on("pointerdown", (event: FederatedPointerEvent) => {
|
|
439
|
-
this.handle_pointer_down(event, handle, -1, index + 2);
|
|
447
|
+
this.handle_pointer_down(event, handle, -1, index + 2);
|
|
440
448
|
});
|
|
441
449
|
|
|
442
|
-
// Center the handle
|
|
443
450
|
handle.x = i < 0 ? 0 : width;
|
|
444
451
|
handle.y = height / 2;
|
|
445
452
|
handle.cursor = "ew-resize";
|
|
@@ -462,14 +469,14 @@ export class ResizeTool implements Tool {
|
|
|
462
469
|
height: number
|
|
463
470
|
): void {
|
|
464
471
|
const corners = [
|
|
465
|
-
{ x: 0, y: 0, cursor: "nwse-resize" },
|
|
466
|
-
{ x: width, y: 0, cursor: "nesw-resize" },
|
|
467
|
-
{ x: 0, y: height, cursor: "nesw-resize" },
|
|
468
|
-
{ x: width, y: height, cursor: "nwse-resize" }
|
|
472
|
+
{ x: 0, y: 0, cursor: "nwse-resize" },
|
|
473
|
+
{ x: width, y: 0, cursor: "nesw-resize" },
|
|
474
|
+
{ x: 0, y: height, cursor: "nesw-resize" },
|
|
475
|
+
{ x: width, y: height, cursor: "nwse-resize" }
|
|
469
476
|
];
|
|
470
477
|
|
|
471
478
|
corners.forEach(({ x, y, cursor }, i) => {
|
|
472
|
-
const corner = this.create_handle(false);
|
|
479
|
+
const corner = this.create_handle(false);
|
|
473
480
|
corner.x = x;
|
|
474
481
|
corner.y = y;
|
|
475
482
|
|
|
@@ -499,7 +506,6 @@ export class ResizeTool implements Tool {
|
|
|
499
506
|
corner_index: number,
|
|
500
507
|
edge_index: number
|
|
501
508
|
): void {
|
|
502
|
-
// Only handle resize events if resize tool is active
|
|
503
509
|
if (this.current_subtool !== "size") return;
|
|
504
510
|
|
|
505
511
|
event.stopPropagation();
|
|
@@ -509,11 +515,20 @@ export class ResizeTool implements Tool {
|
|
|
509
515
|
this.active_corner_index = corner_index;
|
|
510
516
|
this.active_edge_index = edge_index;
|
|
511
517
|
|
|
512
|
-
// Convert global position to the container's local coordinates.
|
|
513
518
|
const local_pos = this.image_editor_context.image_container.toLocal(
|
|
514
519
|
event.global
|
|
515
520
|
);
|
|
516
521
|
this.last_pointer_position = new Point(local_pos.x, local_pos.y);
|
|
522
|
+
|
|
523
|
+
if (this.image_editor_context.background_image) {
|
|
524
|
+
const bg = this.image_editor_context.background_image;
|
|
525
|
+
this.original_state = {
|
|
526
|
+
width: bg.width,
|
|
527
|
+
height: bg.height,
|
|
528
|
+
x: bg.position.x,
|
|
529
|
+
y: bg.position.y
|
|
530
|
+
};
|
|
531
|
+
}
|
|
517
532
|
}
|
|
518
533
|
|
|
519
534
|
/**
|
|
@@ -530,23 +545,14 @@ export class ResizeTool implements Tool {
|
|
|
530
545
|
original_aspect_ratio: number,
|
|
531
546
|
delta: Point
|
|
532
547
|
): { width: number; height: number } {
|
|
533
|
-
// For corner resizing, use the dimension that the user is actively changing
|
|
534
|
-
// This makes the resize follow the mouse cursor more naturally
|
|
535
|
-
|
|
536
|
-
// Determine which dimension changed more significantly
|
|
537
548
|
const abs_delta_x = Math.abs(delta.x);
|
|
538
549
|
const abs_delta_y = Math.abs(delta.y);
|
|
539
550
|
|
|
540
|
-
// Use a more responsive approach to determine which dimension to prioritize
|
|
541
|
-
// This makes the resize follow the mouse cursor more closely
|
|
542
551
|
if (abs_delta_x > abs_delta_y * 1.2) {
|
|
543
|
-
// User is moving more horizontally, so prioritize width
|
|
544
552
|
new_height = new_width / original_aspect_ratio;
|
|
545
553
|
} else if (abs_delta_y > abs_delta_x * 1.2) {
|
|
546
|
-
// User is moving more vertically, so prioritize height
|
|
547
554
|
new_width = new_height * original_aspect_ratio;
|
|
548
555
|
} else {
|
|
549
|
-
// User is moving diagonally, use the larger delta for better control
|
|
550
556
|
if (new_width / original_aspect_ratio > new_height) {
|
|
551
557
|
new_height = new_width / original_aspect_ratio;
|
|
552
558
|
} else {
|
|
@@ -569,7 +575,6 @@ export class ResizeTool implements Tool {
|
|
|
569
575
|
height: number,
|
|
570
576
|
maintain_aspect_ratio: boolean
|
|
571
577
|
): { width: number; height: number } {
|
|
572
|
-
// Get container dimensions - restrict to container size
|
|
573
578
|
const max_width = this.dimensions.width;
|
|
574
579
|
const max_height = this.dimensions.height;
|
|
575
580
|
|
|
@@ -577,7 +582,6 @@ export class ResizeTool implements Tool {
|
|
|
577
582
|
let new_height = height;
|
|
578
583
|
|
|
579
584
|
if (new_width > max_width) {
|
|
580
|
-
// If maintaining aspect ratio, adjust height proportionally
|
|
581
585
|
if (maintain_aspect_ratio && this.active_corner_index !== -1) {
|
|
582
586
|
new_height = (max_width / new_width) * new_height;
|
|
583
587
|
}
|
|
@@ -585,14 +589,12 @@ export class ResizeTool implements Tool {
|
|
|
585
589
|
}
|
|
586
590
|
|
|
587
591
|
if (new_height > max_height) {
|
|
588
|
-
// If maintaining aspect ratio, adjust width proportionally
|
|
589
592
|
if (maintain_aspect_ratio && this.active_corner_index !== -1) {
|
|
590
593
|
new_width = (max_height / new_height) * new_width;
|
|
591
594
|
}
|
|
592
595
|
new_height = max_height;
|
|
593
596
|
}
|
|
594
597
|
|
|
595
|
-
// Ensure minimum size
|
|
596
598
|
new_width = Math.max(20, new_width);
|
|
597
599
|
new_height = Math.max(20, new_height);
|
|
598
600
|
|
|
@@ -616,15 +618,11 @@ export class ResizeTool implements Tool {
|
|
|
616
618
|
let delta_x = 0;
|
|
617
619
|
let delta_y = 0;
|
|
618
620
|
|
|
619
|
-
// Calculate position deltas based on which handle is being dragged
|
|
620
621
|
if (this.active_corner_index === 0 || this.active_edge_index === 2) {
|
|
621
|
-
// Left side (top-left corner or left edge)
|
|
622
622
|
delta_x = old_width - new_width;
|
|
623
623
|
} else if (this.active_corner_index === 2) {
|
|
624
|
-
// Bottom-left corner should anchor to top-right
|
|
625
|
-
// So we need to adjust both x and y
|
|
626
624
|
delta_x = old_width - new_width;
|
|
627
|
-
delta_y = 0;
|
|
625
|
+
delta_y = 0;
|
|
628
626
|
}
|
|
629
627
|
|
|
630
628
|
if (
|
|
@@ -632,7 +630,6 @@ export class ResizeTool implements Tool {
|
|
|
632
630
|
this.active_corner_index === 1 ||
|
|
633
631
|
this.active_edge_index === 0
|
|
634
632
|
) {
|
|
635
|
-
// Top side (top-left, top-right corners or top edge)
|
|
636
633
|
delta_y = old_height - new_height;
|
|
637
634
|
}
|
|
638
635
|
|
|
@@ -649,55 +646,43 @@ export class ResizeTool implements Tool {
|
|
|
649
646
|
const bg = this.image_editor_context.background_image;
|
|
650
647
|
const image_container = this.image_editor_context.image_container;
|
|
651
648
|
|
|
652
|
-
// Get current position
|
|
653
649
|
let new_x = bg.position.x;
|
|
654
650
|
let new_y = bg.position.y;
|
|
655
651
|
|
|
656
|
-
// Apply boundary constraints to keep the image within the container
|
|
657
652
|
const container_width = image_container.width;
|
|
658
653
|
const container_height = image_container.height;
|
|
659
654
|
const bg_width = bg.width;
|
|
660
655
|
const bg_height = bg.height;
|
|
661
656
|
|
|
662
|
-
// For images larger than the effective container area (container minus border),
|
|
663
|
-
// constrain them to maintain the border region
|
|
664
657
|
const effectiveWidth = container_width - this.borderRegion * 2;
|
|
665
658
|
const effectiveHeight = container_height - this.borderRegion * 2;
|
|
666
659
|
|
|
667
660
|
if (bg_width > effectiveWidth) {
|
|
668
|
-
// Calculate the minimum and maximum allowed positions
|
|
669
661
|
const min_x = -(bg_width - effectiveWidth) + this.borderRegion;
|
|
670
662
|
const max_x = this.borderRegion;
|
|
671
663
|
new_x = Math.max(min_x, Math.min(max_x, new_x));
|
|
672
664
|
} else {
|
|
673
|
-
// If image is smaller than effective container, enforce at least the border region on each side
|
|
674
665
|
const min_x = this.borderRegion;
|
|
675
666
|
const max_x = container_width - bg_width - this.borderRegion;
|
|
676
|
-
// Only constrain if the image would intrude on the border region
|
|
677
667
|
if (new_x < min_x || new_x > max_x) {
|
|
678
668
|
new_x = Math.max(min_x, Math.min(max_x, new_x));
|
|
679
669
|
}
|
|
680
670
|
}
|
|
681
671
|
|
|
682
672
|
if (bg_height > effectiveHeight) {
|
|
683
|
-
// Calculate the minimum and maximum allowed positions
|
|
684
673
|
const min_y = -(bg_height - effectiveHeight) + this.borderRegion;
|
|
685
674
|
const max_y = this.borderRegion;
|
|
686
675
|
new_y = Math.max(min_y, Math.min(max_y, new_y));
|
|
687
676
|
} else {
|
|
688
|
-
// If image is smaller than effective container, enforce at least the border region on each side
|
|
689
677
|
const min_y = this.borderRegion;
|
|
690
678
|
const max_y = container_height - bg_height - this.borderRegion;
|
|
691
|
-
// Only constrain if the image would intrude on the border region
|
|
692
679
|
if (new_y < min_y || new_y > max_y) {
|
|
693
680
|
new_y = Math.max(min_y, Math.min(max_y, new_y));
|
|
694
681
|
}
|
|
695
682
|
}
|
|
696
683
|
|
|
697
|
-
// Apply the constrained position
|
|
698
684
|
bg.position.set(new_x, new_y);
|
|
699
685
|
|
|
700
|
-
// Update resize UI with container position and scaled sprite position
|
|
701
686
|
if (this.resize_ui_container) {
|
|
702
687
|
const total_x = image_container.position.x + new_x * this.scale;
|
|
703
688
|
const total_y = image_container.position.y + new_y * this.scale;
|
|
@@ -714,43 +699,39 @@ export class ResizeTool implements Tool {
|
|
|
714
699
|
delta: Point,
|
|
715
700
|
maintain_aspect_ratio = false
|
|
716
701
|
): void {
|
|
717
|
-
// The delta is already in local coordinates, no need to scale it further
|
|
718
702
|
const background_image = this.image_editor_context.background_image;
|
|
719
703
|
|
|
720
704
|
if (!background_image) return;
|
|
721
705
|
|
|
722
|
-
// Store original dimensions and position
|
|
723
706
|
const original_width = background_image.width;
|
|
724
707
|
const original_height = background_image.height;
|
|
725
708
|
const original_x = background_image.position.x;
|
|
726
709
|
const original_y = background_image.position.y;
|
|
727
710
|
const original_aspect_ratio = original_width / original_height;
|
|
728
711
|
|
|
729
|
-
// Calculate current position based on the delta and the active handle
|
|
730
712
|
let current_position: Point;
|
|
731
713
|
|
|
732
714
|
if (this.active_corner_index !== -1) {
|
|
733
|
-
// For corners, calculate the current position based on the corner and delta
|
|
734
715
|
switch (this.active_corner_index) {
|
|
735
|
-
case 0:
|
|
716
|
+
case 0:
|
|
736
717
|
current_position = new Point(
|
|
737
718
|
original_x - delta.x,
|
|
738
719
|
original_y - delta.y
|
|
739
720
|
);
|
|
740
721
|
break;
|
|
741
|
-
case 1:
|
|
722
|
+
case 1:
|
|
742
723
|
current_position = new Point(
|
|
743
724
|
original_x + original_width + delta.x,
|
|
744
725
|
original_y - delta.y
|
|
745
726
|
);
|
|
746
727
|
break;
|
|
747
|
-
case 2:
|
|
728
|
+
case 2:
|
|
748
729
|
current_position = new Point(
|
|
749
730
|
original_x - delta.x,
|
|
750
731
|
original_y + original_height + delta.y
|
|
751
732
|
);
|
|
752
733
|
break;
|
|
753
|
-
case 3:
|
|
734
|
+
case 3:
|
|
754
735
|
current_position = new Point(
|
|
755
736
|
original_x + original_width + delta.x,
|
|
756
737
|
original_y + original_height + delta.y
|
|
@@ -760,7 +741,6 @@ export class ResizeTool implements Tool {
|
|
|
760
741
|
return;
|
|
761
742
|
}
|
|
762
743
|
|
|
763
|
-
// Use the existing corner resize logic
|
|
764
744
|
const dimensions = this.handle_direct_corner_resize(
|
|
765
745
|
current_position,
|
|
766
746
|
original_width,
|
|
@@ -771,32 +751,30 @@ export class ResizeTool implements Tool {
|
|
|
771
751
|
maintain_aspect_ratio
|
|
772
752
|
);
|
|
773
753
|
|
|
774
|
-
// Apply the new dimensions and position
|
|
775
754
|
background_image.width = dimensions.width;
|
|
776
755
|
background_image.height = dimensions.height;
|
|
777
756
|
background_image.position.set(dimensions.x, dimensions.y);
|
|
778
757
|
} else if (this.active_edge_index !== -1) {
|
|
779
|
-
// For edges, calculate the current position based on the edge and delta
|
|
780
758
|
switch (this.active_edge_index) {
|
|
781
|
-
case 0:
|
|
759
|
+
case 0:
|
|
782
760
|
current_position = new Point(
|
|
783
761
|
original_x + original_width / 2,
|
|
784
762
|
original_y - delta.y
|
|
785
763
|
);
|
|
786
764
|
break;
|
|
787
|
-
case 1:
|
|
765
|
+
case 1:
|
|
788
766
|
current_position = new Point(
|
|
789
767
|
original_x + original_width / 2,
|
|
790
768
|
original_y + original_height + delta.y
|
|
791
769
|
);
|
|
792
770
|
break;
|
|
793
|
-
case 2:
|
|
771
|
+
case 2:
|
|
794
772
|
current_position = new Point(
|
|
795
773
|
original_x - delta.x,
|
|
796
774
|
original_y + original_height / 2
|
|
797
775
|
);
|
|
798
776
|
break;
|
|
799
|
-
case 3:
|
|
777
|
+
case 3:
|
|
800
778
|
current_position = new Point(
|
|
801
779
|
original_x + original_width + delta.x,
|
|
802
780
|
original_y + original_height / 2
|
|
@@ -806,7 +784,6 @@ export class ResizeTool implements Tool {
|
|
|
806
784
|
return;
|
|
807
785
|
}
|
|
808
786
|
|
|
809
|
-
// Use the existing edge resize logic
|
|
810
787
|
const dimensions = this.handle_direct_edge_resize(
|
|
811
788
|
current_position,
|
|
812
789
|
original_width,
|
|
@@ -815,16 +792,11 @@ export class ResizeTool implements Tool {
|
|
|
815
792
|
original_y
|
|
816
793
|
);
|
|
817
794
|
|
|
818
|
-
// Apply the new dimensions and position
|
|
819
795
|
background_image.width = dimensions.width;
|
|
820
796
|
background_image.height = dimensions.height;
|
|
821
797
|
background_image.position.set(dimensions.x, dimensions.y);
|
|
822
798
|
}
|
|
823
799
|
|
|
824
|
-
// We don't apply boundary constraints during resizing
|
|
825
|
-
// This will be done when the user releases the mouse in handle_pointer_up
|
|
826
|
-
|
|
827
|
-
// Update the resize UI to match the new position and dimensions
|
|
828
800
|
this.update_resize_ui();
|
|
829
801
|
}
|
|
830
802
|
|
|
@@ -842,36 +814,28 @@ export class ResizeTool implements Tool {
|
|
|
842
814
|
const background_image = this.image_editor_context.background_image;
|
|
843
815
|
const image_container = this.image_editor_context.image_container;
|
|
844
816
|
|
|
845
|
-
// Get current image container global position
|
|
846
817
|
const container_global_pos = image_container.getGlobalPosition();
|
|
847
818
|
|
|
848
|
-
// Calculate background image position in global coordinates
|
|
849
819
|
const bg_pos_x = background_image.position.x * this.scale;
|
|
850
820
|
const bg_pos_y = background_image.position.y * this.scale;
|
|
851
821
|
|
|
852
|
-
// Position resize UI precisely to match the image position in global space
|
|
853
822
|
this.resize_ui_container.position.set(
|
|
854
823
|
container_global_pos.x + bg_pos_x,
|
|
855
824
|
container_global_pos.y + bg_pos_y
|
|
856
825
|
);
|
|
857
826
|
|
|
858
|
-
// Get the scaled dimensions
|
|
859
827
|
const scaled_width = background_image.width * this.scale;
|
|
860
828
|
const scaled_height = background_image.height * this.scale;
|
|
861
829
|
|
|
862
|
-
// Redraw the outline so it matches the resize_bounds size
|
|
863
830
|
const outline = this.resize_ui_container.getChildAt(0) as Graphics;
|
|
864
831
|
outline
|
|
865
832
|
.clear()
|
|
866
833
|
.rect(0, 0, scaled_width, scaled_height)
|
|
867
834
|
.stroke({ width: 1, color: 0x000000, alignment: 0, alpha: 0.3 });
|
|
868
835
|
|
|
869
|
-
// Redraw the dotted outline
|
|
870
836
|
const dotted_outline = this.resize_ui_container.getChildAt(1) as Graphics;
|
|
871
837
|
dotted_outline.clear();
|
|
872
838
|
|
|
873
|
-
// Use simpler more performant approach for the dotted outline
|
|
874
|
-
// Just draw the solid outline with pixelLine for crisp rendering
|
|
875
839
|
dotted_outline.rect(0, 0, scaled_width, scaled_height).stroke({
|
|
876
840
|
width: 1,
|
|
877
841
|
color: 0x000000,
|
|
@@ -879,27 +843,20 @@ export class ResizeTool implements Tool {
|
|
|
879
843
|
pixelLine: true
|
|
880
844
|
});
|
|
881
845
|
|
|
882
|
-
// Only draw dashes when not actively resizing or moving to improve performance
|
|
883
|
-
// And disable dashes during zoom operations to prevent lag
|
|
884
846
|
if (
|
|
885
847
|
!this.is_dragging &&
|
|
886
848
|
!this.is_moving &&
|
|
887
849
|
Math.abs(this.scale - this.last_scale) < 0.001
|
|
888
850
|
) {
|
|
889
|
-
// Create dashed line effect by drawing small rectangles along the border
|
|
890
851
|
const dash_length = 5;
|
|
891
852
|
const gap_length = 5;
|
|
892
853
|
const total_length = dash_length + gap_length;
|
|
893
854
|
|
|
894
|
-
// Draw dashed horizontal lines (top and bottom) - draw fewer dashes for performance
|
|
895
855
|
for (let x = 0; x < scaled_width; x += total_length * 2) {
|
|
896
|
-
// Skip every other dash
|
|
897
|
-
// Top dash
|
|
898
856
|
dotted_outline
|
|
899
857
|
.rect(x, 0, Math.min(dash_length, scaled_width - x), 1)
|
|
900
858
|
.fill(0x000000);
|
|
901
859
|
|
|
902
|
-
// Bottom dash
|
|
903
860
|
dotted_outline
|
|
904
861
|
.rect(
|
|
905
862
|
x,
|
|
@@ -910,15 +867,11 @@ export class ResizeTool implements Tool {
|
|
|
910
867
|
.fill(0x000000);
|
|
911
868
|
}
|
|
912
869
|
|
|
913
|
-
// Draw dashed vertical lines (left and right) - draw fewer dashes for performance
|
|
914
870
|
for (let y = 0; y < scaled_height; y += total_length * 2) {
|
|
915
|
-
// Skip every other dash
|
|
916
|
-
// Left dash
|
|
917
871
|
dotted_outline
|
|
918
872
|
.rect(0, y, 1, Math.min(dash_length, scaled_height - y))
|
|
919
873
|
.fill(0x000000);
|
|
920
874
|
|
|
921
|
-
// Right dash
|
|
922
875
|
dotted_outline
|
|
923
876
|
.rect(
|
|
924
877
|
scaled_width - 1,
|
|
@@ -930,14 +883,11 @@ export class ResizeTool implements Tool {
|
|
|
930
883
|
}
|
|
931
884
|
}
|
|
932
885
|
|
|
933
|
-
// Store the current scale for comparison on next update
|
|
934
886
|
this.last_scale = this.scale;
|
|
935
887
|
|
|
936
|
-
// Update the move area
|
|
937
888
|
const move_area = this.resize_ui_container.getChildAt(2) as Graphics;
|
|
938
889
|
move_area.clear().rect(0, 0, scaled_width, scaled_height).fill(0xffffff, 0);
|
|
939
890
|
|
|
940
|
-
// Update handle positions to reflect the resized area
|
|
941
891
|
this.update_handle_positions(scaled_width, scaled_height);
|
|
942
892
|
}
|
|
943
893
|
|
|
@@ -949,34 +899,28 @@ export class ResizeTool implements Tool {
|
|
|
949
899
|
private update_handle_positions(width: number, height: number): void {
|
|
950
900
|
if (!this.resize_ui_container) return;
|
|
951
901
|
|
|
952
|
-
// Skip the outline, dotted outline, and move area (index 0, 1, and 2), get corner/edge handles
|
|
953
902
|
const handles = this.resize_ui_container.children.slice(3);
|
|
954
903
|
|
|
955
|
-
// Update the move area size
|
|
956
904
|
const move_area = this.resize_ui_container.getChildAt(2) as Graphics;
|
|
957
905
|
move_area.clear().rect(0, 0, width, height).fill(0xffffff, 0);
|
|
958
906
|
|
|
959
|
-
// First 4 are corners
|
|
960
907
|
const corners = handles.slice(0, 4);
|
|
961
908
|
const cornerPositions = [
|
|
962
|
-
{ x: 0, y: 0 },
|
|
963
|
-
{ x: width, y: 0 },
|
|
964
|
-
{ x: 0, y: height },
|
|
965
|
-
{ x: width, y: height }
|
|
909
|
+
{ x: 0, y: 0 },
|
|
910
|
+
{ x: width, y: 0 },
|
|
911
|
+
{ x: 0, y: height },
|
|
912
|
+
{ x: width, y: height }
|
|
966
913
|
];
|
|
967
914
|
|
|
968
915
|
corners.forEach((handle, i) => {
|
|
969
916
|
handle.position.set(cornerPositions[i].x, cornerPositions[i].y);
|
|
970
917
|
});
|
|
971
918
|
|
|
972
|
-
// Remaining 4 are edges
|
|
973
919
|
const edges = handles.slice(4);
|
|
974
920
|
edges.forEach((handle, i) => {
|
|
975
921
|
if (i < 2) {
|
|
976
|
-
// Top/Bottom edges
|
|
977
922
|
handle.position.set(width / 2, i === 0 ? 0 : height);
|
|
978
923
|
} else {
|
|
979
|
-
// Left/Right edges
|
|
980
924
|
handle.position.set(i === 2 ? 0 : width, height / 2);
|
|
981
925
|
}
|
|
982
926
|
});
|
|
@@ -992,7 +936,6 @@ export class ResizeTool implements Tool {
|
|
|
992
936
|
stage.on("pointerup", this.handle_pointer_up.bind(this));
|
|
993
937
|
stage.on("pointerupoutside", this.handle_pointer_up.bind(this));
|
|
994
938
|
|
|
995
|
-
// Set up direct DOM event listeners for dragging the background image
|
|
996
939
|
this.setup_dom_event_listeners();
|
|
997
940
|
}
|
|
998
941
|
|
|
@@ -1000,113 +943,120 @@ export class ResizeTool implements Tool {
|
|
|
1000
943
|
* Sets up direct DOM event listeners for dragging the background image
|
|
1001
944
|
*/
|
|
1002
945
|
private setup_dom_event_listeners(): void {
|
|
1003
|
-
// Clean up any existing DOM event listeners
|
|
1004
946
|
this.cleanup_dom_event_listeners();
|
|
1005
947
|
|
|
1006
948
|
const canvas = this.image_editor_context.app.canvas;
|
|
1007
949
|
|
|
1008
|
-
// Define the mousedown handler
|
|
1009
950
|
this.dom_mousedown_handler = (e: MouseEvent) => {
|
|
1010
|
-
// Only process if we're in the size subtool
|
|
1011
951
|
if (this.current_subtool !== "size") return;
|
|
1012
952
|
|
|
1013
|
-
// Check if we're already dragging a handle (resize operation)
|
|
1014
953
|
if (this.is_dragging || this.selected_handle) return;
|
|
1015
954
|
|
|
1016
|
-
// Check if we have a background image
|
|
1017
955
|
if (!this.image_editor_context.background_image) return;
|
|
1018
956
|
|
|
1019
957
|
const bg = this.image_editor_context.background_image;
|
|
1020
958
|
|
|
1021
|
-
// Get container and background image bounds
|
|
1022
959
|
const container = this.image_editor_context.image_container;
|
|
1023
960
|
const containerBounds = container.getBounds();
|
|
1024
961
|
|
|
1025
|
-
// Get mouse position relative to the canvas
|
|
1026
962
|
const rect = canvas.getBoundingClientRect();
|
|
1027
963
|
const x = e.clientX - rect.left;
|
|
1028
964
|
const y = e.clientY - rect.top;
|
|
1029
965
|
|
|
1030
|
-
// Convert to local coordinates within the container
|
|
1031
966
|
const localX = (x - containerBounds.x) / this.scale;
|
|
1032
967
|
const localY = (y - containerBounds.y) / this.scale;
|
|
1033
968
|
|
|
1034
|
-
// Check if the click is within the background image bounds using local coordinates
|
|
1035
969
|
if (
|
|
1036
970
|
localX >= bg.position.x &&
|
|
1037
971
|
localX <= bg.position.x + bg.width &&
|
|
1038
972
|
localY >= bg.position.y &&
|
|
1039
973
|
localY <= bg.position.y + bg.height
|
|
1040
974
|
) {
|
|
1041
|
-
// Set the moving flag to true to indicate we're dragging the image
|
|
1042
975
|
this.is_moving = true;
|
|
1043
976
|
|
|
1044
|
-
// Store the initial pointer position in local coordinates
|
|
1045
977
|
this.last_pointer_position = new Point(localX, localY);
|
|
1046
978
|
|
|
1047
|
-
|
|
979
|
+
this.original_state = {
|
|
980
|
+
width: bg.width,
|
|
981
|
+
height: bg.height,
|
|
982
|
+
x: bg.position.x,
|
|
983
|
+
y: bg.position.y
|
|
984
|
+
};
|
|
985
|
+
|
|
1048
986
|
e.preventDefault();
|
|
1049
987
|
e.stopPropagation();
|
|
1050
988
|
}
|
|
1051
989
|
};
|
|
1052
990
|
|
|
1053
|
-
// Define the mousemove handler
|
|
1054
991
|
this.dom_mousemove_handler = (e: MouseEvent) => {
|
|
1055
|
-
// Only process if we're in the size subtool and we're moving the image
|
|
1056
992
|
if (this.current_subtool !== "size" || !this.is_moving) return;
|
|
1057
993
|
|
|
1058
|
-
// Check if we have the necessary state
|
|
1059
994
|
if (
|
|
1060
995
|
!this.last_pointer_position ||
|
|
1061
996
|
!this.image_editor_context.background_image
|
|
1062
997
|
)
|
|
1063
998
|
return;
|
|
1064
999
|
|
|
1065
|
-
// Get container bounds
|
|
1066
1000
|
const container = this.image_editor_context.image_container;
|
|
1067
1001
|
const containerBounds = container.getBounds();
|
|
1068
1002
|
|
|
1069
|
-
// Get mouse position relative to the canvas
|
|
1070
1003
|
const rect = canvas.getBoundingClientRect();
|
|
1071
1004
|
const x = e.clientX - rect.left;
|
|
1072
1005
|
const y = e.clientY - rect.top;
|
|
1073
1006
|
|
|
1074
|
-
// Convert to local coordinates within the container
|
|
1075
1007
|
const localX = (x - containerBounds.x) / this.scale;
|
|
1076
1008
|
const localY = (y - containerBounds.y) / this.scale;
|
|
1077
1009
|
|
|
1078
|
-
// Create a current position point
|
|
1079
1010
|
const current_position = new Point(localX, localY);
|
|
1080
1011
|
|
|
1081
|
-
// Handle image dragging
|
|
1082
1012
|
this.handle_image_dragging(current_position);
|
|
1083
1013
|
|
|
1084
|
-
// Update the last pointer position for continuous dragging
|
|
1085
1014
|
this.last_pointer_position = current_position;
|
|
1086
1015
|
|
|
1087
|
-
// Prevent default behavior and stop propagation
|
|
1088
1016
|
e.preventDefault();
|
|
1089
1017
|
e.stopPropagation();
|
|
1090
1018
|
};
|
|
1091
1019
|
|
|
1092
|
-
// Define the mouseup handler
|
|
1093
1020
|
this.dom_mouseup_handler = (e: MouseEvent) => {
|
|
1094
|
-
|
|
1095
|
-
|
|
1021
|
+
if (this.current_subtool !== "size") return;
|
|
1022
|
+
|
|
1023
|
+
if (this.original_state && this.image_editor_context.background_image) {
|
|
1024
|
+
const bg = this.image_editor_context.background_image;
|
|
1025
|
+
const new_state = {
|
|
1026
|
+
width: bg.width,
|
|
1027
|
+
height: bg.height,
|
|
1028
|
+
x: bg.position.x,
|
|
1029
|
+
y: bg.position.y
|
|
1030
|
+
};
|
|
1031
|
+
|
|
1032
|
+
if (
|
|
1033
|
+
this.original_state.width !== new_state.width ||
|
|
1034
|
+
this.original_state.height !== new_state.height ||
|
|
1035
|
+
this.original_state.x !== new_state.x ||
|
|
1036
|
+
this.original_state.y !== new_state.y
|
|
1037
|
+
) {
|
|
1038
|
+
const resize_command = new ResizeCommand(
|
|
1039
|
+
this.image_editor_context,
|
|
1040
|
+
this.original_state,
|
|
1041
|
+
new_state,
|
|
1042
|
+
this.update_resize_ui.bind(this)
|
|
1043
|
+
);
|
|
1044
|
+
|
|
1045
|
+
this.image_editor_context.execute_command(resize_command);
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
this.original_state = null;
|
|
1049
|
+
}
|
|
1096
1050
|
|
|
1097
|
-
// Reset all state variables
|
|
1098
1051
|
this.is_moving = false;
|
|
1099
1052
|
this.last_pointer_position = null;
|
|
1100
1053
|
|
|
1101
|
-
// Update the resize UI without applying constraints
|
|
1102
1054
|
this.update_resize_ui();
|
|
1103
1055
|
|
|
1104
|
-
// Prevent default behavior and stop propagation
|
|
1105
1056
|
e.preventDefault();
|
|
1106
1057
|
e.stopPropagation();
|
|
1107
1058
|
};
|
|
1108
1059
|
|
|
1109
|
-
// Add the event listeners
|
|
1110
1060
|
canvas.addEventListener("mousedown", this.dom_mousedown_handler);
|
|
1111
1061
|
window.addEventListener("mousemove", this.dom_mousemove_handler);
|
|
1112
1062
|
window.addEventListener("mouseup", this.dom_mouseup_handler);
|
|
@@ -1144,7 +1094,6 @@ export class ResizeTool implements Tool {
|
|
|
1144
1094
|
stage.off("pointerup", this.handle_pointer_up.bind(this));
|
|
1145
1095
|
stage.off("pointerupoutside", this.handle_pointer_up.bind(this));
|
|
1146
1096
|
|
|
1147
|
-
// Clean up DOM event listeners
|
|
1148
1097
|
this.cleanup_dom_event_listeners();
|
|
1149
1098
|
}
|
|
1150
1099
|
|
|
@@ -1154,38 +1103,31 @@ export class ResizeTool implements Tool {
|
|
|
1154
1103
|
private handle_pointer_move(event: FederatedPointerEvent): void {
|
|
1155
1104
|
if (this.current_subtool !== "size") return;
|
|
1156
1105
|
|
|
1157
|
-
// Get the current pointer position in local coordinates
|
|
1158
1106
|
const local_pos = this.image_editor_context.image_container.toLocal(
|
|
1159
1107
|
event.global
|
|
1160
1108
|
);
|
|
1161
1109
|
const current_position = new Point(local_pos.x, local_pos.y);
|
|
1162
1110
|
|
|
1163
1111
|
if (this.is_moving && this.last_pointer_position) {
|
|
1164
|
-
// Handle image dragging
|
|
1165
1112
|
this.handle_image_dragging(current_position);
|
|
1166
1113
|
|
|
1167
|
-
// Update the last pointer position for continuous dragging
|
|
1168
1114
|
this.last_pointer_position = current_position;
|
|
1169
1115
|
} else if (this.is_dragging && this.selected_handle) {
|
|
1170
|
-
// Handle resizing
|
|
1171
1116
|
if (!this.image_editor_context.background_image) return;
|
|
1172
1117
|
|
|
1173
1118
|
const bg = this.image_editor_context.background_image;
|
|
1174
1119
|
const maintain_aspect_ratio =
|
|
1175
1120
|
this.active_corner_index !== -1 && !event.shiftKey;
|
|
1176
1121
|
|
|
1177
|
-
// Get the original dimensions and position
|
|
1178
1122
|
const original_width = bg.width;
|
|
1179
1123
|
const original_height = bg.height;
|
|
1180
1124
|
const original_x = bg.position.x;
|
|
1181
1125
|
const original_y = bg.position.y;
|
|
1182
1126
|
const original_aspect_ratio = original_width / original_height;
|
|
1183
1127
|
|
|
1184
|
-
// Calculate new dimensions and position based on the active handle
|
|
1185
1128
|
let dimensions: { width: number; height: number; x: number; y: number };
|
|
1186
1129
|
|
|
1187
1130
|
if (this.active_corner_index !== -1) {
|
|
1188
|
-
// Handle corner resizing
|
|
1189
1131
|
dimensions = this.handle_direct_corner_resize(
|
|
1190
1132
|
current_position,
|
|
1191
1133
|
original_width,
|
|
@@ -1196,7 +1138,6 @@ export class ResizeTool implements Tool {
|
|
|
1196
1138
|
maintain_aspect_ratio
|
|
1197
1139
|
);
|
|
1198
1140
|
} else if (this.active_edge_index !== -1) {
|
|
1199
|
-
// Handle edge resizing
|
|
1200
1141
|
dimensions = this.handle_direct_edge_resize(
|
|
1201
1142
|
current_position,
|
|
1202
1143
|
original_width,
|
|
@@ -1205,10 +1146,9 @@ export class ResizeTool implements Tool {
|
|
|
1205
1146
|
original_y
|
|
1206
1147
|
);
|
|
1207
1148
|
} else {
|
|
1208
|
-
return;
|
|
1149
|
+
return;
|
|
1209
1150
|
}
|
|
1210
1151
|
|
|
1211
|
-
// Limit dimensions to container size
|
|
1212
1152
|
dimensions = this.limit_dimensions_to_container(
|
|
1213
1153
|
dimensions,
|
|
1214
1154
|
original_x,
|
|
@@ -1218,26 +1158,21 @@ export class ResizeTool implements Tool {
|
|
|
1218
1158
|
maintain_aspect_ratio
|
|
1219
1159
|
);
|
|
1220
1160
|
|
|
1221
|
-
// Apply the new dimensions and position
|
|
1222
1161
|
bg.width = dimensions.width;
|
|
1223
1162
|
bg.height = dimensions.height;
|
|
1224
1163
|
bg.position.set(dimensions.x, dimensions.y);
|
|
1225
1164
|
|
|
1226
|
-
// Update the resize UI
|
|
1227
1165
|
this.update_resize_ui();
|
|
1228
1166
|
|
|
1229
|
-
// Update the last pointer position
|
|
1230
1167
|
this.last_pointer_position = current_position;
|
|
1231
1168
|
}
|
|
1232
1169
|
}
|
|
1233
|
-
|
|
1234
1170
|
/**
|
|
1235
1171
|
* handles pointer up events
|
|
1236
1172
|
*/
|
|
1237
1173
|
private handle_pointer_up(): void {
|
|
1238
1174
|
if (this.current_subtool !== "size") return;
|
|
1239
1175
|
|
|
1240
|
-
// Reset all state variables
|
|
1241
1176
|
this.is_dragging = false;
|
|
1242
1177
|
this.is_moving = false;
|
|
1243
1178
|
this.selected_handle = null;
|
|
@@ -1245,7 +1180,6 @@ export class ResizeTool implements Tool {
|
|
|
1245
1180
|
this.active_corner_index = -1;
|
|
1246
1181
|
this.active_edge_index = -1;
|
|
1247
1182
|
|
|
1248
|
-
// Update the resize UI without applying constraints
|
|
1249
1183
|
this.update_resize_ui();
|
|
1250
1184
|
this.notify("change");
|
|
1251
1185
|
}
|
|
@@ -1263,58 +1197,46 @@ export class ResizeTool implements Tool {
|
|
|
1263
1197
|
return;
|
|
1264
1198
|
}
|
|
1265
1199
|
|
|
1266
|
-
// Get the background image and container
|
|
1267
1200
|
const bg = this.image_editor_context.background_image;
|
|
1268
1201
|
const image_container =
|
|
1269
1202
|
this.image_editor_context.image_container.getLocalBounds();
|
|
1270
1203
|
|
|
1271
|
-
// Calculate the delta in local coordinates
|
|
1272
1204
|
const dx = current_position.x - this.last_pointer_position.x;
|
|
1273
1205
|
const dy = current_position.y - this.last_pointer_position.y;
|
|
1274
1206
|
|
|
1275
|
-
// Update the position directly based on the current position of the background image
|
|
1276
1207
|
let new_x = bg.position.x + dx;
|
|
1277
1208
|
let new_y = bg.position.y + dy;
|
|
1278
1209
|
|
|
1279
|
-
// Apply boundary constraints directly during dragging
|
|
1280
1210
|
const container_width = image_container.width;
|
|
1281
1211
|
const container_height = image_container.height;
|
|
1282
1212
|
const bg_width = bg.width;
|
|
1283
1213
|
const bg_height = bg.height;
|
|
1284
1214
|
|
|
1285
|
-
// Calculate effective dimensions accounting for border region
|
|
1286
1215
|
const effectiveWidth = container_width - this.borderRegion * 2;
|
|
1287
1216
|
const effectiveHeight = container_height - this.borderRegion * 2;
|
|
1288
1217
|
|
|
1289
|
-
// For images larger than the effective container area, constrain to maintain border
|
|
1290
1218
|
if (bg_width > effectiveWidth) {
|
|
1291
|
-
// Calculate the minimum and maximum allowed positions
|
|
1292
1219
|
const min_x = -(bg_width - effectiveWidth) + this.borderRegion;
|
|
1293
1220
|
const max_x = this.borderRegion;
|
|
1294
1221
|
new_x = Math.max(min_x, Math.min(max_x, new_x));
|
|
1295
1222
|
} else {
|
|
1296
|
-
// For smaller images, ensure they maintain the border region
|
|
1297
1223
|
const min_x = this.borderRegion;
|
|
1298
1224
|
const max_x = container_width - bg_width - this.borderRegion;
|
|
1299
1225
|
new_x = Math.max(min_x, Math.min(max_x, new_x));
|
|
1300
1226
|
}
|
|
1301
1227
|
|
|
1302
1228
|
if (bg_height > effectiveHeight) {
|
|
1303
|
-
// Calculate the minimum and maximum allowed positions
|
|
1304
1229
|
const min_y = -(bg_height - effectiveHeight) + this.borderRegion;
|
|
1305
1230
|
const max_y = this.borderRegion;
|
|
1306
1231
|
new_y = Math.max(min_y, Math.min(max_y, new_y));
|
|
1307
1232
|
} else {
|
|
1308
|
-
// For smaller images, ensure they maintain the border region
|
|
1309
1233
|
const min_y = this.borderRegion;
|
|
1310
1234
|
const max_y = container_height - bg_height - this.borderRegion;
|
|
1311
1235
|
new_y = Math.max(min_y, Math.min(max_y, new_y));
|
|
1312
1236
|
}
|
|
1313
1237
|
|
|
1314
|
-
// Apply the new position
|
|
1315
1238
|
bg.position.set(new_x, new_y);
|
|
1316
1239
|
|
|
1317
|
-
// Update the resize UI to match the new position
|
|
1318
1240
|
this.update_resize_ui();
|
|
1319
1241
|
}
|
|
1320
1242
|
|
|
@@ -1339,76 +1261,63 @@ export class ResizeTool implements Tool {
|
|
|
1339
1261
|
original_aspect_ratio: number,
|
|
1340
1262
|
maintain_aspect_ratio: boolean
|
|
1341
1263
|
): { width: number; height: number; x: number; y: number } {
|
|
1342
|
-
// Calculate the raw dimensions based on mouse position
|
|
1343
1264
|
let raw_width = 0;
|
|
1344
1265
|
let raw_height = 0;
|
|
1345
1266
|
let new_x = original_x;
|
|
1346
1267
|
let new_y = original_y;
|
|
1347
1268
|
|
|
1348
|
-
// Calculate raw dimensions based on the active corner
|
|
1349
1269
|
switch (this.active_corner_index) {
|
|
1350
|
-
case 0:
|
|
1270
|
+
case 0:
|
|
1351
1271
|
raw_width = original_x + original_width - current_position.x;
|
|
1352
1272
|
raw_height = original_y + original_height - current_position.y;
|
|
1353
1273
|
break;
|
|
1354
|
-
case 1:
|
|
1274
|
+
case 1:
|
|
1355
1275
|
raw_width = current_position.x - original_x;
|
|
1356
1276
|
raw_height = original_y + original_height - current_position.y;
|
|
1357
1277
|
break;
|
|
1358
|
-
case 2:
|
|
1278
|
+
case 2:
|
|
1359
1279
|
raw_width = original_x + original_width - current_position.x;
|
|
1360
1280
|
raw_height = current_position.y - original_y;
|
|
1361
1281
|
break;
|
|
1362
|
-
case 3:
|
|
1282
|
+
case 3:
|
|
1363
1283
|
raw_width = current_position.x - original_x;
|
|
1364
1284
|
raw_height = current_position.y - original_y;
|
|
1365
1285
|
break;
|
|
1366
1286
|
}
|
|
1367
1287
|
|
|
1368
|
-
// Ensure minimum size
|
|
1369
1288
|
raw_width = Math.max(20, raw_width);
|
|
1370
1289
|
raw_height = Math.max(20, raw_height);
|
|
1371
1290
|
|
|
1372
|
-
// Calculate final dimensions
|
|
1373
1291
|
let new_width = raw_width;
|
|
1374
1292
|
let new_height = raw_height;
|
|
1375
1293
|
|
|
1376
|
-
// Apply aspect ratio if needed
|
|
1377
1294
|
if (maintain_aspect_ratio) {
|
|
1378
|
-
// Use a consistent approach to determine which dimension to prioritize
|
|
1379
|
-
// This prevents jumping between different calculations
|
|
1380
1295
|
const diagonal_vector = {
|
|
1381
1296
|
x: current_position.x - (original_x + original_width / 2),
|
|
1382
1297
|
y: current_position.y - (original_y + original_height / 2)
|
|
1383
1298
|
};
|
|
1384
1299
|
|
|
1385
|
-
// Calculate the angle of movement relative to horizontal
|
|
1386
1300
|
const angle = Math.abs(Math.atan2(diagonal_vector.y, diagonal_vector.x));
|
|
1387
1301
|
|
|
1388
|
-
// If moving more horizontally, prioritize width; otherwise, prioritize height
|
|
1389
1302
|
if (angle < Math.PI / 4 || angle > (3 * Math.PI) / 4) {
|
|
1390
|
-
// Prioritize width (horizontal movement)
|
|
1391
1303
|
new_height = new_width / original_aspect_ratio;
|
|
1392
1304
|
} else {
|
|
1393
|
-
// Prioritize height (vertical movement)
|
|
1394
1305
|
new_width = new_height * original_aspect_ratio;
|
|
1395
1306
|
}
|
|
1396
1307
|
}
|
|
1397
1308
|
|
|
1398
|
-
// Calculate the new position based on the active corner
|
|
1399
1309
|
switch (this.active_corner_index) {
|
|
1400
|
-
case 0:
|
|
1310
|
+
case 0:
|
|
1401
1311
|
new_x = original_x + original_width - new_width;
|
|
1402
1312
|
new_y = original_y + original_height - new_height;
|
|
1403
1313
|
break;
|
|
1404
|
-
case 1:
|
|
1314
|
+
case 1:
|
|
1405
1315
|
new_y = original_y + original_height - new_height;
|
|
1406
1316
|
break;
|
|
1407
|
-
case 2:
|
|
1317
|
+
case 2:
|
|
1408
1318
|
new_x = original_x + original_width - new_width;
|
|
1409
1319
|
break;
|
|
1410
|
-
case 3:
|
|
1411
|
-
// Position stays the same
|
|
1320
|
+
case 3:
|
|
1412
1321
|
break;
|
|
1413
1322
|
}
|
|
1414
1323
|
|
|
@@ -1432,41 +1341,31 @@ export class ResizeTool implements Tool {
|
|
|
1432
1341
|
original_x: number,
|
|
1433
1342
|
original_y: number
|
|
1434
1343
|
): { width: number; height: number; x: number; y: number } {
|
|
1435
|
-
// Calculate raw dimensions based on mouse position
|
|
1436
1344
|
let new_width = original_width;
|
|
1437
1345
|
let new_height = original_height;
|
|
1438
1346
|
let new_x = original_x;
|
|
1439
1347
|
let new_y = original_y;
|
|
1440
1348
|
|
|
1441
|
-
// Handle edge dragging - directly use mouse position for more intuitive control
|
|
1442
1349
|
switch (this.active_edge_index) {
|
|
1443
|
-
case 0:
|
|
1444
|
-
// Calculate height based on distance from bottom edge
|
|
1350
|
+
case 0:
|
|
1445
1351
|
new_height = Math.max(
|
|
1446
1352
|
20,
|
|
1447
1353
|
original_y + original_height - current_position.y
|
|
1448
1354
|
);
|
|
1449
|
-
// Set y position to mouse position
|
|
1450
1355
|
new_y = current_position.y;
|
|
1451
1356
|
break;
|
|
1452
|
-
case 1:
|
|
1453
|
-
// Calculate height based on distance from top edge
|
|
1357
|
+
case 1:
|
|
1454
1358
|
new_height = Math.max(20, current_position.y - original_y);
|
|
1455
|
-
// y position stays the same
|
|
1456
1359
|
break;
|
|
1457
|
-
case 2:
|
|
1458
|
-
// Calculate width based on distance from right edge
|
|
1360
|
+
case 2:
|
|
1459
1361
|
new_width = Math.max(
|
|
1460
1362
|
20,
|
|
1461
1363
|
original_x + original_width - current_position.x
|
|
1462
1364
|
);
|
|
1463
|
-
// Set x position to mouse position
|
|
1464
1365
|
new_x = current_position.x;
|
|
1465
1366
|
break;
|
|
1466
|
-
case 3:
|
|
1467
|
-
// Calculate width based on distance from left edge
|
|
1367
|
+
case 3:
|
|
1468
1368
|
new_width = Math.max(20, current_position.x - original_x);
|
|
1469
|
-
// x position stays the same
|
|
1470
1369
|
break;
|
|
1471
1370
|
}
|
|
1472
1371
|
|
|
@@ -1499,17 +1398,14 @@ export class ResizeTool implements Tool {
|
|
|
1499
1398
|
y: new_y
|
|
1500
1399
|
} = dimensions;
|
|
1501
1400
|
|
|
1502
|
-
// Account for border region when determining maximum dimensions
|
|
1503
1401
|
const effective_container_width =
|
|
1504
1402
|
this.dimensions.width - this.borderRegion * 2;
|
|
1505
1403
|
const effective_container_height =
|
|
1506
1404
|
this.dimensions.height - this.borderRegion * 2;
|
|
1507
1405
|
|
|
1508
|
-
// Store the original dimensions before limiting
|
|
1509
1406
|
const pre_limit_width = new_width;
|
|
1510
1407
|
const pre_limit_height = new_height;
|
|
1511
1408
|
|
|
1512
|
-
// Step 1: Apply container size limits
|
|
1513
1409
|
const size_limited = this.apply_size_limits(
|
|
1514
1410
|
new_width,
|
|
1515
1411
|
new_height,
|
|
@@ -1522,7 +1418,6 @@ export class ResizeTool implements Tool {
|
|
|
1522
1418
|
new_width = size_limited.width;
|
|
1523
1419
|
new_height = size_limited.height;
|
|
1524
1420
|
|
|
1525
|
-
// Step 2: Recalculate position based on active handles
|
|
1526
1421
|
const position_adjusted = this.adjust_position_for_resizing(
|
|
1527
1422
|
new_width,
|
|
1528
1423
|
new_height,
|
|
@@ -1535,7 +1430,6 @@ export class ResizeTool implements Tool {
|
|
|
1535
1430
|
new_x = position_adjusted.x;
|
|
1536
1431
|
new_y = position_adjusted.y;
|
|
1537
1432
|
|
|
1538
|
-
// Step 3: Apply border region constraints
|
|
1539
1433
|
const border_constrained = this.apply_border_constraints(
|
|
1540
1434
|
new_width,
|
|
1541
1435
|
new_height,
|
|
@@ -1563,36 +1457,30 @@ export class ResizeTool implements Tool {
|
|
|
1563
1457
|
let new_width = width;
|
|
1564
1458
|
let new_height = height;
|
|
1565
1459
|
|
|
1566
|
-
// Check if we need to limit dimensions
|
|
1567
1460
|
const needs_width_limit = new_width > max_width;
|
|
1568
1461
|
const needs_height_limit = new_height > max_height;
|
|
1569
1462
|
|
|
1570
|
-
// If both dimensions need limiting, choose the more restrictive one
|
|
1571
1463
|
if (needs_width_limit && needs_height_limit) {
|
|
1572
1464
|
const width_scale = max_width / new_width;
|
|
1573
1465
|
const height_scale = max_height / new_height;
|
|
1574
1466
|
|
|
1575
1467
|
if (width_scale < height_scale) {
|
|
1576
|
-
// Width is more restrictive
|
|
1577
1468
|
new_width = max_width;
|
|
1578
1469
|
if (maintain_aspect_ratio) {
|
|
1579
1470
|
new_height = new_width / aspect_ratio;
|
|
1580
1471
|
}
|
|
1581
1472
|
} else {
|
|
1582
|
-
// Height is more restrictive
|
|
1583
1473
|
new_height = max_height;
|
|
1584
1474
|
if (maintain_aspect_ratio) {
|
|
1585
1475
|
new_width = new_height * aspect_ratio;
|
|
1586
1476
|
}
|
|
1587
1477
|
}
|
|
1588
1478
|
} else if (needs_width_limit) {
|
|
1589
|
-
// Only width needs limiting
|
|
1590
1479
|
new_width = max_width;
|
|
1591
1480
|
if (maintain_aspect_ratio) {
|
|
1592
1481
|
new_height = new_width / aspect_ratio;
|
|
1593
1482
|
}
|
|
1594
1483
|
} else if (needs_height_limit) {
|
|
1595
|
-
// Only height needs limiting
|
|
1596
1484
|
new_height = max_height;
|
|
1597
1485
|
if (maintain_aspect_ratio) {
|
|
1598
1486
|
new_width = new_height * aspect_ratio;
|
|
@@ -1617,30 +1505,27 @@ export class ResizeTool implements Tool {
|
|
|
1617
1505
|
let new_x = original_x;
|
|
1618
1506
|
let new_y = original_y;
|
|
1619
1507
|
|
|
1620
|
-
// Recalculate position based on the active handle and the new dimensions
|
|
1621
1508
|
switch (this.active_corner_index) {
|
|
1622
|
-
case 0:
|
|
1509
|
+
case 0:
|
|
1623
1510
|
new_x = original_x + original_width - new_width;
|
|
1624
1511
|
new_y = original_y + original_height - new_height;
|
|
1625
1512
|
break;
|
|
1626
|
-
case 1:
|
|
1513
|
+
case 1:
|
|
1627
1514
|
new_y = original_y + original_height - new_height;
|
|
1628
1515
|
break;
|
|
1629
|
-
case 2:
|
|
1516
|
+
case 2:
|
|
1630
1517
|
new_x = original_x + original_width - new_width;
|
|
1631
1518
|
break;
|
|
1632
|
-
case 3:
|
|
1633
|
-
// Position stays the same
|
|
1519
|
+
case 3:
|
|
1634
1520
|
break;
|
|
1635
1521
|
}
|
|
1636
1522
|
|
|
1637
|
-
// Handle edge cases
|
|
1638
1523
|
if (this.active_edge_index !== -1) {
|
|
1639
1524
|
switch (this.active_edge_index) {
|
|
1640
|
-
case 0:
|
|
1525
|
+
case 0:
|
|
1641
1526
|
new_y = original_y + original_height - new_height;
|
|
1642
1527
|
break;
|
|
1643
|
-
case 2:
|
|
1528
|
+
case 2:
|
|
1644
1529
|
new_x = original_x + original_width - new_width;
|
|
1645
1530
|
break;
|
|
1646
1531
|
}
|
|
@@ -1661,7 +1546,6 @@ export class ResizeTool implements Tool {
|
|
|
1661
1546
|
maintain_aspect_ratio: boolean,
|
|
1662
1547
|
aspect_ratio: number
|
|
1663
1548
|
): { width: number; height: number; x: number; y: number } {
|
|
1664
|
-
// First apply left and top constraints
|
|
1665
1549
|
const top_left_constrained = this.apply_top_left_constraints(
|
|
1666
1550
|
width,
|
|
1667
1551
|
height,
|
|
@@ -1671,7 +1555,6 @@ export class ResizeTool implements Tool {
|
|
|
1671
1555
|
aspect_ratio
|
|
1672
1556
|
);
|
|
1673
1557
|
|
|
1674
|
-
// Then apply right and bottom constraints
|
|
1675
1558
|
const all_constrained = this.apply_bottom_right_constraints(
|
|
1676
1559
|
top_left_constrained.width,
|
|
1677
1560
|
top_left_constrained.height,
|
|
@@ -1681,7 +1564,6 @@ export class ResizeTool implements Tool {
|
|
|
1681
1564
|
aspect_ratio
|
|
1682
1565
|
);
|
|
1683
1566
|
|
|
1684
|
-
// Ensure minimum dimensions
|
|
1685
1567
|
all_constrained.width = Math.max(20, all_constrained.width);
|
|
1686
1568
|
all_constrained.height = Math.max(20, all_constrained.height);
|
|
1687
1569
|
|
|
@@ -1705,36 +1587,28 @@ export class ResizeTool implements Tool {
|
|
|
1705
1587
|
let new_x = x;
|
|
1706
1588
|
let new_y = y;
|
|
1707
1589
|
|
|
1708
|
-
// Apply left border constraint
|
|
1709
1590
|
if (new_x < this.borderRegion) {
|
|
1710
|
-
// Adjust width if needed when left edge hits border
|
|
1711
1591
|
if (
|
|
1712
1592
|
this.active_corner_index === 0 ||
|
|
1713
1593
|
this.active_corner_index === 2 ||
|
|
1714
1594
|
this.active_edge_index === 2
|
|
1715
1595
|
) {
|
|
1716
|
-
// If resizing from left side, adjust width to maintain the adjusted position
|
|
1717
1596
|
new_width -= this.borderRegion - new_x;
|
|
1718
1597
|
if (maintain_aspect_ratio) {
|
|
1719
|
-
// Recalculate height based on new width
|
|
1720
1598
|
new_height = new_width / aspect_ratio;
|
|
1721
1599
|
}
|
|
1722
1600
|
}
|
|
1723
1601
|
new_x = this.borderRegion;
|
|
1724
1602
|
}
|
|
1725
1603
|
|
|
1726
|
-
// Apply top border constraint
|
|
1727
1604
|
if (new_y < this.borderRegion) {
|
|
1728
|
-
// Adjust height if needed when top edge hits border
|
|
1729
1605
|
if (
|
|
1730
1606
|
this.active_corner_index === 0 ||
|
|
1731
1607
|
this.active_corner_index === 1 ||
|
|
1732
1608
|
this.active_edge_index === 0
|
|
1733
1609
|
) {
|
|
1734
|
-
// If resizing from top side, adjust height to maintain the adjusted position
|
|
1735
1610
|
new_height -= this.borderRegion - new_y;
|
|
1736
1611
|
if (maintain_aspect_ratio) {
|
|
1737
|
-
// Recalculate width based on new height
|
|
1738
1612
|
new_width = new_height * aspect_ratio;
|
|
1739
1613
|
}
|
|
1740
1614
|
}
|
|
@@ -1761,9 +1635,7 @@ export class ResizeTool implements Tool {
|
|
|
1761
1635
|
let new_x = x;
|
|
1762
1636
|
let new_y = y;
|
|
1763
1637
|
|
|
1764
|
-
// Apply right border constraint
|
|
1765
1638
|
if (new_x + new_width > this.dimensions.width - this.borderRegion) {
|
|
1766
|
-
// If resizing from right side, adjust width
|
|
1767
1639
|
if (
|
|
1768
1640
|
this.active_corner_index === 1 ||
|
|
1769
1641
|
this.active_corner_index === 3 ||
|
|
@@ -1774,16 +1646,13 @@ export class ResizeTool implements Tool {
|
|
|
1774
1646
|
new_height = new_width / aspect_ratio;
|
|
1775
1647
|
}
|
|
1776
1648
|
} else {
|
|
1777
|
-
// If resizing from left side, adjust position and preserve width if possible
|
|
1778
1649
|
const right_edge = new_x + new_width;
|
|
1779
1650
|
const excess = right_edge - (this.dimensions.width - this.borderRegion);
|
|
1780
1651
|
new_x = Math.max(this.borderRegion, new_x - excess);
|
|
1781
1652
|
}
|
|
1782
1653
|
}
|
|
1783
1654
|
|
|
1784
|
-
// Apply bottom border constraint
|
|
1785
1655
|
if (new_y + new_height > this.dimensions.height - this.borderRegion) {
|
|
1786
|
-
// If resizing from bottom side, adjust height
|
|
1787
1656
|
if (
|
|
1788
1657
|
this.active_corner_index === 2 ||
|
|
1789
1658
|
this.active_corner_index === 3 ||
|
|
@@ -1794,7 +1663,6 @@ export class ResizeTool implements Tool {
|
|
|
1794
1663
|
new_width = new_height * aspect_ratio;
|
|
1795
1664
|
}
|
|
1796
1665
|
} else {
|
|
1797
|
-
// If resizing from top side, adjust position and preserve height if possible
|
|
1798
1666
|
const bottom_edge = new_y + new_height;
|
|
1799
1667
|
const excess =
|
|
1800
1668
|
bottom_edge - (this.dimensions.height - this.borderRegion);
|
|
@@ -1815,14 +1683,12 @@ export class ResizeTool implements Tool {
|
|
|
1815
1683
|
this.update_resize_ui();
|
|
1816
1684
|
}
|
|
1817
1685
|
|
|
1818
|
-
// Make sure the background image has a cursor indicating it can be moved
|
|
1819
1686
|
if (this.image_editor_context.background_image) {
|
|
1820
1687
|
const bg = this.image_editor_context.background_image;
|
|
1821
1688
|
this.current_cursor = bg.cursor;
|
|
1822
1689
|
bg.cursor = "move";
|
|
1823
1690
|
}
|
|
1824
1691
|
|
|
1825
|
-
// Set up DOM event listeners
|
|
1826
1692
|
this.setup_dom_event_listeners();
|
|
1827
1693
|
}
|
|
1828
1694
|
|
|
@@ -1835,13 +1701,11 @@ export class ResizeTool implements Tool {
|
|
|
1835
1701
|
this.resize_ui_container.visible = false;
|
|
1836
1702
|
}
|
|
1837
1703
|
|
|
1838
|
-
// Reset cursor on background image
|
|
1839
1704
|
if (this.image_editor_context.background_image) {
|
|
1840
1705
|
const bg = this.image_editor_context.background_image;
|
|
1841
1706
|
bg.cursor = this.current_cursor;
|
|
1842
1707
|
}
|
|
1843
1708
|
|
|
1844
|
-
// Clean up DOM event listeners
|
|
1845
1709
|
this.cleanup_dom_event_listeners();
|
|
1846
1710
|
}
|
|
1847
1711
|
|
|
@@ -1872,18 +1736,15 @@ export class ResizeTool implements Tool {
|
|
|
1872
1736
|
public set_border_region(borderRegion: number): void {
|
|
1873
1737
|
this.borderRegion = borderRegion;
|
|
1874
1738
|
|
|
1875
|
-
// Store it on the background image if it exists
|
|
1876
1739
|
if (this.image_editor_context?.background_image) {
|
|
1877
1740
|
(this.image_editor_context.background_image as any).borderRegion =
|
|
1878
1741
|
borderRegion;
|
|
1879
1742
|
}
|
|
1880
1743
|
|
|
1881
|
-
// Update UI if needed
|
|
1882
1744
|
if (this.resize_ui_container) {
|
|
1883
1745
|
this.update_resize_ui();
|
|
1884
1746
|
}
|
|
1885
1747
|
|
|
1886
|
-
// Apply constraints if needed (only if we're in resize mode)
|
|
1887
1748
|
if (
|
|
1888
1749
|
this.current_subtool === "size" &&
|
|
1889
1750
|
this.image_editor_context?.background_image
|