@@ -42,9 +42,15 @@ var showNormalized = false;
42
42
var modeMessage = document . querySelector ( '#mode' ) ;
43
43
// var coords = document.querySelector('#coords');
44
44
45
+
45
46
var isFullscreen = false ;
46
47
var taskbarAndCanvas = document . querySelector ( '.right' ) ;
47
48
49
+ var editMode = false ;
50
+ var selectedPointIndex = - 1 ;
51
+ var selectedPolygonIndex = - 1 ;
52
+
53
+
48
54
function blitCachedCanvas ( ) {
49
55
mainCtx . clearRect ( 0 , 0 , canvas . width , canvas . height ) ;
50
56
mainCtx . drawImage ( offScreenCanvas , 0 , 0 ) ;
@@ -188,6 +194,28 @@ function getParentPoints() {
188
194
return parentPoints ;
189
195
}
190
196
197
+ function findClosestPoint ( x , y ) {
198
+ let minDist = Infinity ;
199
+ let closestPoint = null ;
200
+ let polygonIndex = - 1 ;
201
+ let pointIndex = - 1 ;
202
+
203
+ for ( let i = 0 ; i < masterPoints . length ; i ++ ) {
204
+ for ( let j = 0 ; j < masterPoints [ i ] . length ; j ++ ) {
205
+ const [ px , py ] = masterPoints [ i ] [ j ] ;
206
+ const dist = Math . sqrt ( ( x - px ) ** 2 + ( y - py ) ** 2 ) ;
207
+ if ( dist < minDist && dist < 10 / scaleFactor ) { // grab radius
208
+ minDist = dist ;
209
+ closestPoint = [ px , py ] ;
210
+ polygonIndex = i ;
211
+ pointIndex = j ;
212
+ }
213
+ }
214
+ }
215
+
216
+ return { point : closestPoint , polygonIndex, pointIndex } ;
217
+ }
218
+
191
219
window . addEventListener ( 'keyup' , function ( e ) {
192
220
if ( e . key === 'Shift' ) {
193
221
constrainAngles = false ;
@@ -221,19 +249,16 @@ canvas.addEventListener('mouseleave', function(e) {
221
249
ycoord . innerHTML = '' ;
222
250
} ) ;
223
251
224
- // on canvas hover, if cursor is crosshair, draw line from last point to cursor
225
252
canvas . addEventListener ( 'mousemove' , function ( e ) {
226
- var x = getScaledCoords ( e ) [ 0 ] ;
227
- var y = getScaledCoords ( e ) [ 1 ] ;
228
- // round
253
+ var [ x , y ] = getScaledCoords ( e ) ;
229
254
x = Math . round ( x ) ;
230
255
y = Math . round ( y ) ;
231
256
232
257
// update x y coords
233
258
var xcoord = document . querySelector ( '#x' ) ;
234
259
var ycoord = document . querySelector ( '#y' ) ;
235
260
236
- if ( constrainAngles ) {
261
+ if ( constrainAngles && points . length > 0 ) {
237
262
var lastPoint = points [ points . length - 1 ] ;
238
263
var dx = x - lastPoint [ 0 ] ;
239
264
var dy = y - lastPoint [ 1 ] ;
@@ -261,6 +286,7 @@ canvas.addEventListener('mousemove', function(e) {
261
286
ctx . lineJoin = 'bevel' ;
262
287
ctx . fillStyle = 'white' ;
263
288
289
+ // if cursor is crosshair, draw line from last point to cursor
264
290
if ( canvas . style . cursor == 'crosshair' ) {
265
291
blitCachedCanvas ( ) ;
266
292
@@ -287,30 +313,32 @@ canvas.addEventListener('mousemove', function(e) {
287
313
drawNode ( ctx , points [ i ] [ 0 ] , points [ i ] [ 1 ] ) ;
288
314
}
289
315
}
316
+
317
+ if ( editMode && selectedPointIndex !== - 1 ) {
318
+ masterPoints [ selectedPolygonIndex ] [ selectedPointIndex ] = [ x , y ] ;
319
+ drawAllPolygons ( offScreenCtx ) ;
320
+ blitCachedCanvas ( ) ;
321
+ writePoints ( getParentPoints ( ) ) ;
322
+ }
290
323
} ) ;
291
324
292
325
canvas . addEventListener ( 'drop' , function ( e ) {
293
326
e . preventDefault ( ) ;
294
327
var file = e . dataTransfer . files [ 0 ] ;
328
+
329
+ // only allow image files
330
+ var supportedImageTypes = [ 'image/png' , 'image/jpeg' , 'image/jpg' , 'image/webp' ] ;
331
+ if ( ! supportedImageTypes . includes ( file . type ) ) {
332
+ alert ( 'Only PNG, JPEG, JPG, and WebP files are allowed.' ) ;
333
+ return ;
334
+ }
335
+
295
336
var reader = new FileReader ( ) ;
296
-
297
337
reader . onload = function ( event ) {
298
- // only allow image files
299
338
img . src = event . target . result ;
300
339
} ;
301
340
reader . readAsDataURL ( file ) ;
302
341
303
- var mime_type = file . type ;
304
-
305
- if (
306
- mime_type != 'image/png' &&
307
- mime_type != 'image/jpeg' &&
308
- mime_type != 'image/jpg'
309
- ) {
310
- alert ( 'Only PNG, JPEG, and JPG files are allowed.' ) ;
311
- return ;
312
- }
313
-
314
342
img . onload = function ( ) {
315
343
scaleFactor = 0.25 ;
316
344
canvas . style . width = img . width * scaleFactor + 'px' ;
@@ -352,7 +380,7 @@ function writePoints(parentPoints) {
352
380
353
381
if ( ! parentPoints . length ) {
354
382
document . querySelector ( '#python' ) . innerHTML = '' ;
355
- document . querySelector ( '#json' ) . innerHTML ;
383
+ document . querySelector ( '#json' ) . innerHTML = '' ;
356
384
return ;
357
385
}
358
386
@@ -372,57 +400,74 @@ function writePoints(parentPoints) {
372
400
document . querySelector ( '#json' ) . innerHTML = json_template ;
373
401
}
374
402
375
- canvas . addEventListener ( 'click' , function ( e ) {
376
- canvas . style . cursor = 'crosshair' ;
377
-
378
- var x = getScaledCoords ( e ) [ 0 ] ;
379
- var y = getScaledCoords ( e ) [ 1 ] ;
403
+ canvas . addEventListener ( 'mousedown' , function ( e ) {
404
+ var [ x , y ] = getScaledCoords ( e ) ;
380
405
x = Math . round ( x ) ;
381
406
y = Math . round ( y ) ;
382
407
383
- if ( constrainAngles ) {
384
- var lastPoint = points [ points . length - 1 ] ;
385
- var dx = x - lastPoint [ 0 ] ;
386
- var dy = y - lastPoint [ 1 ] ;
387
- var angle = Math . atan2 ( dy , dx ) ;
388
- var length = Math . sqrt ( dx * dx + dy * dy ) ;
389
- const snappedAngle = Math . round ( angle / radiansPer45Degrees ) * radiansPer45Degrees ;
390
- var new_x = lastPoint [ 0 ] + length * Math . cos ( snappedAngle ) ;
391
- var new_y = lastPoint [ 1 ] + length * Math . sin ( snappedAngle ) ;
392
- x = Math . round ( new_x ) ;
393
- y = Math . round ( new_y ) ;
394
- }
408
+ if ( editMode ) {
409
+ const { point, polygonIndex, pointIndex } = findClosestPoint ( x , y ) ;
410
+ if ( point ) {
411
+ selectedPointIndex = pointIndex ;
412
+ selectedPolygonIndex = polygonIndex ;
413
+ canvas . style . cursor = 'grabbing' ;
414
+ }
415
+ } else {
416
+ // click handling for drawing mode
417
+ canvas . style . cursor = 'crosshair' ;
418
+
419
+ if ( constrainAngles && points . length > 0 ) {
420
+ var lastPoint = points [ points . length - 1 ] ;
421
+ var dx = x - lastPoint [ 0 ] ;
422
+ var dy = y - lastPoint [ 1 ] ;
423
+ var angle = Math . atan2 ( dy , dx ) ;
424
+ var length = Math . sqrt ( dx * dx + dy * dy ) ;
425
+ const snappedAngle = Math . round ( angle / radiansPer45Degrees ) * radiansPer45Degrees ;
426
+ var new_x = lastPoint [ 0 ] + length * Math . cos ( snappedAngle ) ;
427
+ var new_y = lastPoint [ 1 ] + length * Math . sin ( snappedAngle ) ;
428
+ x = Math . round ( new_x ) ;
429
+ y = Math . round ( new_y ) ;
430
+ }
395
431
396
- if ( points . length > 2 && drawMode == "polygon" ) {
397
- distX = x - points [ 0 ] [ 0 ] ;
398
- distY = y - points [ 0 ] [ 1 ] ;
399
- // stroke is 3px and centered on the circle (i.e. 1/2 * 3px) and arc radius is
400
- if ( Math . sqrt ( distX * distX + distY * distY ) <= 6.5 ) {
401
- onPathClose ( ) ;
402
- return ;
432
+ if ( points . length > 2 && drawMode == "polygon" ) {
433
+ distX = x - points [ 0 ] [ 0 ] ;
434
+ distY = y - points [ 0 ] [ 1 ] ;
435
+ // stroke is 3px and centered on the circle (i.e. 1/2 * 3px) and arc radius is
436
+ if ( Math . sqrt ( distX * distX + distY * distY ) <= 6.5 ) {
437
+ onPathClose ( ) ;
438
+ return ;
439
+ }
403
440
}
404
- }
405
441
406
- points . push ( [ x , y ] ) ;
442
+ points . push ( [ x , y ] ) ;
407
443
408
- drawNode ( mainCtx , x , y , rgb_color ) ;
444
+ drawNode ( mainCtx , x , y , rgb_color ) ;
409
445
410
- if ( drawMode == "line" && points . length == 2 ) {
411
- onPathClose ( ) ;
412
- }
446
+ if ( drawMode == "line" && points . length == 2 ) {
447
+ onPathClose ( ) ;
448
+ }
413
449
414
- // concat all points into one array
415
- var parentPoints = [ ] ;
450
+ // concat all points into one array
451
+ var parentPoints = [ ] ;
416
452
417
- for ( var i = 0 ; i < masterPoints . length ; i ++ ) {
418
- parentPoints . push ( masterPoints [ i ] ) ;
419
- }
420
- // add "points"
421
- if ( points . length > 0 ) {
422
- parentPoints . push ( points ) ;
453
+ for ( var i = 0 ; i < masterPoints . length ; i ++ ) {
454
+ parentPoints . push ( masterPoints [ i ] ) ;
455
+ }
456
+ // add "points"
457
+ if ( points . length > 0 ) {
458
+ parentPoints . push ( points ) ;
459
+ }
460
+
461
+ writePoints ( parentPoints ) ;
423
462
}
463
+ } ) ;
424
464
425
- writePoints ( parentPoints ) ;
465
+ canvas . addEventListener ( 'mouseup' , function ( e ) {
466
+ if ( editMode ) {
467
+ selectedPointIndex = - 1 ;
468
+ selectedPolygonIndex = - 1 ;
469
+ canvas . style . cursor = 'move' ;
470
+ }
426
471
} ) ;
427
472
428
473
document . querySelector ( '#normalize-checkbox' ) . addEventListener ( 'change' , function ( e ) {
@@ -433,11 +478,19 @@ document.querySelector('#normalize-checkbox').addEventListener('change', functio
433
478
434
479
function setDrawMode ( mode ) {
435
480
drawMode = mode ;
481
+ setEditMode ( false ) ;
436
482
canvas . style . cursor = 'crosshair' ;
437
483
document . querySelectorAll ( '.t-mode' ) . forEach ( el => el . classList . remove ( 'active' ) ) ;
438
484
document . querySelector ( `#mode-${ mode } ` ) . classList . add ( 'active' ) ;
439
485
}
440
486
487
+ function setEditMode ( editEnabled ) {
488
+ editMode = editEnabled ;
489
+ canvas . style . cursor = editEnabled ? 'move' : 'crosshair' ;
490
+ document . querySelectorAll ( '.t-mode' ) . forEach ( el => el . classList . remove ( 'active' ) ) ;
491
+ document . querySelector ( `#mode-${ editEnabled ? 'edit' : drawMode } ` ) . classList . add ( 'active' ) ;
492
+ }
493
+
441
494
document . querySelector ( '#mode-polygon' ) . addEventListener ( 'click' , function ( e ) {
442
495
setDrawMode ( 'polygon' ) ;
443
496
} )
@@ -446,14 +499,21 @@ document.querySelector('#mode-line').addEventListener('click', function(e) {
446
499
setDrawMode ( 'line' ) ;
447
500
} )
448
501
502
+ document . querySelector ( '#mode-edit' ) . addEventListener ( 'click' , function ( e ) {
503
+ setEditMode ( true ) ;
504
+ } ) ;
505
+
449
506
document . addEventListener ( 'keydown' , function ( e ) {
450
507
if ( e . key == 'l' || e . key == 'L' ) {
451
508
setDrawMode ( 'line' ) ;
452
509
}
453
510
if ( e . key == 'p' || e . key == 'P' ) {
454
511
setDrawMode ( 'polygon' ) ;
455
512
}
456
- } )
513
+ if ( e . key == 'e' || e . key == 'E' ) {
514
+ setEditMode ( true ) ;
515
+ }
516
+ } ) ;
457
517
458
518
function rewritePoints ( ) {
459
519
var parentPoints = getParentPoints ( ) ;
0 commit comments