This commit is contained in:
changkyu choi 2024-08-26 10:01:13 +09:00
commit e248736aaf
11 changed files with 522 additions and 66 deletions

View File

@ -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() {
<div className="test">
<p className="text-white">Sass 테스트입니다.</p>
</div>
<div>{getMessage('hi')}</div>
</div>
</>
)

View File

@ -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 && (
<>
<div className=" my-8 w-full text:pretty">
<Button
className="m-1 p-2"
onClick={() => {
setContent(<SettingsModal canvasProps={canvas} />)
setOpen(true)
}}
>
그리드 설정
</Button>
<Button className="m-1 p-2" color={`${mode === Mode.DEFAULT ? 'primary' : 'default'}`} onClick={fillCellInPolygon}>
모드 DEFAULT
</Button>
@ -742,7 +758,7 @@ export default function Roof2(props) {
<ThumbnailList {...thumbnailProps} />
<div className="flex justify-start my-8 mx-2 w-full">
<canvas ref={canvasRef} id="canvas" style={{ border: '1px solid black' }} />
{canvas !== undefined && <CanvasWithContextMenu ref={canvasRef} canvasProps={canvas} />}
{canvas !== undefined && <QContextMenu contextRef={canvasRef} canvasProps={canvas} />}
</div>
</>
)

View File

@ -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 (
<>
<div>
<div className="flex w-full flex-wrap md:flex-nowrap gap-4">
<label style={{ display: 'block', marginBottom: '5px' }}>
<CheckboxGroup label="그리드 설정" value={gridCheckedValue} onValueChange={setGridCheckValue}>
<Checkbox value="dot"> 그리드</Checkbox>
<Checkbox value="line">점선 그리드</Checkbox>
</CheckboxGroup>
</label>
</div>
<div className="flex w-full flex-wrap md:flex-nowrap gap-4">
<Input type="number" label="모듈" ref={moduleLength} defaultValue="90" />
mm
</div>
<div className="flex w-full flex-wrap md:flex-nowrap gap-4">
<RadioGroup label="비율 설정" onValueChange={setRatioValue}>
<Radio value="원치수">원치수</Radio>
<Radio value="1/2">1/2</Radio>
<Radio value="1/4">1/4</Radio>
<Radio value="1/10">1/10</Radio>
<Radio value="custom">임의간격</Radio>
</RadioGroup>
</div>
<div className="flex w-full flex-wrap md:flex-nowrap gap-4"></div>
<Checkbox value="linked" isDisabled={isCustomGridSetting}>
종횡연동
</Checkbox>
</div>
<div className="flex w-full flex-wrap md:flex-nowrap gap-4">
<Input type="number" label="가로간격" min={0} isDisabled={isCustomGridSetting} />
mm
</div>
<div className="flex w-full flex-wrap md:flex-nowrap gap-4">
<Input type="number" label="세로간격" min={0} isDisabled={isCustomGridSetting} />
mm
</div>
<div className="flex gap-4 items-center">
<Button size="sm">초기화</Button>
<Button size="sm" color="secondary" onClick={drawGridSettings} isDisabled={gridCheckedValue.length === 0}>
저장
</Button>
<Button size="sm" onClick={() => setOpen(!open)}>
취소
</Button>
</div>
</>
)
}

View File

@ -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
}

38
src/hooks/useMessage.js Normal file
View File

@ -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 }
}

View File

@ -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
}

3
src/locales/ja.json Normal file
View File

@ -0,0 +1,3 @@
{
"hi": "こんにちは"
}

3
src/locales/ko.json Normal file
View File

@ -0,0 +1,3 @@
{
"hi": "안녕하세요"
}

View File

@ -88,3 +88,9 @@ export const compassState = atom({
default: undefined,
dangerouslyAllowMutability: true,
})
export const guideLineState = atom({
key: 'guideLine',
default: {},
dangerouslyAllowMutability: true,
})

6
src/store/localeAtom.js Normal file
View File

@ -0,0 +1,6 @@
import { atom } from 'recoil'
export const globalLocaleState = atom({
key: 'globalLocaleState',
default: 'ko',
})

View File

@ -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
}