@@ -8,6 +8,7 @@ import { Shape as ShapeInterface } from './interfaces'
88import { ShapeType } from './utils/constants'
99import Sidebar from './components/Sidebar'
1010import ThreeJSViewer from './components/ThreeJSViewer'
11+ import { isEmpty } from 'lodash'
1112
1213const Canvas : React . FC = ( ) => {
1314 const pathThickness = 25
@@ -17,15 +18,14 @@ const Canvas: React.FC = () => {
1718 const [ shapes , setShapes ] = useState < ShapeInterface [ ] > ( [ ] )
1819 const [ selectedShape , setSelectedShape ] = useState < ShapeInterface | null > ( null )
1920 const [ shapeType , setShapeType ] = useState < ShapeType > ( 'rectangle' )
20- const [ currentPath , setCurrentPath ] = useState < Shape | null > ( null )
21- const [ pathPoints , setPathPoints ] = useState < { x : number ; y : number } [ ] > ( [ ] )
2221 const [ isDrawing , setIsDrawing ] = useState ( false )
2322 const [ startPoint , setStartPoint ] = useState < { x : number ; y : number } | null > ( null )
2423 const [ currentShape , setCurrentShape ] = useState < Shape | null > ( null )
2524 const [ is3DMode , setIs3DMode ] = useState ( false )
2625 const pathColor = useRef ( getRandomColor ( ) )
2726
2827 const selectedIdRef = useRef < number | undefined > ( undefined )
28+ const pathPointsRef = useRef < { x : number ; y : number } [ ] > ( [ ] )
2929
3030 const toggleViewMode = ( ) => setIs3DMode ( ! is3DMode )
3131
@@ -163,20 +163,33 @@ const Canvas: React.FC = () => {
163163 const g = currentShape . graphics
164164 g . clear ( )
165165
166+ const { x : startX , y : startY } = startPoint
167+
166168 switch ( shapeType ) {
167169 case 'rectangle' :
168170 g . beginFill ( pathColor . current )
169171 . beginStroke ( pathColor . current )
170- . drawRect ( startPoint . x , startPoint . y , x - startPoint . x , y - startPoint . y )
172+ . drawRect ( startX , startY , x - startX , y - startY )
171173 break
172174 case 'circle' : {
173- const radius = Math . sqrt ( Math . pow ( x - startPoint . x , 2 ) + Math . pow ( y - startPoint . y , 2 ) )
174- g . beginFill ( pathColor . current ) . beginStroke ( pathColor . current ) . drawCircle ( startPoint . x , startPoint . y , radius )
175+ const radius = Math . sqrt ( Math . pow ( x - startX , 2 ) + Math . pow ( y - startY , 2 ) )
176+ g . beginFill ( pathColor . current ) . beginStroke ( pathColor . current ) . drawCircle ( startX , startY , radius )
175177 break
176178 }
177179 case 'line' :
178- g . beginStroke ( pathColor . current ) . moveTo ( startPoint . x , startPoint . y ) . lineTo ( x , y )
180+ g . beginStroke ( pathColor . current ) . moveTo ( startX , startY ) . lineTo ( x , y )
179181 break
182+ case 'path' : {
183+ const newPoints = g . beginStroke ( pathColor . current ) . setStrokeStyle ( pathThickness )
184+
185+ if ( isEmpty ( pathPointsRef . current ) ) {
186+ newPoints . moveTo ( startX , startY )
187+ } else {
188+ pathPointsRef . current . forEach ( point => newPoints . lineTo ( point . x , point . y ) )
189+ }
190+
191+ newPoints . lineTo ( x , y )
192+ }
180193 }
181194
182195 stageRef . current ?. update ( )
@@ -233,57 +246,22 @@ const Canvas: React.FC = () => {
233246 stageRef . current ?. update ( )
234247 }
235248
236- const updatePath = ( x : number , y : number ) => {
237- if ( currentPath && pathPoints . length > 0 ) {
238- const g = currentPath . graphics
239- g . clear ( ) . beginStroke ( pathColor . current ) . setStrokeStyle ( pathThickness )
240- g . moveTo ( pathPoints [ 0 ] . x , pathPoints [ 0 ] . y )
241- g . lineTo ( x , y ) // Draw the latest line to the current mouse point
242-
243- stageRef . current ?. update ( ) // Refresh stage
244- }
245- }
246-
247- const endPath = ( x : number , y : number ) => {
248- if ( currentPath && pathPoints . length > 0 ) {
249- const newPoints = [ ...pathPoints , { x, y } ]
250-
251- createShape ( {
252- type : 'path' ,
253- fillColor : 'transparent' ,
254- strokeColor : pathColor . current ,
255- x : 0 ,
256- y : 0 ,
257- points : newPoints ,
258- } )
259-
260- stageRef . current ?. removeChild ( currentPath )
261- setPathPoints ( [ ] )
262- setCurrentPath ( null )
263- setIsDrawing ( false )
264- stageRef . current ?. update ( )
265- }
266- }
267-
268249 // Mouse event handlers
269250 const handleCanvasMouseDown = useCallback (
270251 ( event : React . MouseEvent < HTMLCanvasElement > ) => {
271- pathColor . current = getRandomColor ( )
272-
273252 const { offsetX, offsetY } = event . nativeEvent
253+
254+ if ( shapeType === 'path' ) {
255+ pathPointsRef . current . push ( { x : offsetX , y : offsetY } )
256+ } else {
257+ pathColor . current = getRandomColor ( )
258+ }
259+
274260 const clickedShape = stageRef . current ?. getObjectsUnderPoint ( offsetX , offsetY , 1 ) ?. [ 0 ] as ShapeInterface
275261
276262 if ( clickedShape ) {
277263 selectedIdRef . current = clickedShape ?. id as number
278-
279264 setSelectedShape ( clickedShape )
280- } else if ( shapeType === 'path' ) {
281- if ( ! isDrawing ) {
282- startPath ( offsetX , offsetY )
283- } else {
284- endPath ( offsetX , offsetY )
285- startPath ( offsetX , offsetY )
286- }
287265 } else {
288266 startDrawing ( offsetX , offsetY )
289267 }
@@ -296,11 +274,7 @@ const Canvas: React.FC = () => {
296274 const { offsetX, offsetY } = event . nativeEvent
297275
298276 if ( isDrawing ) {
299- if ( shapeType === 'path' ) {
300- updatePath ( offsetX , offsetY )
301- } else {
302- draw ( offsetX , offsetY )
303- }
277+ draw ( offsetX , offsetY )
304278 }
305279 } ,
306280 [ isDrawing , shapeType ]
@@ -312,7 +286,7 @@ const Canvas: React.FC = () => {
312286
313287 if ( isDrawing ) {
314288 if ( shapeType === 'path' ) {
315- endPath ( offsetX , offsetY )
289+ pathPointsRef . current = [ ... pathPointsRef . current , { x : offsetX , y : offsetY } ]
316290 } else {
317291 endDrawing ( offsetX , offsetY )
318292 }
@@ -339,16 +313,6 @@ const Canvas: React.FC = () => {
339313 [ isDrawing , shapeType ]
340314 )
341315
342- const startPath = ( x : number , y : number ) => {
343- const newPath = new Shape ( )
344- newPath . graphics . beginStroke ( pathColor . current ) . setStrokeStyle ( pathThickness )
345- stageRef . current ?. addChild ( newPath )
346-
347- setCurrentPath ( newPath )
348- setPathPoints ( [ { x, y } ] )
349- setIsDrawing ( true ) // Ensure we are in drawing mode
350- }
351-
352316 // Initialize canvas and attach event handlers
353317 useEffect ( ( ) => {
354318 if ( canvasRef . current ) {
@@ -380,9 +344,8 @@ const Canvas: React.FC = () => {
380344
381345 setShapes ( [ ] )
382346 setSelectedShape ( null )
383- setCurrentPath ( null )
384- setPathPoints ( [ ] )
385347 setIsDrawing ( false )
348+ pathPointsRef . current = [ ]
386349 } , [ ] )
387350
388351 // Keyboard delete, backspace handlers
@@ -411,28 +374,41 @@ const Canvas: React.FC = () => {
411374 event . preventDefault ( )
412375
413376 if ( isDrawing ) {
377+ if ( shapeType === 'path' && Array . isArray ( pathPointsRef . current ) && pathPointsRef . current . length > 1 ) {
378+ const current = {
379+ type : 'path' ,
380+ fillColor : pathColor . current ,
381+ strokeColor : pathColor . current ,
382+ x : 0 ,
383+ y : 0 ,
384+ points : pathPointsRef . current ,
385+ instance : stageRef . current ?. children [ stageRef . current ?. children . length - 1 ] as Shape ,
386+ }
387+
388+ stageRef . current ?. removeChild ( currentShape )
389+ stageRef . current ?. update ( )
390+
391+ createShape ( {
392+ ...current ,
393+ } )
394+
395+ pathPointsRef . current = [ ]
396+ setStartPoint ( null )
397+ }
398+
414399 setIsDrawing ( false )
415- setCurrentPath ( null )
416- setPathPoints ( [ ] )
417400 }
418401 }
419402
420403 window . addEventListener ( 'contextmenu' , handleRightClick )
421404
422- if ( ! isDrawing ) {
423- setCurrentPath ( null )
424- setPathPoints ( [ ] )
425- }
426-
427405 return ( ) => {
428406 window . removeEventListener ( 'contextmenu' , handleRightClick )
429407 }
430408 } , [ isDrawing ] )
431409
432410 useEffect ( ( ) => {
433- if ( shapeType !== 'path' ) {
434- setIsDrawing ( false )
435- }
411+ setIsDrawing ( false )
436412 } , [ shapeType ] )
437413
438414 return (
0 commit comments