@gradio/imageeditor 0.14.5 → 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 +14 -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
|
@@ -1,6 +1,52 @@
|
|
|
1
1
|
import { Container, Graphics, Point, Rectangle, FederatedPointerEvent } from "pixi.js";
|
|
2
2
|
import {} from "../core/editor";
|
|
3
3
|
import {} from "../Toolbar.svelte";
|
|
4
|
+
import {} from "../core/commands";
|
|
5
|
+
/**
|
|
6
|
+
* Command that handles resizing the image and supports undo/redo
|
|
7
|
+
*/
|
|
8
|
+
export class ResizeCommand {
|
|
9
|
+
context;
|
|
10
|
+
original_width;
|
|
11
|
+
original_height;
|
|
12
|
+
original_x;
|
|
13
|
+
original_y;
|
|
14
|
+
new_width;
|
|
15
|
+
new_height;
|
|
16
|
+
new_x;
|
|
17
|
+
new_y;
|
|
18
|
+
reset_ui;
|
|
19
|
+
name;
|
|
20
|
+
constructor(context, original_state, new_state, reset_ui) {
|
|
21
|
+
this.context = context;
|
|
22
|
+
this.name = "Resize";
|
|
23
|
+
this.original_width = original_state.width;
|
|
24
|
+
this.original_height = original_state.height;
|
|
25
|
+
this.original_x = original_state.x;
|
|
26
|
+
this.original_y = original_state.y;
|
|
27
|
+
this.new_width = new_state.width;
|
|
28
|
+
this.new_height = new_state.height;
|
|
29
|
+
this.new_x = new_state.x;
|
|
30
|
+
this.new_y = new_state.y;
|
|
31
|
+
this.reset_ui = reset_ui;
|
|
32
|
+
}
|
|
33
|
+
async execute() {
|
|
34
|
+
if (!this.context.background_image)
|
|
35
|
+
return;
|
|
36
|
+
this.context.background_image.width = this.new_width;
|
|
37
|
+
this.context.background_image.height = this.new_height;
|
|
38
|
+
this.context.background_image.position.set(this.new_x, this.new_y);
|
|
39
|
+
this.reset_ui();
|
|
40
|
+
}
|
|
41
|
+
async undo() {
|
|
42
|
+
if (!this.context.background_image)
|
|
43
|
+
return;
|
|
44
|
+
this.context.background_image.width = this.original_width;
|
|
45
|
+
this.context.background_image.height = this.original_height;
|
|
46
|
+
this.context.background_image.position.set(this.original_x, this.original_y);
|
|
47
|
+
this.reset_ui();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
4
50
|
/**
|
|
5
51
|
* @class ResizeTool
|
|
6
52
|
* @implements {Tool}
|
|
@@ -12,14 +58,11 @@ export class ResizeTool {
|
|
|
12
58
|
current_tool = "image";
|
|
13
59
|
current_subtool = "size";
|
|
14
60
|
current_cursor = "unset";
|
|
15
|
-
// Constants
|
|
16
61
|
CORNER_SIZE = 25;
|
|
17
62
|
LINE_THICKNESS = 5;
|
|
18
63
|
HANDLE_COLOR = 0x000000;
|
|
19
64
|
HIT_AREA_SIZE = 40;
|
|
20
|
-
// Border region for outpainting
|
|
21
65
|
borderRegion = 0;
|
|
22
|
-
// State
|
|
23
66
|
is_dragging = false;
|
|
24
67
|
selected_handle = null;
|
|
25
68
|
active_corner_index = -1;
|
|
@@ -38,6 +81,7 @@ export class ResizeTool {
|
|
|
38
81
|
dom_mouseup_handler = null;
|
|
39
82
|
event_callbacks = new Map();
|
|
40
83
|
last_scale = 1;
|
|
84
|
+
original_state = null;
|
|
41
85
|
/**
|
|
42
86
|
* @method setup
|
|
43
87
|
* @async
|
|
@@ -51,39 +95,29 @@ export class ResizeTool {
|
|
|
51
95
|
this.image_editor_context = context;
|
|
52
96
|
this.current_tool = tool;
|
|
53
97
|
this.current_subtool = subtool;
|
|
54
|
-
// Get the border region from context if it exists
|
|
55
98
|
if (context.background_image) {
|
|
56
|
-
// First try to read from a custom property on the background image
|
|
57
|
-
// This is important as the position might have been adjusted
|
|
58
99
|
if (context.background_image.borderRegion !== undefined) {
|
|
59
100
|
this.borderRegion = context.background_image.borderRegion;
|
|
60
101
|
}
|
|
61
102
|
else {
|
|
62
|
-
// Fall back to the position-based detection as before
|
|
63
|
-
// But add additional checks to make sure it's not just centering
|
|
64
103
|
const bg = context.background_image;
|
|
65
104
|
const container = context.image_container;
|
|
66
|
-
// Check if the image position seems intentional (not just centered)
|
|
67
105
|
const isHorizontallyCentered = Math.abs(bg.position.x - (container.width - bg.width) / 2) < 2;
|
|
68
106
|
const isVerticallyCentered = Math.abs(bg.position.y - (container.height - bg.height) / 2) < 2;
|
|
69
107
|
if (!isHorizontallyCentered || !isVerticallyCentered) {
|
|
70
|
-
// If not centered, this is likely an intentional border
|
|
71
108
|
this.borderRegion = Math.max(bg.position.x, bg.position.y);
|
|
72
|
-
// Store it for future reference
|
|
73
109
|
context.background_image.borderRegion = this.borderRegion;
|
|
74
110
|
}
|
|
75
111
|
}
|
|
76
112
|
}
|
|
77
113
|
context.dimensions.subscribe((dimensions) => {
|
|
78
114
|
this.dimensions = dimensions;
|
|
79
|
-
// Force UI update if it exists
|
|
80
115
|
if (this.resize_ui_container) {
|
|
81
116
|
this.update_resize_ui();
|
|
82
117
|
}
|
|
83
118
|
});
|
|
84
119
|
context.position.subscribe((position) => {
|
|
85
120
|
this.position = position;
|
|
86
|
-
// Update resize UI immediately when position changes to avoid lag
|
|
87
121
|
if (this.resize_ui_container &&
|
|
88
122
|
this.current_tool === "image" &&
|
|
89
123
|
this.current_subtool === "size") {
|
|
@@ -92,17 +126,14 @@ export class ResizeTool {
|
|
|
92
126
|
});
|
|
93
127
|
context.scale.subscribe((scale) => {
|
|
94
128
|
this.scale = scale;
|
|
95
|
-
// Update resize UI immediately when scale changes to avoid lag
|
|
96
129
|
if (this.resize_ui_container &&
|
|
97
130
|
this.current_tool === "image" &&
|
|
98
131
|
this.current_subtool === "size") {
|
|
99
132
|
this.update_resize_ui();
|
|
100
133
|
}
|
|
101
134
|
});
|
|
102
|
-
// Initialize resize UI
|
|
103
135
|
await this.init_resize_ui();
|
|
104
136
|
this.setup_event_listeners();
|
|
105
|
-
// Apply boundary constraints only if the image is outside the container boundaries
|
|
106
137
|
if (this.current_subtool === "size" &&
|
|
107
138
|
this.image_editor_context.background_image) {
|
|
108
139
|
const bg = this.image_editor_context.background_image;
|
|
@@ -112,7 +143,6 @@ export class ResizeTool {
|
|
|
112
143
|
const container_bounds = container.getLocalBounds();
|
|
113
144
|
const container_width = container_bounds.width;
|
|
114
145
|
const container_height = container_bounds.height;
|
|
115
|
-
// Check if the image exceeds the container boundaries
|
|
116
146
|
const min_x = -(bg_width - container_width);
|
|
117
147
|
const min_y = -(bg_height - container_height);
|
|
118
148
|
const max_x = 0;
|
|
@@ -121,7 +151,6 @@ export class ResizeTool {
|
|
|
121
151
|
(bg.position.x < min_x || bg.position.x > max_x);
|
|
122
152
|
const y_out_of_bounds = bg_height > container_height &&
|
|
123
153
|
(bg.position.y < min_y || bg.position.y > max_y);
|
|
124
|
-
// Only apply constraints if the image is out of bounds
|
|
125
154
|
if (x_out_of_bounds || y_out_of_bounds) {
|
|
126
155
|
this.apply_boundary_constraints();
|
|
127
156
|
}
|
|
@@ -136,7 +165,6 @@ export class ResizeTool {
|
|
|
136
165
|
if (this.resize_ui_container) {
|
|
137
166
|
this.image_editor_context.app.stage.removeChild(this.resize_ui_container);
|
|
138
167
|
}
|
|
139
|
-
// Remove event listeners
|
|
140
168
|
this.cleanup_event_listeners();
|
|
141
169
|
}
|
|
142
170
|
/**
|
|
@@ -150,17 +178,13 @@ export class ResizeTool {
|
|
|
150
178
|
this.current_tool = tool;
|
|
151
179
|
this.current_subtool = subtool;
|
|
152
180
|
if (tool === "image" && subtool === "size") {
|
|
153
|
-
// Check if there's a stored border region on the background image
|
|
154
181
|
if (this.image_editor_context.background_image) {
|
|
155
182
|
const storedBorderRegion = this.image_editor_context.background_image.borderRegion;
|
|
156
183
|
if (typeof storedBorderRegion === "number" && storedBorderRegion > 0) {
|
|
157
|
-
// Use the stored border region
|
|
158
184
|
this.borderRegion = storedBorderRegion;
|
|
159
185
|
}
|
|
160
186
|
}
|
|
161
187
|
this.show_resize_ui();
|
|
162
|
-
// Apply boundary constraints when switching to the resize tool
|
|
163
|
-
// but only if the image is outside the container boundaries
|
|
164
188
|
if (this.image_editor_context.background_image) {
|
|
165
189
|
const bg = this.image_editor_context.background_image;
|
|
166
190
|
const container = this.image_editor_context.image_container;
|
|
@@ -169,10 +193,8 @@ export class ResizeTool {
|
|
|
169
193
|
const container_bounds = container.getLocalBounds();
|
|
170
194
|
const container_width = container_bounds.width;
|
|
171
195
|
const container_height = container_bounds.height;
|
|
172
|
-
// Apply effective width/height calculations with border region
|
|
173
196
|
const effectiveWidth = container_width - this.borderRegion * 2;
|
|
174
197
|
const effectiveHeight = container_height - this.borderRegion * 2;
|
|
175
|
-
// Check if the image exceeds the effective container area or intrudes on border
|
|
176
198
|
const min_x = bg_width > effectiveWidth
|
|
177
199
|
? -(bg_width - effectiveWidth) + this.borderRegion
|
|
178
200
|
: this.borderRegion;
|
|
@@ -185,10 +207,8 @@ export class ResizeTool {
|
|
|
185
207
|
const max_y = bg_height > effectiveHeight
|
|
186
208
|
? this.borderRegion
|
|
187
209
|
: container_height - bg_height - this.borderRegion;
|
|
188
|
-
// Check if any constraints are violated
|
|
189
210
|
const x_out_of_bounds = bg.position.x < min_x || bg.position.x > max_x;
|
|
190
211
|
const y_out_of_bounds = bg.position.y < min_y || bg.position.y > max_y;
|
|
191
|
-
// Only apply constraints if the image is out of bounds
|
|
192
212
|
if (x_out_of_bounds || y_out_of_bounds) {
|
|
193
213
|
this.apply_boundary_constraints();
|
|
194
214
|
}
|
|
@@ -206,16 +226,10 @@ export class ResizeTool {
|
|
|
206
226
|
* @returns {Promise<void>}
|
|
207
227
|
*/
|
|
208
228
|
async init_resize_ui() {
|
|
209
|
-
// Create resize UI with scaled dimensions
|
|
210
229
|
this.resize_ui_container = this.make_resize_ui(this.dimensions.width * this.scale, this.dimensions.height * this.scale);
|
|
211
|
-
// Position the resize UI container at the image position
|
|
212
230
|
this.resize_ui_container.position.set(this.position.x, this.position.y);
|
|
213
|
-
// Initially hide the resize UI until explicitly enabled by set_tool
|
|
214
231
|
this.resize_ui_container.visible = false;
|
|
215
232
|
this.image_editor_context.app.stage.addChild(this.resize_ui_container);
|
|
216
|
-
// Instead of using the ticker for continuous updates, we'll use immediate updates
|
|
217
|
-
// on scale and position changes, which we've added in the setup method
|
|
218
|
-
// This will eliminate lag during zoom operations
|
|
219
233
|
}
|
|
220
234
|
/**
|
|
221
235
|
* @method make_resize_ui
|
|
@@ -229,61 +243,46 @@ export class ResizeTool {
|
|
|
229
243
|
const resize_container = new Container();
|
|
230
244
|
resize_container.eventMode = "static";
|
|
231
245
|
resize_container.interactiveChildren = true;
|
|
232
|
-
// Create solid outline (will be mostly hidden by the dotted outline)
|
|
233
246
|
const outline = new Graphics().rect(0, 0, width, height).stroke({
|
|
234
247
|
width: 1,
|
|
235
248
|
color: 0x000000,
|
|
236
249
|
alignment: 0,
|
|
237
250
|
alpha: 0.3
|
|
238
251
|
});
|
|
239
|
-
// Add a second graphics object for the dotted effect
|
|
240
252
|
const dotted_outline = new Graphics();
|
|
241
|
-
// Use pixelLine for crisp rendering
|
|
242
253
|
dotted_outline.rect(0, 0, width, height).stroke({
|
|
243
254
|
width: 1,
|
|
244
255
|
color: 0x000000,
|
|
245
256
|
alpha: 0.7,
|
|
246
257
|
pixelLine: true
|
|
247
258
|
});
|
|
248
|
-
// Create dashed line effect by drawing small rectangles along the border
|
|
249
259
|
const dash_length = 5;
|
|
250
260
|
const gap_length = 5;
|
|
251
261
|
const total_length = dash_length + gap_length;
|
|
252
|
-
// Draw dashed horizontal lines (top and bottom) - draw fewer dashes for performance
|
|
253
262
|
for (let x = 0; x < width; x += total_length * 2) {
|
|
254
|
-
// Skip every other dash
|
|
255
|
-
// Top dash
|
|
256
263
|
dotted_outline
|
|
257
264
|
.rect(x, 0, Math.min(dash_length, width - x), 1)
|
|
258
265
|
.fill(0x000000);
|
|
259
|
-
// Bottom dash
|
|
260
266
|
dotted_outline
|
|
261
267
|
.rect(x, height - 1, Math.min(dash_length, width - x), 1)
|
|
262
268
|
.fill(0x000000);
|
|
263
269
|
}
|
|
264
|
-
// Draw dashed vertical lines (left and right) - draw fewer dashes for performance
|
|
265
270
|
for (let y = 0; y < height; y += total_length * 2) {
|
|
266
|
-
// Skip every other dash
|
|
267
|
-
// Left dash
|
|
268
271
|
dotted_outline
|
|
269
272
|
.rect(0, y, 1, Math.min(dash_length, height - y))
|
|
270
273
|
.fill(0x000000);
|
|
271
|
-
// Right dash
|
|
272
274
|
dotted_outline
|
|
273
275
|
.rect(width - 1, y, 1, Math.min(dash_length, height - y))
|
|
274
276
|
.fill(0x000000);
|
|
275
277
|
}
|
|
276
278
|
resize_container.addChild(outline);
|
|
277
279
|
resize_container.addChild(dotted_outline);
|
|
278
|
-
// Create a transparent interactive area for the move cursor
|
|
279
280
|
const move_area = new Graphics()
|
|
280
281
|
.rect(0, 0, width, height)
|
|
281
|
-
.fill(0xffffff, 0);
|
|
282
|
+
.fill(0xffffff, 0);
|
|
282
283
|
move_area.eventMode = "static";
|
|
283
|
-
move_area.cursor = "move";
|
|
284
|
-
// Add the move area before the handles so it doesn't interfere with handle interactions
|
|
284
|
+
move_area.cursor = "move";
|
|
285
285
|
resize_container.addChild(move_area);
|
|
286
|
-
// Create handles
|
|
287
286
|
this.create_corner_handles(resize_container, width, height);
|
|
288
287
|
this.create_edge_handles(resize_container, width, height);
|
|
289
288
|
return resize_container;
|
|
@@ -300,14 +299,12 @@ export class ResizeTool {
|
|
|
300
299
|
handle.eventMode = "static";
|
|
301
300
|
handle.cursor = "pointer";
|
|
302
301
|
const handle_graphics = new Graphics();
|
|
303
|
-
const handle_size = is_edge ? 8 : 10;
|
|
304
|
-
// Create square handles
|
|
302
|
+
const handle_size = is_edge ? 8 : 10;
|
|
305
303
|
handle_graphics
|
|
306
304
|
.rect(-handle_size / 2, -handle_size / 2, handle_size, handle_size)
|
|
307
|
-
.fill(0xffffff)
|
|
308
|
-
.stroke({ width: 1, color: this.HANDLE_COLOR });
|
|
305
|
+
.fill(0xffffff)
|
|
306
|
+
.stroke({ width: 1, color: this.HANDLE_COLOR });
|
|
309
307
|
handle.addChild(handle_graphics);
|
|
310
|
-
// Adjust hit area based on handle type
|
|
311
308
|
const hit_size = is_edge ? this.HIT_AREA_SIZE * 1.5 : this.HIT_AREA_SIZE;
|
|
312
309
|
handle.hitArea = new Rectangle(-hit_size / 2, -hit_size / 2, hit_size, hit_size);
|
|
313
310
|
return handle;
|
|
@@ -322,27 +319,23 @@ export class ResizeTool {
|
|
|
322
319
|
* @returns {void}
|
|
323
320
|
*/
|
|
324
321
|
create_edge_handles(container, width, height) {
|
|
325
|
-
// Horizontal handles at top and bottom
|
|
326
322
|
[-1, 1].forEach((i, index) => {
|
|
327
|
-
const handle = this.create_handle(true);
|
|
323
|
+
const handle = this.create_handle(true);
|
|
328
324
|
handle.rotation = 0;
|
|
329
325
|
handle.on("pointerdown", (event) => {
|
|
330
|
-
this.handle_pointer_down(event, handle, -1, index);
|
|
326
|
+
this.handle_pointer_down(event, handle, -1, index);
|
|
331
327
|
});
|
|
332
|
-
// Center the handle
|
|
333
328
|
handle.x = width / 2;
|
|
334
329
|
handle.y = i < 0 ? 0 : height;
|
|
335
330
|
handle.cursor = "ns-resize";
|
|
336
331
|
container.addChild(handle);
|
|
337
332
|
});
|
|
338
|
-
// Vertical handles at left and right
|
|
339
333
|
[-1, 1].forEach((i, index) => {
|
|
340
|
-
const handle = this.create_handle(true);
|
|
341
|
-
handle.rotation = 0;
|
|
334
|
+
const handle = this.create_handle(true);
|
|
335
|
+
handle.rotation = 0;
|
|
342
336
|
handle.on("pointerdown", (event) => {
|
|
343
|
-
this.handle_pointer_down(event, handle, -1, index + 2);
|
|
337
|
+
this.handle_pointer_down(event, handle, -1, index + 2);
|
|
344
338
|
});
|
|
345
|
-
// Center the handle
|
|
346
339
|
handle.x = i < 0 ? 0 : width;
|
|
347
340
|
handle.y = height / 2;
|
|
348
341
|
handle.cursor = "ew-resize";
|
|
@@ -360,13 +353,13 @@ export class ResizeTool {
|
|
|
360
353
|
*/
|
|
361
354
|
create_corner_handles(container, width, height) {
|
|
362
355
|
const corners = [
|
|
363
|
-
{ x: 0, y: 0, cursor: "nwse-resize" },
|
|
364
|
-
{ x: width, y: 0, cursor: "nesw-resize" },
|
|
365
|
-
{ x: 0, y: height, cursor: "nesw-resize" },
|
|
366
|
-
{ x: width, y: height, cursor: "nwse-resize" }
|
|
356
|
+
{ x: 0, y: 0, cursor: "nwse-resize" },
|
|
357
|
+
{ x: width, y: 0, cursor: "nesw-resize" },
|
|
358
|
+
{ x: 0, y: height, cursor: "nesw-resize" },
|
|
359
|
+
{ x: width, y: height, cursor: "nwse-resize" }
|
|
367
360
|
];
|
|
368
361
|
corners.forEach(({ x, y, cursor }, i) => {
|
|
369
|
-
const corner = this.create_handle(false);
|
|
362
|
+
const corner = this.create_handle(false);
|
|
370
363
|
corner.x = x;
|
|
371
364
|
corner.y = y;
|
|
372
365
|
corner.on("pointerdown", (event) => {
|
|
@@ -387,7 +380,6 @@ export class ResizeTool {
|
|
|
387
380
|
* @returns {void}
|
|
388
381
|
*/
|
|
389
382
|
handle_pointer_down(event, handle, corner_index, edge_index) {
|
|
390
|
-
// Only handle resize events if resize tool is active
|
|
391
383
|
if (this.current_subtool !== "size")
|
|
392
384
|
return;
|
|
393
385
|
event.stopPropagation();
|
|
@@ -395,9 +387,17 @@ export class ResizeTool {
|
|
|
395
387
|
this.selected_handle = handle;
|
|
396
388
|
this.active_corner_index = corner_index;
|
|
397
389
|
this.active_edge_index = edge_index;
|
|
398
|
-
// Convert global position to the container's local coordinates.
|
|
399
390
|
const local_pos = this.image_editor_context.image_container.toLocal(event.global);
|
|
400
391
|
this.last_pointer_position = new Point(local_pos.x, local_pos.y);
|
|
392
|
+
if (this.image_editor_context.background_image) {
|
|
393
|
+
const bg = this.image_editor_context.background_image;
|
|
394
|
+
this.original_state = {
|
|
395
|
+
width: bg.width,
|
|
396
|
+
height: bg.height,
|
|
397
|
+
x: bg.position.x,
|
|
398
|
+
y: bg.position.y
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
401
|
}
|
|
402
402
|
/**
|
|
403
403
|
* Maintains aspect ratio during resizing
|
|
@@ -408,23 +408,15 @@ export class ResizeTool {
|
|
|
408
408
|
* @returns The adjusted dimensions
|
|
409
409
|
*/
|
|
410
410
|
maintain_aspect_ratio(new_width, new_height, original_aspect_ratio, delta) {
|
|
411
|
-
// For corner resizing, use the dimension that the user is actively changing
|
|
412
|
-
// This makes the resize follow the mouse cursor more naturally
|
|
413
|
-
// Determine which dimension changed more significantly
|
|
414
411
|
const abs_delta_x = Math.abs(delta.x);
|
|
415
412
|
const abs_delta_y = Math.abs(delta.y);
|
|
416
|
-
// Use a more responsive approach to determine which dimension to prioritize
|
|
417
|
-
// This makes the resize follow the mouse cursor more closely
|
|
418
413
|
if (abs_delta_x > abs_delta_y * 1.2) {
|
|
419
|
-
// User is moving more horizontally, so prioritize width
|
|
420
414
|
new_height = new_width / original_aspect_ratio;
|
|
421
415
|
}
|
|
422
416
|
else if (abs_delta_y > abs_delta_x * 1.2) {
|
|
423
|
-
// User is moving more vertically, so prioritize height
|
|
424
417
|
new_width = new_height * original_aspect_ratio;
|
|
425
418
|
}
|
|
426
419
|
else {
|
|
427
|
-
// User is moving diagonally, use the larger delta for better control
|
|
428
420
|
if (new_width / original_aspect_ratio > new_height) {
|
|
429
421
|
new_height = new_width / original_aspect_ratio;
|
|
430
422
|
}
|
|
@@ -442,26 +434,22 @@ export class ResizeTool {
|
|
|
442
434
|
* @returns The limited dimensions
|
|
443
435
|
*/
|
|
444
436
|
limit_dimensions(width, height, maintain_aspect_ratio) {
|
|
445
|
-
// Get container dimensions - restrict to container size
|
|
446
437
|
const max_width = this.dimensions.width;
|
|
447
438
|
const max_height = this.dimensions.height;
|
|
448
439
|
let new_width = width;
|
|
449
440
|
let new_height = height;
|
|
450
441
|
if (new_width > max_width) {
|
|
451
|
-
// If maintaining aspect ratio, adjust height proportionally
|
|
452
442
|
if (maintain_aspect_ratio && this.active_corner_index !== -1) {
|
|
453
443
|
new_height = (max_width / new_width) * new_height;
|
|
454
444
|
}
|
|
455
445
|
new_width = max_width;
|
|
456
446
|
}
|
|
457
447
|
if (new_height > max_height) {
|
|
458
|
-
// If maintaining aspect ratio, adjust width proportionally
|
|
459
448
|
if (maintain_aspect_ratio && this.active_corner_index !== -1) {
|
|
460
449
|
new_width = (max_height / new_height) * new_width;
|
|
461
450
|
}
|
|
462
451
|
new_height = max_height;
|
|
463
452
|
}
|
|
464
|
-
// Ensure minimum size
|
|
465
453
|
new_width = Math.max(20, new_width);
|
|
466
454
|
new_height = Math.max(20, new_height);
|
|
467
455
|
return { width: new_width, height: new_height };
|
|
@@ -477,21 +465,16 @@ export class ResizeTool {
|
|
|
477
465
|
calculate_position_deltas(old_width, old_height, new_width, new_height) {
|
|
478
466
|
let delta_x = 0;
|
|
479
467
|
let delta_y = 0;
|
|
480
|
-
// Calculate position deltas based on which handle is being dragged
|
|
481
468
|
if (this.active_corner_index === 0 || this.active_edge_index === 2) {
|
|
482
|
-
// Left side (top-left corner or left edge)
|
|
483
469
|
delta_x = old_width - new_width;
|
|
484
470
|
}
|
|
485
471
|
else if (this.active_corner_index === 2) {
|
|
486
|
-
// Bottom-left corner should anchor to top-right
|
|
487
|
-
// So we need to adjust both x and y
|
|
488
472
|
delta_x = old_width - new_width;
|
|
489
|
-
delta_y = 0;
|
|
473
|
+
delta_y = 0;
|
|
490
474
|
}
|
|
491
475
|
if (this.active_corner_index === 0 ||
|
|
492
476
|
this.active_corner_index === 1 ||
|
|
493
477
|
this.active_edge_index === 0) {
|
|
494
|
-
// Top side (top-left, top-right corners or top edge)
|
|
495
478
|
delta_y = old_height - new_height;
|
|
496
479
|
}
|
|
497
480
|
return { x: delta_x, y: delta_y };
|
|
@@ -505,51 +488,39 @@ export class ResizeTool {
|
|
|
505
488
|
return;
|
|
506
489
|
const bg = this.image_editor_context.background_image;
|
|
507
490
|
const image_container = this.image_editor_context.image_container;
|
|
508
|
-
// Get current position
|
|
509
491
|
let new_x = bg.position.x;
|
|
510
492
|
let new_y = bg.position.y;
|
|
511
|
-
// Apply boundary constraints to keep the image within the container
|
|
512
493
|
const container_width = image_container.width;
|
|
513
494
|
const container_height = image_container.height;
|
|
514
495
|
const bg_width = bg.width;
|
|
515
496
|
const bg_height = bg.height;
|
|
516
|
-
// For images larger than the effective container area (container minus border),
|
|
517
|
-
// constrain them to maintain the border region
|
|
518
497
|
const effectiveWidth = container_width - this.borderRegion * 2;
|
|
519
498
|
const effectiveHeight = container_height - this.borderRegion * 2;
|
|
520
499
|
if (bg_width > effectiveWidth) {
|
|
521
|
-
// Calculate the minimum and maximum allowed positions
|
|
522
500
|
const min_x = -(bg_width - effectiveWidth) + this.borderRegion;
|
|
523
501
|
const max_x = this.borderRegion;
|
|
524
502
|
new_x = Math.max(min_x, Math.min(max_x, new_x));
|
|
525
503
|
}
|
|
526
504
|
else {
|
|
527
|
-
// If image is smaller than effective container, enforce at least the border region on each side
|
|
528
505
|
const min_x = this.borderRegion;
|
|
529
506
|
const max_x = container_width - bg_width - this.borderRegion;
|
|
530
|
-
// Only constrain if the image would intrude on the border region
|
|
531
507
|
if (new_x < min_x || new_x > max_x) {
|
|
532
508
|
new_x = Math.max(min_x, Math.min(max_x, new_x));
|
|
533
509
|
}
|
|
534
510
|
}
|
|
535
511
|
if (bg_height > effectiveHeight) {
|
|
536
|
-
// Calculate the minimum and maximum allowed positions
|
|
537
512
|
const min_y = -(bg_height - effectiveHeight) + this.borderRegion;
|
|
538
513
|
const max_y = this.borderRegion;
|
|
539
514
|
new_y = Math.max(min_y, Math.min(max_y, new_y));
|
|
540
515
|
}
|
|
541
516
|
else {
|
|
542
|
-
// If image is smaller than effective container, enforce at least the border region on each side
|
|
543
517
|
const min_y = this.borderRegion;
|
|
544
518
|
const max_y = container_height - bg_height - this.borderRegion;
|
|
545
|
-
// Only constrain if the image would intrude on the border region
|
|
546
519
|
if (new_y < min_y || new_y > max_y) {
|
|
547
520
|
new_y = Math.max(min_y, Math.min(max_y, new_y));
|
|
548
521
|
}
|
|
549
522
|
}
|
|
550
|
-
// Apply the constrained position
|
|
551
523
|
bg.position.set(new_x, new_y);
|
|
552
|
-
// Update resize UI with container position and scaled sprite position
|
|
553
524
|
if (this.resize_ui_container) {
|
|
554
525
|
const total_x = image_container.position.x + new_x * this.scale;
|
|
555
526
|
const total_y = image_container.position.y + new_y * this.scale;
|
|
@@ -562,71 +533,59 @@ export class ResizeTool {
|
|
|
562
533
|
* @param maintain_aspect_ratio - Whether to maintain the aspect ratio
|
|
563
534
|
*/
|
|
564
535
|
update_resize_bounds(delta, maintain_aspect_ratio = false) {
|
|
565
|
-
// The delta is already in local coordinates, no need to scale it further
|
|
566
536
|
const background_image = this.image_editor_context.background_image;
|
|
567
537
|
if (!background_image)
|
|
568
538
|
return;
|
|
569
|
-
// Store original dimensions and position
|
|
570
539
|
const original_width = background_image.width;
|
|
571
540
|
const original_height = background_image.height;
|
|
572
541
|
const original_x = background_image.position.x;
|
|
573
542
|
const original_y = background_image.position.y;
|
|
574
543
|
const original_aspect_ratio = original_width / original_height;
|
|
575
|
-
// Calculate current position based on the delta and the active handle
|
|
576
544
|
let current_position;
|
|
577
545
|
if (this.active_corner_index !== -1) {
|
|
578
|
-
// For corners, calculate the current position based on the corner and delta
|
|
579
546
|
switch (this.active_corner_index) {
|
|
580
|
-
case 0:
|
|
547
|
+
case 0:
|
|
581
548
|
current_position = new Point(original_x - delta.x, original_y - delta.y);
|
|
582
549
|
break;
|
|
583
|
-
case 1:
|
|
550
|
+
case 1:
|
|
584
551
|
current_position = new Point(original_x + original_width + delta.x, original_y - delta.y);
|
|
585
552
|
break;
|
|
586
|
-
case 2:
|
|
553
|
+
case 2:
|
|
587
554
|
current_position = new Point(original_x - delta.x, original_y + original_height + delta.y);
|
|
588
555
|
break;
|
|
589
|
-
case 3:
|
|
556
|
+
case 3:
|
|
590
557
|
current_position = new Point(original_x + original_width + delta.x, original_y + original_height + delta.y);
|
|
591
558
|
break;
|
|
592
559
|
default:
|
|
593
560
|
return;
|
|
594
561
|
}
|
|
595
|
-
// Use the existing corner resize logic
|
|
596
562
|
const dimensions = this.handle_direct_corner_resize(current_position, original_width, original_height, original_x, original_y, original_aspect_ratio, maintain_aspect_ratio);
|
|
597
|
-
// Apply the new dimensions and position
|
|
598
563
|
background_image.width = dimensions.width;
|
|
599
564
|
background_image.height = dimensions.height;
|
|
600
565
|
background_image.position.set(dimensions.x, dimensions.y);
|
|
601
566
|
}
|
|
602
567
|
else if (this.active_edge_index !== -1) {
|
|
603
|
-
// For edges, calculate the current position based on the edge and delta
|
|
604
568
|
switch (this.active_edge_index) {
|
|
605
|
-
case 0:
|
|
569
|
+
case 0:
|
|
606
570
|
current_position = new Point(original_x + original_width / 2, original_y - delta.y);
|
|
607
571
|
break;
|
|
608
|
-
case 1:
|
|
572
|
+
case 1:
|
|
609
573
|
current_position = new Point(original_x + original_width / 2, original_y + original_height + delta.y);
|
|
610
574
|
break;
|
|
611
|
-
case 2:
|
|
575
|
+
case 2:
|
|
612
576
|
current_position = new Point(original_x - delta.x, original_y + original_height / 2);
|
|
613
577
|
break;
|
|
614
|
-
case 3:
|
|
578
|
+
case 3:
|
|
615
579
|
current_position = new Point(original_x + original_width + delta.x, original_y + original_height / 2);
|
|
616
580
|
break;
|
|
617
581
|
default:
|
|
618
582
|
return;
|
|
619
583
|
}
|
|
620
|
-
// Use the existing edge resize logic
|
|
621
584
|
const dimensions = this.handle_direct_edge_resize(current_position, original_width, original_height, original_x, original_y);
|
|
622
|
-
// Apply the new dimensions and position
|
|
623
585
|
background_image.width = dimensions.width;
|
|
624
586
|
background_image.height = dimensions.height;
|
|
625
587
|
background_image.position.set(dimensions.x, dimensions.y);
|
|
626
588
|
}
|
|
627
|
-
// We don't apply boundary constraints during resizing
|
|
628
|
-
// This will be done when the user releases the mouse in handle_pointer_up
|
|
629
|
-
// Update the resize UI to match the new position and dimensions
|
|
630
589
|
this.update_resize_ui();
|
|
631
590
|
}
|
|
632
591
|
/**
|
|
@@ -639,73 +598,51 @@ export class ResizeTool {
|
|
|
639
598
|
return;
|
|
640
599
|
const background_image = this.image_editor_context.background_image;
|
|
641
600
|
const image_container = this.image_editor_context.image_container;
|
|
642
|
-
// Get current image container global position
|
|
643
601
|
const container_global_pos = image_container.getGlobalPosition();
|
|
644
|
-
// Calculate background image position in global coordinates
|
|
645
602
|
const bg_pos_x = background_image.position.x * this.scale;
|
|
646
603
|
const bg_pos_y = background_image.position.y * this.scale;
|
|
647
|
-
// Position resize UI precisely to match the image position in global space
|
|
648
604
|
this.resize_ui_container.position.set(container_global_pos.x + bg_pos_x, container_global_pos.y + bg_pos_y);
|
|
649
|
-
// Get the scaled dimensions
|
|
650
605
|
const scaled_width = background_image.width * this.scale;
|
|
651
606
|
const scaled_height = background_image.height * this.scale;
|
|
652
|
-
// Redraw the outline so it matches the resize_bounds size
|
|
653
607
|
const outline = this.resize_ui_container.getChildAt(0);
|
|
654
608
|
outline
|
|
655
609
|
.clear()
|
|
656
610
|
.rect(0, 0, scaled_width, scaled_height)
|
|
657
611
|
.stroke({ width: 1, color: 0x000000, alignment: 0, alpha: 0.3 });
|
|
658
|
-
// Redraw the dotted outline
|
|
659
612
|
const dotted_outline = this.resize_ui_container.getChildAt(1);
|
|
660
613
|
dotted_outline.clear();
|
|
661
|
-
// Use simpler more performant approach for the dotted outline
|
|
662
|
-
// Just draw the solid outline with pixelLine for crisp rendering
|
|
663
614
|
dotted_outline.rect(0, 0, scaled_width, scaled_height).stroke({
|
|
664
615
|
width: 1,
|
|
665
616
|
color: 0x000000,
|
|
666
617
|
alpha: 0.7,
|
|
667
618
|
pixelLine: true
|
|
668
619
|
});
|
|
669
|
-
// Only draw dashes when not actively resizing or moving to improve performance
|
|
670
|
-
// And disable dashes during zoom operations to prevent lag
|
|
671
620
|
if (!this.is_dragging &&
|
|
672
621
|
!this.is_moving &&
|
|
673
622
|
Math.abs(this.scale - this.last_scale) < 0.001) {
|
|
674
|
-
// Create dashed line effect by drawing small rectangles along the border
|
|
675
623
|
const dash_length = 5;
|
|
676
624
|
const gap_length = 5;
|
|
677
625
|
const total_length = dash_length + gap_length;
|
|
678
|
-
// Draw dashed horizontal lines (top and bottom) - draw fewer dashes for performance
|
|
679
626
|
for (let x = 0; x < scaled_width; x += total_length * 2) {
|
|
680
|
-
// Skip every other dash
|
|
681
|
-
// Top dash
|
|
682
627
|
dotted_outline
|
|
683
628
|
.rect(x, 0, Math.min(dash_length, scaled_width - x), 1)
|
|
684
629
|
.fill(0x000000);
|
|
685
|
-
// Bottom dash
|
|
686
630
|
dotted_outline
|
|
687
631
|
.rect(x, scaled_height - 1, Math.min(dash_length, scaled_width - x), 1)
|
|
688
632
|
.fill(0x000000);
|
|
689
633
|
}
|
|
690
|
-
// Draw dashed vertical lines (left and right) - draw fewer dashes for performance
|
|
691
634
|
for (let y = 0; y < scaled_height; y += total_length * 2) {
|
|
692
|
-
// Skip every other dash
|
|
693
|
-
// Left dash
|
|
694
635
|
dotted_outline
|
|
695
636
|
.rect(0, y, 1, Math.min(dash_length, scaled_height - y))
|
|
696
637
|
.fill(0x000000);
|
|
697
|
-
// Right dash
|
|
698
638
|
dotted_outline
|
|
699
639
|
.rect(scaled_width - 1, y, 1, Math.min(dash_length, scaled_height - y))
|
|
700
640
|
.fill(0x000000);
|
|
701
641
|
}
|
|
702
642
|
}
|
|
703
|
-
// Store the current scale for comparison on next update
|
|
704
643
|
this.last_scale = this.scale;
|
|
705
|
-
// Update the move area
|
|
706
644
|
const move_area = this.resize_ui_container.getChildAt(2);
|
|
707
645
|
move_area.clear().rect(0, 0, scaled_width, scaled_height).fill(0xffffff, 0);
|
|
708
|
-
// Update handle positions to reflect the resized area
|
|
709
646
|
this.update_handle_positions(scaled_width, scaled_height);
|
|
710
647
|
}
|
|
711
648
|
/**
|
|
@@ -716,31 +653,25 @@ export class ResizeTool {
|
|
|
716
653
|
update_handle_positions(width, height) {
|
|
717
654
|
if (!this.resize_ui_container)
|
|
718
655
|
return;
|
|
719
|
-
// Skip the outline, dotted outline, and move area (index 0, 1, and 2), get corner/edge handles
|
|
720
656
|
const handles = this.resize_ui_container.children.slice(3);
|
|
721
|
-
// Update the move area size
|
|
722
657
|
const move_area = this.resize_ui_container.getChildAt(2);
|
|
723
658
|
move_area.clear().rect(0, 0, width, height).fill(0xffffff, 0);
|
|
724
|
-
// First 4 are corners
|
|
725
659
|
const corners = handles.slice(0, 4);
|
|
726
660
|
const cornerPositions = [
|
|
727
|
-
{ x: 0, y: 0 },
|
|
728
|
-
{ x: width, y: 0 },
|
|
729
|
-
{ x: 0, y: height },
|
|
730
|
-
{ x: width, y: height }
|
|
661
|
+
{ x: 0, y: 0 },
|
|
662
|
+
{ x: width, y: 0 },
|
|
663
|
+
{ x: 0, y: height },
|
|
664
|
+
{ x: width, y: height }
|
|
731
665
|
];
|
|
732
666
|
corners.forEach((handle, i) => {
|
|
733
667
|
handle.position.set(cornerPositions[i].x, cornerPositions[i].y);
|
|
734
668
|
});
|
|
735
|
-
// Remaining 4 are edges
|
|
736
669
|
const edges = handles.slice(4);
|
|
737
670
|
edges.forEach((handle, i) => {
|
|
738
671
|
if (i < 2) {
|
|
739
|
-
// Top/Bottom edges
|
|
740
672
|
handle.position.set(width / 2, i === 0 ? 0 : height);
|
|
741
673
|
}
|
|
742
674
|
else {
|
|
743
|
-
// Left/Right edges
|
|
744
675
|
handle.position.set(i === 2 ? 0 : width, height / 2);
|
|
745
676
|
}
|
|
746
677
|
});
|
|
@@ -754,96 +685,90 @@ export class ResizeTool {
|
|
|
754
685
|
stage.on("pointermove", this.handle_pointer_move.bind(this));
|
|
755
686
|
stage.on("pointerup", this.handle_pointer_up.bind(this));
|
|
756
687
|
stage.on("pointerupoutside", this.handle_pointer_up.bind(this));
|
|
757
|
-
// Set up direct DOM event listeners for dragging the background image
|
|
758
688
|
this.setup_dom_event_listeners();
|
|
759
689
|
}
|
|
760
690
|
/**
|
|
761
691
|
* Sets up direct DOM event listeners for dragging the background image
|
|
762
692
|
*/
|
|
763
693
|
setup_dom_event_listeners() {
|
|
764
|
-
// Clean up any existing DOM event listeners
|
|
765
694
|
this.cleanup_dom_event_listeners();
|
|
766
695
|
const canvas = this.image_editor_context.app.canvas;
|
|
767
|
-
// Define the mousedown handler
|
|
768
696
|
this.dom_mousedown_handler = (e) => {
|
|
769
|
-
// Only process if we're in the size subtool
|
|
770
697
|
if (this.current_subtool !== "size")
|
|
771
698
|
return;
|
|
772
|
-
// Check if we're already dragging a handle (resize operation)
|
|
773
699
|
if (this.is_dragging || this.selected_handle)
|
|
774
700
|
return;
|
|
775
|
-
// Check if we have a background image
|
|
776
701
|
if (!this.image_editor_context.background_image)
|
|
777
702
|
return;
|
|
778
703
|
const bg = this.image_editor_context.background_image;
|
|
779
|
-
// Get container and background image bounds
|
|
780
704
|
const container = this.image_editor_context.image_container;
|
|
781
705
|
const containerBounds = container.getBounds();
|
|
782
|
-
// Get mouse position relative to the canvas
|
|
783
706
|
const rect = canvas.getBoundingClientRect();
|
|
784
707
|
const x = e.clientX - rect.left;
|
|
785
708
|
const y = e.clientY - rect.top;
|
|
786
|
-
// Convert to local coordinates within the container
|
|
787
709
|
const localX = (x - containerBounds.x) / this.scale;
|
|
788
710
|
const localY = (y - containerBounds.y) / this.scale;
|
|
789
|
-
// Check if the click is within the background image bounds using local coordinates
|
|
790
711
|
if (localX >= bg.position.x &&
|
|
791
712
|
localX <= bg.position.x + bg.width &&
|
|
792
713
|
localY >= bg.position.y &&
|
|
793
714
|
localY <= bg.position.y + bg.height) {
|
|
794
|
-
// Set the moving flag to true to indicate we're dragging the image
|
|
795
715
|
this.is_moving = true;
|
|
796
|
-
// Store the initial pointer position in local coordinates
|
|
797
716
|
this.last_pointer_position = new Point(localX, localY);
|
|
798
|
-
|
|
717
|
+
this.original_state = {
|
|
718
|
+
width: bg.width,
|
|
719
|
+
height: bg.height,
|
|
720
|
+
x: bg.position.x,
|
|
721
|
+
y: bg.position.y
|
|
722
|
+
};
|
|
799
723
|
e.preventDefault();
|
|
800
724
|
e.stopPropagation();
|
|
801
725
|
}
|
|
802
726
|
};
|
|
803
|
-
// Define the mousemove handler
|
|
804
727
|
this.dom_mousemove_handler = (e) => {
|
|
805
|
-
// Only process if we're in the size subtool and we're moving the image
|
|
806
728
|
if (this.current_subtool !== "size" || !this.is_moving)
|
|
807
729
|
return;
|
|
808
|
-
// Check if we have the necessary state
|
|
809
730
|
if (!this.last_pointer_position ||
|
|
810
731
|
!this.image_editor_context.background_image)
|
|
811
732
|
return;
|
|
812
|
-
// Get container bounds
|
|
813
733
|
const container = this.image_editor_context.image_container;
|
|
814
734
|
const containerBounds = container.getBounds();
|
|
815
|
-
// Get mouse position relative to the canvas
|
|
816
735
|
const rect = canvas.getBoundingClientRect();
|
|
817
736
|
const x = e.clientX - rect.left;
|
|
818
737
|
const y = e.clientY - rect.top;
|
|
819
|
-
// Convert to local coordinates within the container
|
|
820
738
|
const localX = (x - containerBounds.x) / this.scale;
|
|
821
739
|
const localY = (y - containerBounds.y) / this.scale;
|
|
822
|
-
// Create a current position point
|
|
823
740
|
const current_position = new Point(localX, localY);
|
|
824
|
-
// Handle image dragging
|
|
825
741
|
this.handle_image_dragging(current_position);
|
|
826
|
-
// Update the last pointer position for continuous dragging
|
|
827
742
|
this.last_pointer_position = current_position;
|
|
828
|
-
// Prevent default behavior and stop propagation
|
|
829
743
|
e.preventDefault();
|
|
830
744
|
e.stopPropagation();
|
|
831
745
|
};
|
|
832
|
-
// Define the mouseup handler
|
|
833
746
|
this.dom_mouseup_handler = (e) => {
|
|
834
|
-
|
|
835
|
-
if (this.current_subtool !== "size" || !this.is_moving)
|
|
747
|
+
if (this.current_subtool !== "size")
|
|
836
748
|
return;
|
|
837
|
-
|
|
749
|
+
if (this.original_state && this.image_editor_context.background_image) {
|
|
750
|
+
const bg = this.image_editor_context.background_image;
|
|
751
|
+
const new_state = {
|
|
752
|
+
width: bg.width,
|
|
753
|
+
height: bg.height,
|
|
754
|
+
x: bg.position.x,
|
|
755
|
+
y: bg.position.y
|
|
756
|
+
};
|
|
757
|
+
if (this.original_state.width !== new_state.width ||
|
|
758
|
+
this.original_state.height !== new_state.height ||
|
|
759
|
+
this.original_state.x !== new_state.x ||
|
|
760
|
+
this.original_state.y !== new_state.y) {
|
|
761
|
+
const resize_command = new ResizeCommand(this.image_editor_context, this.original_state, new_state, this.update_resize_ui.bind(this));
|
|
762
|
+
this.image_editor_context.execute_command(resize_command);
|
|
763
|
+
}
|
|
764
|
+
this.original_state = null;
|
|
765
|
+
}
|
|
838
766
|
this.is_moving = false;
|
|
839
767
|
this.last_pointer_position = null;
|
|
840
|
-
// Update the resize UI without applying constraints
|
|
841
768
|
this.update_resize_ui();
|
|
842
|
-
// Prevent default behavior and stop propagation
|
|
843
769
|
e.preventDefault();
|
|
844
770
|
e.stopPropagation();
|
|
845
771
|
};
|
|
846
|
-
// Add the event listeners
|
|
847
772
|
canvas.addEventListener("mousedown", this.dom_mousedown_handler);
|
|
848
773
|
window.addEventListener("mousemove", this.dom_mousemove_handler);
|
|
849
774
|
window.addEventListener("mouseup", this.dom_mouseup_handler);
|
|
@@ -874,7 +799,6 @@ export class ResizeTool {
|
|
|
874
799
|
stage.off("pointermove", this.handle_pointer_move.bind(this));
|
|
875
800
|
stage.off("pointerup", this.handle_pointer_up.bind(this));
|
|
876
801
|
stage.off("pointerupoutside", this.handle_pointer_up.bind(this));
|
|
877
|
-
// Clean up DOM event listeners
|
|
878
802
|
this.cleanup_dom_event_listeners();
|
|
879
803
|
}
|
|
880
804
|
/**
|
|
@@ -883,49 +807,37 @@ export class ResizeTool {
|
|
|
883
807
|
handle_pointer_move(event) {
|
|
884
808
|
if (this.current_subtool !== "size")
|
|
885
809
|
return;
|
|
886
|
-
// Get the current pointer position in local coordinates
|
|
887
810
|
const local_pos = this.image_editor_context.image_container.toLocal(event.global);
|
|
888
811
|
const current_position = new Point(local_pos.x, local_pos.y);
|
|
889
812
|
if (this.is_moving && this.last_pointer_position) {
|
|
890
|
-
// Handle image dragging
|
|
891
813
|
this.handle_image_dragging(current_position);
|
|
892
|
-
// Update the last pointer position for continuous dragging
|
|
893
814
|
this.last_pointer_position = current_position;
|
|
894
815
|
}
|
|
895
816
|
else if (this.is_dragging && this.selected_handle) {
|
|
896
|
-
// Handle resizing
|
|
897
817
|
if (!this.image_editor_context.background_image)
|
|
898
818
|
return;
|
|
899
819
|
const bg = this.image_editor_context.background_image;
|
|
900
820
|
const maintain_aspect_ratio = this.active_corner_index !== -1 && !event.shiftKey;
|
|
901
|
-
// Get the original dimensions and position
|
|
902
821
|
const original_width = bg.width;
|
|
903
822
|
const original_height = bg.height;
|
|
904
823
|
const original_x = bg.position.x;
|
|
905
824
|
const original_y = bg.position.y;
|
|
906
825
|
const original_aspect_ratio = original_width / original_height;
|
|
907
|
-
// Calculate new dimensions and position based on the active handle
|
|
908
826
|
let dimensions;
|
|
909
827
|
if (this.active_corner_index !== -1) {
|
|
910
|
-
// Handle corner resizing
|
|
911
828
|
dimensions = this.handle_direct_corner_resize(current_position, original_width, original_height, original_x, original_y, original_aspect_ratio, maintain_aspect_ratio);
|
|
912
829
|
}
|
|
913
830
|
else if (this.active_edge_index !== -1) {
|
|
914
|
-
// Handle edge resizing
|
|
915
831
|
dimensions = this.handle_direct_edge_resize(current_position, original_width, original_height, original_x, original_y);
|
|
916
832
|
}
|
|
917
833
|
else {
|
|
918
|
-
return;
|
|
834
|
+
return;
|
|
919
835
|
}
|
|
920
|
-
// Limit dimensions to container size
|
|
921
836
|
dimensions = this.limit_dimensions_to_container(dimensions, original_x, original_y, original_width, original_height, maintain_aspect_ratio);
|
|
922
|
-
// Apply the new dimensions and position
|
|
923
837
|
bg.width = dimensions.width;
|
|
924
838
|
bg.height = dimensions.height;
|
|
925
839
|
bg.position.set(dimensions.x, dimensions.y);
|
|
926
|
-
// Update the resize UI
|
|
927
840
|
this.update_resize_ui();
|
|
928
|
-
// Update the last pointer position
|
|
929
841
|
this.last_pointer_position = current_position;
|
|
930
842
|
}
|
|
931
843
|
}
|
|
@@ -935,14 +847,12 @@ export class ResizeTool {
|
|
|
935
847
|
handle_pointer_up() {
|
|
936
848
|
if (this.current_subtool !== "size")
|
|
937
849
|
return;
|
|
938
|
-
// Reset all state variables
|
|
939
850
|
this.is_dragging = false;
|
|
940
851
|
this.is_moving = false;
|
|
941
852
|
this.selected_handle = null;
|
|
942
853
|
this.last_pointer_position = null;
|
|
943
854
|
this.active_corner_index = -1;
|
|
944
855
|
this.active_edge_index = -1;
|
|
945
|
-
// Update the resize UI without applying constraints
|
|
946
856
|
this.update_resize_ui();
|
|
947
857
|
this.notify("change");
|
|
948
858
|
}
|
|
@@ -956,51 +866,39 @@ export class ResizeTool {
|
|
|
956
866
|
!this.image_editor_context.background_image) {
|
|
957
867
|
return;
|
|
958
868
|
}
|
|
959
|
-
// Get the background image and container
|
|
960
869
|
const bg = this.image_editor_context.background_image;
|
|
961
870
|
const image_container = this.image_editor_context.image_container.getLocalBounds();
|
|
962
|
-
// Calculate the delta in local coordinates
|
|
963
871
|
const dx = current_position.x - this.last_pointer_position.x;
|
|
964
872
|
const dy = current_position.y - this.last_pointer_position.y;
|
|
965
|
-
// Update the position directly based on the current position of the background image
|
|
966
873
|
let new_x = bg.position.x + dx;
|
|
967
874
|
let new_y = bg.position.y + dy;
|
|
968
|
-
// Apply boundary constraints directly during dragging
|
|
969
875
|
const container_width = image_container.width;
|
|
970
876
|
const container_height = image_container.height;
|
|
971
877
|
const bg_width = bg.width;
|
|
972
878
|
const bg_height = bg.height;
|
|
973
|
-
// Calculate effective dimensions accounting for border region
|
|
974
879
|
const effectiveWidth = container_width - this.borderRegion * 2;
|
|
975
880
|
const effectiveHeight = container_height - this.borderRegion * 2;
|
|
976
|
-
// For images larger than the effective container area, constrain to maintain border
|
|
977
881
|
if (bg_width > effectiveWidth) {
|
|
978
|
-
// Calculate the minimum and maximum allowed positions
|
|
979
882
|
const min_x = -(bg_width - effectiveWidth) + this.borderRegion;
|
|
980
883
|
const max_x = this.borderRegion;
|
|
981
884
|
new_x = Math.max(min_x, Math.min(max_x, new_x));
|
|
982
885
|
}
|
|
983
886
|
else {
|
|
984
|
-
// For smaller images, ensure they maintain the border region
|
|
985
887
|
const min_x = this.borderRegion;
|
|
986
888
|
const max_x = container_width - bg_width - this.borderRegion;
|
|
987
889
|
new_x = Math.max(min_x, Math.min(max_x, new_x));
|
|
988
890
|
}
|
|
989
891
|
if (bg_height > effectiveHeight) {
|
|
990
|
-
// Calculate the minimum and maximum allowed positions
|
|
991
892
|
const min_y = -(bg_height - effectiveHeight) + this.borderRegion;
|
|
992
893
|
const max_y = this.borderRegion;
|
|
993
894
|
new_y = Math.max(min_y, Math.min(max_y, new_y));
|
|
994
895
|
}
|
|
995
896
|
else {
|
|
996
|
-
// For smaller images, ensure they maintain the border region
|
|
997
897
|
const min_y = this.borderRegion;
|
|
998
898
|
const max_y = container_height - bg_height - this.borderRegion;
|
|
999
899
|
new_y = Math.max(min_y, Math.min(max_y, new_y));
|
|
1000
900
|
}
|
|
1001
|
-
// Apply the new position
|
|
1002
901
|
bg.position.set(new_x, new_y);
|
|
1003
|
-
// Update the resize UI to match the new position
|
|
1004
902
|
this.update_resize_ui();
|
|
1005
903
|
}
|
|
1006
904
|
/**
|
|
@@ -1016,70 +914,57 @@ export class ResizeTool {
|
|
|
1016
914
|
* @private
|
|
1017
915
|
*/
|
|
1018
916
|
handle_direct_corner_resize(current_position, original_width, original_height, original_x, original_y, original_aspect_ratio, maintain_aspect_ratio) {
|
|
1019
|
-
// Calculate the raw dimensions based on mouse position
|
|
1020
917
|
let raw_width = 0;
|
|
1021
918
|
let raw_height = 0;
|
|
1022
919
|
let new_x = original_x;
|
|
1023
920
|
let new_y = original_y;
|
|
1024
|
-
// Calculate raw dimensions based on the active corner
|
|
1025
921
|
switch (this.active_corner_index) {
|
|
1026
|
-
case 0:
|
|
922
|
+
case 0:
|
|
1027
923
|
raw_width = original_x + original_width - current_position.x;
|
|
1028
924
|
raw_height = original_y + original_height - current_position.y;
|
|
1029
925
|
break;
|
|
1030
|
-
case 1:
|
|
926
|
+
case 1:
|
|
1031
927
|
raw_width = current_position.x - original_x;
|
|
1032
928
|
raw_height = original_y + original_height - current_position.y;
|
|
1033
929
|
break;
|
|
1034
|
-
case 2:
|
|
930
|
+
case 2:
|
|
1035
931
|
raw_width = original_x + original_width - current_position.x;
|
|
1036
932
|
raw_height = current_position.y - original_y;
|
|
1037
933
|
break;
|
|
1038
|
-
case 3:
|
|
934
|
+
case 3:
|
|
1039
935
|
raw_width = current_position.x - original_x;
|
|
1040
936
|
raw_height = current_position.y - original_y;
|
|
1041
937
|
break;
|
|
1042
938
|
}
|
|
1043
|
-
// Ensure minimum size
|
|
1044
939
|
raw_width = Math.max(20, raw_width);
|
|
1045
940
|
raw_height = Math.max(20, raw_height);
|
|
1046
|
-
// Calculate final dimensions
|
|
1047
941
|
let new_width = raw_width;
|
|
1048
942
|
let new_height = raw_height;
|
|
1049
|
-
// Apply aspect ratio if needed
|
|
1050
943
|
if (maintain_aspect_ratio) {
|
|
1051
|
-
// Use a consistent approach to determine which dimension to prioritize
|
|
1052
|
-
// This prevents jumping between different calculations
|
|
1053
944
|
const diagonal_vector = {
|
|
1054
945
|
x: current_position.x - (original_x + original_width / 2),
|
|
1055
946
|
y: current_position.y - (original_y + original_height / 2)
|
|
1056
947
|
};
|
|
1057
|
-
// Calculate the angle of movement relative to horizontal
|
|
1058
948
|
const angle = Math.abs(Math.atan2(diagonal_vector.y, diagonal_vector.x));
|
|
1059
|
-
// If moving more horizontally, prioritize width; otherwise, prioritize height
|
|
1060
949
|
if (angle < Math.PI / 4 || angle > (3 * Math.PI) / 4) {
|
|
1061
|
-
// Prioritize width (horizontal movement)
|
|
1062
950
|
new_height = new_width / original_aspect_ratio;
|
|
1063
951
|
}
|
|
1064
952
|
else {
|
|
1065
|
-
// Prioritize height (vertical movement)
|
|
1066
953
|
new_width = new_height * original_aspect_ratio;
|
|
1067
954
|
}
|
|
1068
955
|
}
|
|
1069
|
-
// Calculate the new position based on the active corner
|
|
1070
956
|
switch (this.active_corner_index) {
|
|
1071
|
-
case 0:
|
|
957
|
+
case 0:
|
|
1072
958
|
new_x = original_x + original_width - new_width;
|
|
1073
959
|
new_y = original_y + original_height - new_height;
|
|
1074
960
|
break;
|
|
1075
|
-
case 1:
|
|
961
|
+
case 1:
|
|
1076
962
|
new_y = original_y + original_height - new_height;
|
|
1077
963
|
break;
|
|
1078
|
-
case 2:
|
|
964
|
+
case 2:
|
|
1079
965
|
new_x = original_x + original_width - new_width;
|
|
1080
966
|
break;
|
|
1081
|
-
case 3:
|
|
1082
|
-
// Position stays the same
|
|
967
|
+
case 3:
|
|
1083
968
|
break;
|
|
1084
969
|
}
|
|
1085
970
|
return { width: new_width, height: new_height, x: new_x, y: new_y };
|
|
@@ -1095,34 +980,24 @@ export class ResizeTool {
|
|
|
1095
980
|
* @private
|
|
1096
981
|
*/
|
|
1097
982
|
handle_direct_edge_resize(current_position, original_width, original_height, original_x, original_y) {
|
|
1098
|
-
// Calculate raw dimensions based on mouse position
|
|
1099
983
|
let new_width = original_width;
|
|
1100
984
|
let new_height = original_height;
|
|
1101
985
|
let new_x = original_x;
|
|
1102
986
|
let new_y = original_y;
|
|
1103
|
-
// Handle edge dragging - directly use mouse position for more intuitive control
|
|
1104
987
|
switch (this.active_edge_index) {
|
|
1105
|
-
case 0:
|
|
1106
|
-
// Calculate height based on distance from bottom edge
|
|
988
|
+
case 0:
|
|
1107
989
|
new_height = Math.max(20, original_y + original_height - current_position.y);
|
|
1108
|
-
// Set y position to mouse position
|
|
1109
990
|
new_y = current_position.y;
|
|
1110
991
|
break;
|
|
1111
|
-
case 1:
|
|
1112
|
-
// Calculate height based on distance from top edge
|
|
992
|
+
case 1:
|
|
1113
993
|
new_height = Math.max(20, current_position.y - original_y);
|
|
1114
|
-
// y position stays the same
|
|
1115
994
|
break;
|
|
1116
|
-
case 2:
|
|
1117
|
-
// Calculate width based on distance from right edge
|
|
995
|
+
case 2:
|
|
1118
996
|
new_width = Math.max(20, original_x + original_width - current_position.x);
|
|
1119
|
-
// Set x position to mouse position
|
|
1120
997
|
new_x = current_position.x;
|
|
1121
998
|
break;
|
|
1122
|
-
case 3:
|
|
1123
|
-
// Calculate width based on distance from left edge
|
|
999
|
+
case 3:
|
|
1124
1000
|
new_width = Math.max(20, current_position.x - original_x);
|
|
1125
|
-
// x position stays the same
|
|
1126
1001
|
break;
|
|
1127
1002
|
}
|
|
1128
1003
|
return { width: new_width, height: new_height, x: new_x, y: new_y };
|
|
@@ -1140,21 +1015,16 @@ export class ResizeTool {
|
|
|
1140
1015
|
*/
|
|
1141
1016
|
limit_dimensions_to_container(dimensions, original_x, original_y, original_width, original_height, maintain_aspect_ratio) {
|
|
1142
1017
|
let { width: new_width, height: new_height, x: new_x, y: new_y } = dimensions;
|
|
1143
|
-
// Account for border region when determining maximum dimensions
|
|
1144
1018
|
const effective_container_width = this.dimensions.width - this.borderRegion * 2;
|
|
1145
1019
|
const effective_container_height = this.dimensions.height - this.borderRegion * 2;
|
|
1146
|
-
// Store the original dimensions before limiting
|
|
1147
1020
|
const pre_limit_width = new_width;
|
|
1148
1021
|
const pre_limit_height = new_height;
|
|
1149
|
-
// Step 1: Apply container size limits
|
|
1150
1022
|
const size_limited = this.apply_size_limits(new_width, new_height, effective_container_width, effective_container_height, maintain_aspect_ratio, pre_limit_width / pre_limit_height);
|
|
1151
1023
|
new_width = size_limited.width;
|
|
1152
1024
|
new_height = size_limited.height;
|
|
1153
|
-
// Step 2: Recalculate position based on active handles
|
|
1154
1025
|
const position_adjusted = this.adjust_position_for_resizing(new_width, new_height, original_x, original_y, original_width, original_height);
|
|
1155
1026
|
new_x = position_adjusted.x;
|
|
1156
1027
|
new_y = position_adjusted.y;
|
|
1157
|
-
// Step 3: Apply border region constraints
|
|
1158
1028
|
const border_constrained = this.apply_border_constraints(new_width, new_height, new_x, new_y, maintain_aspect_ratio, pre_limit_width / pre_limit_height);
|
|
1159
1029
|
return border_constrained;
|
|
1160
1030
|
}
|
|
@@ -1165,22 +1035,18 @@ export class ResizeTool {
|
|
|
1165
1035
|
apply_size_limits(width, height, max_width, max_height, maintain_aspect_ratio, aspect_ratio) {
|
|
1166
1036
|
let new_width = width;
|
|
1167
1037
|
let new_height = height;
|
|
1168
|
-
// Check if we need to limit dimensions
|
|
1169
1038
|
const needs_width_limit = new_width > max_width;
|
|
1170
1039
|
const needs_height_limit = new_height > max_height;
|
|
1171
|
-
// If both dimensions need limiting, choose the more restrictive one
|
|
1172
1040
|
if (needs_width_limit && needs_height_limit) {
|
|
1173
1041
|
const width_scale = max_width / new_width;
|
|
1174
1042
|
const height_scale = max_height / new_height;
|
|
1175
1043
|
if (width_scale < height_scale) {
|
|
1176
|
-
// Width is more restrictive
|
|
1177
1044
|
new_width = max_width;
|
|
1178
1045
|
if (maintain_aspect_ratio) {
|
|
1179
1046
|
new_height = new_width / aspect_ratio;
|
|
1180
1047
|
}
|
|
1181
1048
|
}
|
|
1182
1049
|
else {
|
|
1183
|
-
// Height is more restrictive
|
|
1184
1050
|
new_height = max_height;
|
|
1185
1051
|
if (maintain_aspect_ratio) {
|
|
1186
1052
|
new_width = new_height * aspect_ratio;
|
|
@@ -1188,14 +1054,12 @@ export class ResizeTool {
|
|
|
1188
1054
|
}
|
|
1189
1055
|
}
|
|
1190
1056
|
else if (needs_width_limit) {
|
|
1191
|
-
// Only width needs limiting
|
|
1192
1057
|
new_width = max_width;
|
|
1193
1058
|
if (maintain_aspect_ratio) {
|
|
1194
1059
|
new_height = new_width / aspect_ratio;
|
|
1195
1060
|
}
|
|
1196
1061
|
}
|
|
1197
1062
|
else if (needs_height_limit) {
|
|
1198
|
-
// Only height needs limiting
|
|
1199
1063
|
new_height = max_height;
|
|
1200
1064
|
if (maintain_aspect_ratio) {
|
|
1201
1065
|
new_width = new_height * aspect_ratio;
|
|
@@ -1210,29 +1074,26 @@ export class ResizeTool {
|
|
|
1210
1074
|
adjust_position_for_resizing(new_width, new_height, original_x, original_y, original_width, original_height) {
|
|
1211
1075
|
let new_x = original_x;
|
|
1212
1076
|
let new_y = original_y;
|
|
1213
|
-
// Recalculate position based on the active handle and the new dimensions
|
|
1214
1077
|
switch (this.active_corner_index) {
|
|
1215
|
-
case 0:
|
|
1078
|
+
case 0:
|
|
1216
1079
|
new_x = original_x + original_width - new_width;
|
|
1217
1080
|
new_y = original_y + original_height - new_height;
|
|
1218
1081
|
break;
|
|
1219
|
-
case 1:
|
|
1082
|
+
case 1:
|
|
1220
1083
|
new_y = original_y + original_height - new_height;
|
|
1221
1084
|
break;
|
|
1222
|
-
case 2:
|
|
1085
|
+
case 2:
|
|
1223
1086
|
new_x = original_x + original_width - new_width;
|
|
1224
1087
|
break;
|
|
1225
|
-
case 3:
|
|
1226
|
-
// Position stays the same
|
|
1088
|
+
case 3:
|
|
1227
1089
|
break;
|
|
1228
1090
|
}
|
|
1229
|
-
// Handle edge cases
|
|
1230
1091
|
if (this.active_edge_index !== -1) {
|
|
1231
1092
|
switch (this.active_edge_index) {
|
|
1232
|
-
case 0:
|
|
1093
|
+
case 0:
|
|
1233
1094
|
new_y = original_y + original_height - new_height;
|
|
1234
1095
|
break;
|
|
1235
|
-
case 2:
|
|
1096
|
+
case 2:
|
|
1236
1097
|
new_x = original_x + original_width - new_width;
|
|
1237
1098
|
break;
|
|
1238
1099
|
}
|
|
@@ -1244,11 +1105,8 @@ export class ResizeTool {
|
|
|
1244
1105
|
* @private
|
|
1245
1106
|
*/
|
|
1246
1107
|
apply_border_constraints(width, height, x, y, maintain_aspect_ratio, aspect_ratio) {
|
|
1247
|
-
// First apply left and top constraints
|
|
1248
1108
|
const top_left_constrained = this.apply_top_left_constraints(width, height, x, y, maintain_aspect_ratio, aspect_ratio);
|
|
1249
|
-
// Then apply right and bottom constraints
|
|
1250
1109
|
const all_constrained = this.apply_bottom_right_constraints(top_left_constrained.width, top_left_constrained.height, top_left_constrained.x, top_left_constrained.y, maintain_aspect_ratio, aspect_ratio);
|
|
1251
|
-
// Ensure minimum dimensions
|
|
1252
1110
|
all_constrained.width = Math.max(20, all_constrained.width);
|
|
1253
1111
|
all_constrained.height = Math.max(20, all_constrained.height);
|
|
1254
1112
|
return all_constrained;
|
|
@@ -1262,31 +1120,23 @@ export class ResizeTool {
|
|
|
1262
1120
|
let new_height = height;
|
|
1263
1121
|
let new_x = x;
|
|
1264
1122
|
let new_y = y;
|
|
1265
|
-
// Apply left border constraint
|
|
1266
1123
|
if (new_x < this.borderRegion) {
|
|
1267
|
-
// Adjust width if needed when left edge hits border
|
|
1268
1124
|
if (this.active_corner_index === 0 ||
|
|
1269
1125
|
this.active_corner_index === 2 ||
|
|
1270
1126
|
this.active_edge_index === 2) {
|
|
1271
|
-
// If resizing from left side, adjust width to maintain the adjusted position
|
|
1272
1127
|
new_width -= this.borderRegion - new_x;
|
|
1273
1128
|
if (maintain_aspect_ratio) {
|
|
1274
|
-
// Recalculate height based on new width
|
|
1275
1129
|
new_height = new_width / aspect_ratio;
|
|
1276
1130
|
}
|
|
1277
1131
|
}
|
|
1278
1132
|
new_x = this.borderRegion;
|
|
1279
1133
|
}
|
|
1280
|
-
// Apply top border constraint
|
|
1281
1134
|
if (new_y < this.borderRegion) {
|
|
1282
|
-
// Adjust height if needed when top edge hits border
|
|
1283
1135
|
if (this.active_corner_index === 0 ||
|
|
1284
1136
|
this.active_corner_index === 1 ||
|
|
1285
1137
|
this.active_edge_index === 0) {
|
|
1286
|
-
// If resizing from top side, adjust height to maintain the adjusted position
|
|
1287
1138
|
new_height -= this.borderRegion - new_y;
|
|
1288
1139
|
if (maintain_aspect_ratio) {
|
|
1289
|
-
// Recalculate width based on new height
|
|
1290
1140
|
new_width = new_height * aspect_ratio;
|
|
1291
1141
|
}
|
|
1292
1142
|
}
|
|
@@ -1303,9 +1153,7 @@ export class ResizeTool {
|
|
|
1303
1153
|
let new_height = height;
|
|
1304
1154
|
let new_x = x;
|
|
1305
1155
|
let new_y = y;
|
|
1306
|
-
// Apply right border constraint
|
|
1307
1156
|
if (new_x + new_width > this.dimensions.width - this.borderRegion) {
|
|
1308
|
-
// If resizing from right side, adjust width
|
|
1309
1157
|
if (this.active_corner_index === 1 ||
|
|
1310
1158
|
this.active_corner_index === 3 ||
|
|
1311
1159
|
this.active_edge_index === 3) {
|
|
@@ -1315,15 +1163,12 @@ export class ResizeTool {
|
|
|
1315
1163
|
}
|
|
1316
1164
|
}
|
|
1317
1165
|
else {
|
|
1318
|
-
// If resizing from left side, adjust position and preserve width if possible
|
|
1319
1166
|
const right_edge = new_x + new_width;
|
|
1320
1167
|
const excess = right_edge - (this.dimensions.width - this.borderRegion);
|
|
1321
1168
|
new_x = Math.max(this.borderRegion, new_x - excess);
|
|
1322
1169
|
}
|
|
1323
1170
|
}
|
|
1324
|
-
// Apply bottom border constraint
|
|
1325
1171
|
if (new_y + new_height > this.dimensions.height - this.borderRegion) {
|
|
1326
|
-
// If resizing from bottom side, adjust height
|
|
1327
1172
|
if (this.active_corner_index === 2 ||
|
|
1328
1173
|
this.active_corner_index === 3 ||
|
|
1329
1174
|
this.active_edge_index === 1) {
|
|
@@ -1333,7 +1178,6 @@ export class ResizeTool {
|
|
|
1333
1178
|
}
|
|
1334
1179
|
}
|
|
1335
1180
|
else {
|
|
1336
|
-
// If resizing from top side, adjust position and preserve height if possible
|
|
1337
1181
|
const bottom_edge = new_y + new_height;
|
|
1338
1182
|
const excess = bottom_edge - (this.dimensions.height - this.borderRegion);
|
|
1339
1183
|
new_y = Math.max(this.borderRegion, new_y - excess);
|
|
@@ -1350,13 +1194,11 @@ export class ResizeTool {
|
|
|
1350
1194
|
this.resize_ui_container.visible = true;
|
|
1351
1195
|
this.update_resize_ui();
|
|
1352
1196
|
}
|
|
1353
|
-
// Make sure the background image has a cursor indicating it can be moved
|
|
1354
1197
|
if (this.image_editor_context.background_image) {
|
|
1355
1198
|
const bg = this.image_editor_context.background_image;
|
|
1356
1199
|
this.current_cursor = bg.cursor;
|
|
1357
1200
|
bg.cursor = "move";
|
|
1358
1201
|
}
|
|
1359
|
-
// Set up DOM event listeners
|
|
1360
1202
|
this.setup_dom_event_listeners();
|
|
1361
1203
|
}
|
|
1362
1204
|
/**
|
|
@@ -1367,12 +1209,10 @@ export class ResizeTool {
|
|
|
1367
1209
|
if (this.resize_ui_container) {
|
|
1368
1210
|
this.resize_ui_container.visible = false;
|
|
1369
1211
|
}
|
|
1370
|
-
// Reset cursor on background image
|
|
1371
1212
|
if (this.image_editor_context.background_image) {
|
|
1372
1213
|
const bg = this.image_editor_context.background_image;
|
|
1373
1214
|
bg.cursor = this.current_cursor;
|
|
1374
1215
|
}
|
|
1375
|
-
// Clean up DOM event listeners
|
|
1376
1216
|
this.cleanup_dom_event_listeners();
|
|
1377
1217
|
}
|
|
1378
1218
|
on(event, callback) {
|
|
@@ -1395,16 +1235,13 @@ export class ResizeTool {
|
|
|
1395
1235
|
*/
|
|
1396
1236
|
set_border_region(borderRegion) {
|
|
1397
1237
|
this.borderRegion = borderRegion;
|
|
1398
|
-
// Store it on the background image if it exists
|
|
1399
1238
|
if (this.image_editor_context?.background_image) {
|
|
1400
1239
|
this.image_editor_context.background_image.borderRegion =
|
|
1401
1240
|
borderRegion;
|
|
1402
1241
|
}
|
|
1403
|
-
// Update UI if needed
|
|
1404
1242
|
if (this.resize_ui_container) {
|
|
1405
1243
|
this.update_resize_ui();
|
|
1406
1244
|
}
|
|
1407
|
-
// Apply constraints if needed (only if we're in resize mode)
|
|
1408
1245
|
if (this.current_subtool === "size" &&
|
|
1409
1246
|
this.image_editor_context?.background_image) {
|
|
1410
1247
|
this.apply_boundary_constraints();
|