diff --git a/src/components/Playground.jsx b/src/components/Playground.jsx index 2467798a..e1043d2b 100644 --- a/src/components/Playground.jsx +++ b/src/components/Playground.jsx @@ -3,6 +3,7 @@ import { Button, Table, TableBody, TableCell, TableColumn, TableHeader, TableRow } from '@nextui-org/react' import { useAxios } from '@/hooks/useAxios' +import { useMessage } from '@/hooks/useMessage' // import { get } from '@/lib/Axios' import QSelect from '@/components/ui/QSelect' @@ -12,6 +13,7 @@ import styles from './playground.module.css' export default function Playground() { const { get } = useAxios() const testVar = process.env.NEXT_PUBLIC_TEST + const { getMessage } = useMessage() const handleUsers = async () => { // const users = await get('/api/user/find-all') @@ -58,6 +60,7 @@ export default function Playground() {

Sass 테스트입니다.

+
{getMessage('hi')}
) diff --git a/src/components/Roof2.jsx b/src/components/Roof2.jsx index 9e3d7589..7467a634 100644 --- a/src/components/Roof2.jsx +++ b/src/components/Roof2.jsx @@ -4,6 +4,7 @@ import { useCanvas } from '@/hooks/useCanvas' import { useEffect, useRef, useState } from 'react' import { v4 as uuidv4 } from 'uuid' import { useMode } from '@/hooks/useMode' +import { Mode } from '@/common/common' import { Button } from '@nextui-org/react' import RangeSlider from './ui/RangeSlider' import { useRecoilState, useRecoilValue } from 'recoil' @@ -22,14 +23,17 @@ import { getCanvasState, insertCanvasState } from '@/lib/canvas' import { calculateIntersection } from '@/util/canvas-util' import { QPolygon } from '@/components/fabric/QPolygon' import ThumbnailList from './ui/ThumbnailLIst' -import CanvasWithContextMenu from '@/util/context-util' -import { Mode } from '@/common/common' -import { get } from '@/lib/Axios' +import QContextMenu from './common/context-menu/QContextMenu' +import { modalContent, modalState } from '@/store/modalAtom' +import SettingsModal from './SettingsModal' +import { useAxios } from '@/hooks/useAxios' export default function Roof2(props) { const { name, userId, email, isLoggedIn } = props const { canvas, handleRedo, handleUndo, setCanvasBackgroundWithDots, saveImage, addCanvas } = useCanvas('canvas') + const { get } = useAxios() + const canvasRef = useRef(null) //canvas 기본 사이즈 @@ -59,6 +63,9 @@ export default function Roof2(props) { const wall = useRecoilValue(wallState) + const [open, setOpen] = useRecoilState(modalState) + const [contents, setContent] = useRecoilState(modalContent) + const [scale, setScale] = useState(1) //canvas 썸네일 @@ -564,6 +571,15 @@ export default function Roof2(props) { {canvas && ( <>
+ @@ -742,7 +758,7 @@ export default function Roof2(props) {
- {canvas !== undefined && } + {canvas !== undefined && }
) diff --git a/src/components/SettingsModal.jsx b/src/components/SettingsModal.jsx new file mode 100644 index 00000000..b263b47b --- /dev/null +++ b/src/components/SettingsModal.jsx @@ -0,0 +1,274 @@ +import { useEffect, useRef, useState } from 'react' +import { Button, Checkbox, CheckboxGroup, RadioGroup, Radio, Input } from '@nextui-org/react' +import { useRecoilState, useRecoilValue } from 'recoil' +import { modalContent, modalState } from '@/store/modalAtom' +import { guideLineState } from '@/store/canvasAtom' +import { fabric } from 'fabric' + +export default function SettingsModal(props) { + const { canvasProps } = props + const [isCustomGridSetting, setIsCustomGridSetting] = useState(true) + const [gridCheckedValue, setGridCheckValue] = useState([]) + const [ratioValue, setRatioValue] = useState('원치수') + const moduleLength = useRef(90) //모듈 mm 길이 입력 + + const [open, setOpen] = useRecoilState(modalState) + const [guideLine, setGuideLine] = useRecoilState(guideLineState) + + useEffect(() => { + setIsCustomGridSetting(ratioValue !== 'custom') + }, [ratioValue]) + + const drawGridSettings = () => { + const gridSettingArray = [] + + if (gridCheckedValue.includes('line')) { + // fabric.GuideRect = fabric.util.createClass(fabric.Rect, { + // _render: function (ctx) { + // this.callSuper('_render', ctx) + // ctx.strokeStyle = this.stroke || 'blue' + // ctx.lineWidth = this.strokeWidth || 1 + + // ctx.beginPath() + // ctx.moveTo(-this.width / 2, -this.height / 2) + // ctx.lineTo(this.width / 2, -this.height / 2) + // ctx.lineTo(this.width / 2, this.height / 2) + // ctx.stroke() + // }, + // }) + + // const guideRect = new fabric.GuideRect({ + // width: canvasProps.width, + // height: canvasProps.height, + // stroke: 'blue', + // strokeWidth: 1, + // selectable: false, + // fill: 'transparent', + // name: 'guideRect', + // }) + + // const patternSourceCanvas = new fabric.StaticCanvas(null, { + // width: moduleLength.current.value, + // height: moduleLength.current.value, + // backgroundColor: 'white', + // }) + + // patternSourceCanvas.add(guideRect) + // patternSourceCanvas.renderAll() + + // const pattern = new fabric.Pattern({ + // source: patternSourceCanvas.getElement(), + // repeat: 'repeat', + // }) + + const horizontalLineArray = [] + const verticalLineArray = [] + + for (let i = 0; i < canvasProps.height / moduleLength.current.value + 1; i++) { + const horizontalLine = new fabric.Line( + [ + 0, + i * moduleLength.current.value - moduleLength.current.value / 2, + canvasProps.width, + i * moduleLength.current.value - moduleLength.current.value / 2, + ], + { + stroke: 'gray', + strokeWidth: 1, + selectable: false, + name: 'guideLine', + }, + ) + canvasProps.add(horizontalLine) + horizontalLineArray.push(horizontalLine) + } + + for (let i = 0; i < canvasProps.width / moduleLength.current.value + 1; i++) { + const verticalLine = new fabric.Line( + [ + i * moduleLength.current.value - moduleLength.current.value / 2, + 0, + i * moduleLength.current.value - moduleLength.current.value / 2, + canvasProps.height, + ], + { + stroke: 'gray', + strokeWidth: 1, + selectable: false, + name: 'guideLine', + }, + ) + canvasProps.add(verticalLine) + verticalLineArray.push(verticalLine) + } + canvasProps.renderAll() + + const snapDistance = 10 + + canvasProps.on('mouse:move', function (e) { + let mouseObj = e + const mouseCursorX = e.pointer.x + const mouseCursorY = e.pointer.y + + horizontalLineArray.forEach((line) => { + if (Math.abs(mouseCursorY - line.y1) < snapDistance) { + mouseObj.x = mouseCursorX + mouseObj.y = line.y1 + } + // if (Math.abs(mouseCursorX - line.x2) < snapDistance) { + // line.set({ x2: mouseCursorX }) + // } + }) + }) + + // canvasProps.on('mouse:move', (e) => { + // console.log('event', e.pointer) + + // const obj = e + + // if (!obj) return + + // canvasProps.forEachObject(function (target) { + // if (target.name === 'guideLine' || target.name === 'guideDot') { + // console.log('line : ', target) + + // const lineStartX = target.x1, + // lineStartY = target.y1 + // const lineEndX = target.x2, + // lineEndY = target.y2 + + // console.log('target.x1', target.x1) + // console.log('target.y1', target.y1) + // console.log(`obj.pointer.y : ${obj.pointer.y}, obj.pointer.x : ${obj.pointer.x}`) + + // // 수평 선에 대한 스냅 + // if (Math.abs(obj.pointer.y - lineStartY) < snapDistance) { + // obj.pointer.y = lineStartY + // } + + // // 수직 선에 대한 스냅 + // if (Math.abs(obj.pointer.x - lineStartX) < snapDistance) { + // obj.pointer.x = lineStartX + // } + // } + // }) + // }) + + const recoilObj = { + guideMode: 'guideLine', + horizontalLineArray, + verticalLineArray, + moduleLength: moduleLength.current.value, + } + gridSettingArray.push(recoilObj) + } + + if (gridCheckedValue.includes('dot')) { + const circle = new fabric.Circle({ + radius: 2, + fill: 'red', + originX: 'center', + originY: 'center', + selectable: false, + lockMovementX: true, + lockMovementY: true, + lockRotation: true, + lockScalingX: true, + lockScalingY: true, + name: 'guideDot', + }) + + const patternSourceCanvas = new fabric.StaticCanvas(null, { + width: moduleLength.current.value, + height: moduleLength.current.value, + }) + + patternSourceCanvas.add(circle) + + circle.set({ + left: patternSourceCanvas.width / 2, + top: patternSourceCanvas.height / 2, + }) + + patternSourceCanvas.renderAll() + + const pattern = new fabric.Pattern({ + source: patternSourceCanvas.getElement(), + repeat: 'repeat', + }) + + const backgroundPolygon = new fabric.Polygon( + [ + { x: 0, y: 0 }, + { x: canvasProps.width, y: 0 }, + { x: canvasProps.width, y: canvasProps.height }, + { x: 0, y: canvasProps.height }, + ], + { + fill: pattern, + selectable: false, + }, + ) + canvasProps.add(backgroundPolygon) + canvasProps.renderAll() + + const recoilObj = { + guideMode: 'guideDot', + moduleLength: moduleLength.current.value, + } + + gridSettingArray.push(recoilObj) + } + canvasProps.renderAll() + setGuideLine(gridSettingArray) + } + + return ( + <> +
+
+ +
+
+ + mm +
+
+ + 원치수 + 1/2 + 1/4 + 1/10 + 임의간격 + +
+
+ + 종횡연동 + +
+
+ + mm +
+
+ + mm +
+
+ + + +
+ + ) +} diff --git a/src/util/context-util.js b/src/components/common/context-menu/QContextMenu.jsx similarity index 86% rename from src/util/context-util.js rename to src/components/common/context-menu/QContextMenu.jsx index 0179fdf0..57aaf45b 100644 --- a/src/util/context-util.js +++ b/src/components/common/context-menu/QContextMenu.jsx @@ -1,10 +1,15 @@ -import React, { useState, useEffect, forwardRef, useContext } from 'react' +'use client' +import { useEffect, useState } from 'react' +import { useRecoilState, useRecoilValue } from 'recoil' -const CanvasWithContextMenu = forwardRef(({ canvasProps }, ref) => { +export default function QContextMenu(props) { + const { contextRef, canvasProps } = props + + // const children = useRecoilValue(modalContent) const [contextMenu, setContextMenu] = useState({ visible: false, x: 0, y: 0 }) useEffect(() => { - if (!ref.current) return + if (!contextRef.current) return const handleContextMenu = (e) => { e.preventDefault() //기존 contextmenu 막고 @@ -34,7 +39,7 @@ const CanvasWithContextMenu = forwardRef(({ canvasProps }, ref) => { document.removeEventListener('click', handleClick) document.removeEventListener('click', handleOutsideClick) } - }, [ref, contextMenu]) + }, [contextRef, contextMenu]) const handleMenuClick = (option) => { alert(`option ${option} clicked`) @@ -60,6 +65,4 @@ const CanvasWithContextMenu = forwardRef(({ canvasProps }, ref) => { )} ) -}) - -export default CanvasWithContextMenu +} diff --git a/src/hooks/useMessage.js b/src/hooks/useMessage.js new file mode 100644 index 00000000..bc2f7eef --- /dev/null +++ b/src/hooks/useMessage.js @@ -0,0 +1,38 @@ +import { useRecoilValue } from 'recoil' +import { globalLocaleState } from '@/store/localeAtom' + +import KO from '@/locales/ko.json' +import JA from '@/locales/ja.json' + +const SESSION_STORAGE_MESSAGE_KEY = 'QCAST_MESSAGE_STORAGE' + +export const useMessage = () => { + const globalLocale = useRecoilValue(globalLocaleState) + + const getMessage = (key, args = []) => { + if (sessionStorage.getItem(SESSION_STORAGE_MESSAGE_KEY) === null) { + if (globalLocale === 'ko') { + setSessionMessage(JSON.stringify(KO)) + } else { + setSessionMessage(JSON.stringify(JA)) + } + } + + const sessionMessage = getSessionMessage() + const message = sessionMessage[key] || key + + return args.reduce((acc, arg, i) => { + return acc.replaceAll(`{${i}}`, arg) + }, message) + } + + const setSessionMessage = (sessionMessage) => { + sessionStorage.setItem(SESSION_STORAGE_MESSAGE_KEY, sessionMessage) + } + + const getSessionMessage = () => { + return JSON.parse(sessionStorage.getItem(SESSION_STORAGE_MESSAGE_KEY)) + } + + return { getMessage } +} diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index 77d9de25..11cc0ec2 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -4,6 +4,8 @@ import { distanceBetweenPoints, findTopTwoIndexesByDistance, getCenterPoint, + getClosestHorizontalLine, + getClosestVerticalLine, getDirection, getStartIndex, rearrangeArray, @@ -23,6 +25,7 @@ import { sortedPolygonArray, templateTypeState, wallState, + guideLineState, } from '@/store/canvasAtom' import { QLine } from '@/components/fabric/QLine' import { fabric } from 'fabric' @@ -41,7 +44,6 @@ export function useMode() { const [canvas, setCanvas] = useState(null) const [zoom, setZoom] = useState(100) const [fontSize] = useRecoilState(fontSizeState) - const [shape, setShape] = useState(0) const [sortedArray, setSortedArray] = useRecoilState(sortedPolygonArray) const [roof, setRoof] = useRecoilState(roofState) const [wall, setWall] = useRecoilState(wallState) @@ -64,6 +66,11 @@ export function useMode() { const compass = useRecoilValue(compassState) const [isCellCenter, setIsCellCenter] = useState(false) + const guideLineInfo = useRecoilValue(guideLineState) + + const [guideLineMode, setGuideLineMode] = useState(false) + const [guideDotMode, setGuideDotMode] = useState(false) + useEffect(() => { // 이벤트 리스너 추가 // if (!canvas) { @@ -101,30 +108,127 @@ export function useMode() { canvas?.off('mouse:down') canvas?.on('mouse:down', mouseEvent.editMode) } - }, [mode, isGuidePointMode]) + }, [mode]) + + useEffect(() => { + setGuideLineMode(false) + setGuideDotMode(false) + if (isObjectNotEmpty(guideLineInfo)) { + const guideLineState = guideLineInfo.filter((item) => item.guideMode === 'guideLine') + const guideDotState = guideLineInfo.filter((item) => item.guideMode === 'guideDot') + + setGuideLineMode(guideLineState.length > 0) + setGuideDotMode(guideDotState.length > 0) + } + }, [guideLineInfo]) const drawMouseLines = (e) => { + let isGuideLineMode = false, + isGuideDotMode = false + let guideDotLength, guideLineLength, horizontalLineArray, verticalLineArray + + if (isObjectNotEmpty(guideLineInfo)) { + const guideLineState = guideLineInfo.filter((item) => item.guideMode === 'guideLine') + const guideDotState = guideLineInfo.filter((item) => item.guideMode === 'guideDot') + setGuideLineMode(guideLineState.length > 0) + setGuideDotMode(guideDotState.length > 0) + isGuideLineMode = guideLineState.length > 0 + isGuideDotMode = guideDotState.length > 0 + + if (isGuideDotMode) { + guideDotLength = Number(guideDotState[0].moduleLength) + } + + if (isGuideLineMode) { + horizontalLineArray = [...guideLineState[0].horizontalLineArray] + verticalLineArray = [...guideLineState[0].verticalLineArray] + guideLineLength = Number(guideLineState[0].moduleLength) + } + } + // 현재 마우스 포인터의 위치를 가져옵니다. const pointer = canvas?.getPointer(e.e) // 기존에 그려진 가이드라인을 제거합니다. removeMouseLines() - if (canvas?.getActiveObject()) { - return - } - let newX, newY - if (isGuidePointMode && mode === Mode.EDIT) { - newX = Math.round(pointer.x / 200) * 200 - newY = Math.round(pointer.y / 200) * 200 - } else { - newX = pointer.x - newY = pointer.y + let newX = pointer.x + let newY = pointer.y + + if (isGuideLineMode && isGuideDotMode && mode === Mode.EDIT) { + const closestHorizontalLine = getClosestHorizontalLine(pointer, horizontalLineArray) + const closetVerticalLine = getClosestVerticalLine(pointer, verticalLineArray) + const xDiff = Math.abs(pointer.x - closetVerticalLine.x1) + const yDiff = Math.abs(pointer.y - closestHorizontalLine.y1) + + const x = pointer.x - guideLineLength * Math.floor(pointer.x / guideLineLength) + const y = pointer.y - guideLineLength * Math.floor(pointer.y / guideLineLength) + + const xRate = x / guideLineLength + const yRate = y / guideLineLength + const isAttachX = xRate >= 0.4 && xRate <= 0.7 + const isAttachY = yRate >= 0.4 && yRate <= 0.7 + + if (isAttachX && isAttachY) { + newX = Math.floor(pointer.x / guideLineLength) * guideLineLength + guideLineLength / 2 + newY = Math.floor(pointer.y / guideLineLength) * guideLineLength + guideLineLength / 2 + } else { + if (Math.min(xDiff, yDiff) <= 20) { + if (xDiff < yDiff) { + newX = closetVerticalLine.x1 + newY = pointer.y + } else { + newX = pointer.x + newY = closestHorizontalLine.y1 + } + } + } + } else if (isGuideDotMode && mode === Mode.EDIT) { + const x = pointer.x - guideDotLength * Math.floor(pointer.x / guideDotLength) + const y = pointer.y - guideDotLength * Math.floor(pointer.y / guideDotLength) + + const xRate = x / guideDotLength + const yRate = y / guideDotLength + const isAttachX = xRate >= 0.4 && xRate <= 0.7 + const isAttachY = yRate >= 0.4 && yRate <= 0.7 + + if (isAttachX && isAttachY) { + newX = Math.floor(pointer.x / guideDotLength) * guideDotLength + guideDotLength / 2 + newY = Math.floor(pointer.y / guideDotLength) * guideDotLength + guideDotLength / 2 + } + } else if (isGuideLineMode && mode === Mode.EDIT) { + const closestHorizontalLine = getClosestHorizontalLine(pointer, horizontalLineArray) + const closetVerticalLine = getClosestVerticalLine(pointer, verticalLineArray) + const xDiff = Math.abs(pointer.x - closetVerticalLine.x1) + const yDiff = Math.abs(pointer.y - closestHorizontalLine.y1) + + const x = pointer.x - guideLineLength * Math.floor(pointer.x / guideLineLength) + const y = pointer.y - guideLineLength * Math.floor(pointer.y / guideLineLength) + + const xRate = x / guideLineLength + const yRate = y / guideLineLength + const isAttachX = xRate >= 0.4 && xRate <= 0.7 + const isAttachY = yRate >= 0.4 && yRate <= 0.7 + + if (isAttachX && isAttachY) { + newX = Math.floor(pointer.x / guideLineLength) * guideLineLength + guideLineLength / 2 + newY = Math.floor(pointer.y / guideLineLength) * guideLineLength + guideLineLength / 2 + } else { + if (Math.min(xDiff, yDiff) <= 20) { + if (xDiff < yDiff) { + newX = closetVerticalLine.x1 + newY = pointer.y + } else { + newX = pointer.x + newY = closestHorizontalLine.y1 + } + } + } } // 가로선을 그립니다. const horizontalLine = new fabric.Line([0, newY, canvasSize.horizontal, newY], { - stroke: 'black', + stroke: 'red', strokeWidth: 1, selectable: false, name: 'mouseLine', @@ -132,7 +236,7 @@ export function useMode() { // 세로선을 그립니다. const verticalLine = new fabric.Line([newX, 0, newX, canvasSize.vertical], { - stroke: 'black', + stroke: 'red', strokeWidth: 1, selectable: false, name: 'mouseLine', @@ -154,7 +258,7 @@ export function useMode() { }, [pointCount.current]) const removeGuideLines = () => { - const guideLines = canvas?._objects.filter((obj) => obj.name === 'guideLine') + const guideLines = canvas?._objects.filter((obj) => obj.name === 'helpGuideLine') guideLines?.forEach((item) => canvas?.remove(item)) } @@ -176,7 +280,7 @@ export function useMode() { strokeWidth: 1, strokeDashArray: [1, 1, 1], }) - guideLine.name = 'guideLine' + guideLine.name = 'helpGuideLine' canvas?.add(guideLine) } else { const guideLine1 = new QLine([lastX, lastY, lastX, arrivalY], { @@ -193,8 +297,8 @@ export function useMode() { strokeDashArray: [1, 1, 1], }) - guideLine1.name = 'guideLine' - guideLine2.name = 'guideLine' + guideLine1.name = 'helpGuideLine' + guideLine2.name = 'helpGuideLine' canvas?.add(guideLine1) canvas?.add(guideLine2) @@ -587,7 +691,7 @@ export function useMode() { canvas?.add(circle) if (points.current.length === 2) { - if (isGuidePointMode) { + if (guideLineMode || guideDotMode) { const vector = { x: points.current[1].left - points.current[0].left, y: points.current[1].top - points.current[0].top, @@ -757,10 +861,8 @@ export function useMode() { points.current = [] historyPoints.current = [] - // handleOuterlines() const wall = makePolygon(null, sort) wall.set({ name: 'wall' }) - console.log('wall', wall) return wall } @@ -799,33 +901,7 @@ export function useMode() { const makePolygon = (otherLines, sort = true) => { // 캔버스에서 모든 라인 객체를 찾습니다. const lines = otherLines || historyLines.current - - if (!otherLines) { - //외각선 기준 - const topIndex = findTopTwoIndexesByDistance(sortedArray) //배열중에 큰 2값을 가져옴 TODO: 나중에는 인자로 받아서 다각으로 수정 해야됨 - - //일단 배열 6개 짜리 기준의 선 번호 - if (topIndex[0] === 4) { - if (topIndex[1] === 5) { - //1번 - setShape(1) - } - } else if (topIndex[0] === 1) { - //4번 - if (topIndex[1] === 2) { - setShape(4) - } - } else if (topIndex[0] === 0) { - if (topIndex[1] === 1) { - //2번 - setShape(2) - } else if (topIndex[1] === 5) { - setShape(3) - } - } - - historyLines.current = [] - } + historyLines.current = [] // 각 라인의 시작점과 끝점을 사용하여 다각형의 점 배열을 생성합니다. const points = lines.map((line) => ({ x: line.x1, y: line.y1 })) @@ -853,12 +929,10 @@ export function useMode() { canvas.add(polygon) // 캔버스를 다시 그립니다. - if (!otherLines) { - // polygon.fillCell() - canvas?.renderAll() - // polygon.setViewLengthText(false) - setMode(Mode.DEFAULT) - } + // polygon.fillCell() + canvas?.renderAll() + // polygon.setViewLengthText(false) + setMode(Mode.DEFAULT) return polygon } diff --git a/src/locales/ja.json b/src/locales/ja.json new file mode 100644 index 00000000..a1725dfd --- /dev/null +++ b/src/locales/ja.json @@ -0,0 +1,3 @@ +{ + "hi": "こんにちは" +} diff --git a/src/locales/ko.json b/src/locales/ko.json new file mode 100644 index 00000000..282d9722 --- /dev/null +++ b/src/locales/ko.json @@ -0,0 +1,3 @@ +{ + "hi": "안녕하세요" +} diff --git a/src/store/canvasAtom.js b/src/store/canvasAtom.js index b1bacbdb..062698c0 100644 --- a/src/store/canvasAtom.js +++ b/src/store/canvasAtom.js @@ -88,3 +88,9 @@ export const compassState = atom({ default: undefined, dangerouslyAllowMutability: true, }) + +export const guideLineState = atom({ + key: 'guideLine', + default: {}, + dangerouslyAllowMutability: true, +}) diff --git a/src/store/localeAtom.js b/src/store/localeAtom.js new file mode 100644 index 00000000..d82bcf7b --- /dev/null +++ b/src/store/localeAtom.js @@ -0,0 +1,6 @@ +import { atom } from 'recoil' + +export const globalLocaleState = atom({ + key: 'globalLocaleState', + default: 'ko', +}) diff --git a/src/util/canvas-util.js b/src/util/canvas-util.js index 7374c024..268370c1 100644 --- a/src/util/canvas-util.js +++ b/src/util/canvas-util.js @@ -38,7 +38,7 @@ export function actionHandler(eventData, transform, x, y) { // define a function that can keep the polygon in the same position when we change its width/height/top/left export function anchorWrapper(anchorIndex, fn) { - return function(eventData, transform, x, y) { + return function (eventData, transform, x, y) { let fabricObject = transform.target let originX = fabricObject?.points[anchorIndex].x - fabricObject.pathOffset.x let originY = fabricObject.points[anchorIndex].y - fabricObject.pathOffset.y @@ -675,3 +675,33 @@ export function findClosestPointWithDifferentXY(targetPoint, points) { return closestPoint } + +export const getClosestHorizontalLine = (pointer, horizontalLineArray) => { + let closestLine = null + let minDistance = Infinity + + horizontalLineArray.forEach((line) => { + const distance = Math.abs(line.y1 - pointer.y) // Assuming horizontal lines have the same y1 and y2 + if (distance < minDistance) { + minDistance = distance + closestLine = line + } + }) + + return closestLine +} + +export const getClosestVerticalLine = (pointer, verticalLineArray) => { + let closestLine = null + let minDistance = Infinity + + verticalLineArray.forEach((line) => { + const distance = Math.abs(line.x1 - pointer.x) // Assuming horizontal lines have the same y1 and y2 + if (distance < minDistance) { + minDistance = distance + closestLine = line + } + }) + + return closestLine +}