This commit is contained in:
changkyu choi 2024-09-26 15:50:10 +09:00
commit 64d4b7e472
12 changed files with 525 additions and 89 deletions

View File

@ -31,6 +31,28 @@ export default async function RootLayout({ children }) {
// const isLoggedIn = await checkSession() // const isLoggedIn = await checkSession()
const session = await getSession() const session = await getSession()
console.log('session[layout]:', session) console.log('session[layout]:', session)
let sessionProps = {}
if (session.isLoggedIn) {
sessionProps = {
userId: session.userId,
saleStoreId: session.saleStoreId,
name: session.name,
mail: session.mail,
tel: session.tel,
storeId: session.storeId,
userNm: session.userNm,
userNmKana: session.userNmKana,
category: session.category,
telNo: session.telNo,
fax: session.fax,
email: session.email,
pwdInitYn: session.pwdInitYn,
isLoggedIn: session.isLoggedIn,
}
}
if (!headerPathname.includes('/login') && !session.isLoggedIn) { if (!headerPathname.includes('/login') && !session.isLoggedIn) {
redirect('/login') redirect('/login')
} }
@ -41,7 +63,7 @@ export default async function RootLayout({ children }) {
<body> <body>
{/*{headerPathname !== '/login' && <Headers />}*/} {/*{headerPathname !== '/login' && <Headers />}*/}
<div className="wrap"> <div className="wrap">
<Header loginedUserNm={session?.userNm} /> <Header userSession={sessionProps} />
<UIProvider> <UIProvider>
<div className="content"> <div className="content">
<Dimmed /> <Dimmed />

View File

@ -2,21 +2,225 @@ import WithDraggable from '@/components/common/draggable/withDraggable'
import QSelectBox from '@/components/common/select/QSelectBox' import QSelectBox from '@/components/common/select/QSelectBox'
import { useState } from 'react' import { useState } from 'react'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { canvasState, dotLineGridSettingState, dotLineIntervalSelector } from '@/store/canvasAtom'
import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'
import { onlyNumberInputChange } from '@/util/input-utils'
import { fabric } from 'fabric'
const TYPE = {
DOT: 'DOT',
LINE: 'LINE',
}
export default function DotLineGrid(props) { export default function DotLineGrid(props) {
// const [modalOption, setModalOption] = useRecoilState(modalState); //modal state // const [modalOption, setModalOption] = useRecoilState(modalState); //modal state
const [close, setClose] = useState(false) const [close, setClose] = useState(false)
const { setShowDotLineGridModal } = props const { setShowDotLineGridModal } = props
const canvas = useRecoilValue(canvasState)
const [dotLineGridSetting, setDotLineGridSettingState] = useRecoilState(dotLineGridSettingState)
const resetDotLineGridSetting = useResetRecoilState(dotLineGridSettingState)
const interval = useRecoilValue(dotLineIntervalSelector)
const { getMessage } = useMessage() const { getMessage } = useMessage()
const SelectOption = [ const SelectOption = [
{ id: 1, name: getMessage('modal.canvas.setting.grid.dot.line.setting.line.origin') }, { id: 1, name: getMessage('modal.canvas.setting.grid.dot.line.setting.line.origin'), value: 1 },
{ id: 2, name: '1/2' }, { id: 2, name: '1/2', value: 1 / 2 },
{ {
id: 3, id: 3,
name: '1/4', name: '1/4',
value: 1 / 4,
}, },
{ id: 4, name: '1/10' }, { id: 4, name: '1/10', value: 1 / 10 },
] ]
const [selectOption, setSelectOption] = useState(SelectOption[0])
const HandleClickClose = () => {
// setClose(true)
// setTimeout(() => {
// setModalOption({ ...modalOption, gridoption: false })
// setClose(false)
// }, 180)
}
const handleCheckBoxChange = (e) => {
const { value, checked } = e.target
setDotLineGridSettingState((prev) => {
return {
...prev,
[value]: checked,
}
})
}
const handleSave = () => {
// 1. .
canvas
?.getObjects()
.filter((obj) => obj.name === 'lineGrid')
.forEach((obj) => canvas?.remove(obj))
canvas
?.getObjects()
.filter((obj) => obj.name === 'dotGrid')
.forEach((obj) => canvas?.remove(obj))
const horizontalInterval = interval.horizontalInterval
const verticalInterval = interval.verticalInterval
if (dotLineGridSetting.DOT) {
const circle = new fabric.Circle({
radius: 2,
fill: 'red',
strokeWidth: 0.7,
originX: 'center',
originY: 'center',
selectable: false,
lockMovementX: true,
lockMovementY: true,
lockRotation: true,
lockScalingX: true,
lockScalingY: true,
})
const patternSourceCanvas = new fabric.StaticCanvas(null, {
width: horizontalInterval,
height: verticalInterval,
})
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: canvas.width, y: 0 },
{ x: canvas.width, y: canvas.height },
{ x: 0, y: canvas.height },
],
{
fill: pattern,
selectable: false,
name: 'dotGrid',
},
)
canvas.add(backgroundPolygon)
backgroundPolygon.sendToBack()
canvas.renderAll()
}
if (dotLineGridSetting.LINE) {
for (let i = 0; i < canvas.height / verticalInterval + 1; i++) {
const horizontalLine = new fabric.Line(
[0, i * verticalInterval - verticalInterval / 2, canvas.width, i * verticalInterval - verticalInterval / 2],
{
stroke: 'black',
strokeWidth: 1,
selectable: true,
lockMovementX: true,
lockMovementY: true,
lockRotation: true,
lockScalingX: true,
lockScalingY: true,
name: 'lineGrid',
strokeDashArray: [5, 2],
opacity: 0.3,
direction: 'horizontal',
},
)
canvas.add(horizontalLine)
}
for (let i = 0; i < canvas.width / horizontalInterval + 1; i++) {
const verticalLine = new fabric.Line(
[i * horizontalInterval - horizontalInterval / 2, 0, i * horizontalInterval - horizontalInterval / 2, canvas.height],
{
stroke: 'black',
strokeWidth: 1,
selectable: true,
lockMovementX: true,
lockMovementY: true,
lockRotation: true,
lockScalingX: true,
lockScalingY: true,
name: 'lineGrid',
strokeDashArray: [5, 2],
opacity: 0.3,
direction: 'vertical',
},
)
canvas.add(verticalLine)
}
}
canvas.renderAll()
}
const handleRadioChange = (e) => {
const { value, name, checked, selected } = e.target
setDotLineGridSettingState((prev) => {
return {
...prev,
INTERVAL: {
...prev.INTERVAL,
type: Number(value),
},
}
})
}
const changeInput = (value, e) => {
const { name } = e.target
setDotLineGridSettingState((prev) => {
return {
...prev,
INTERVAL: {
...prev.INTERVAL,
[name]: value,
},
}
})
}
const changeDimension = (result) => {
const { value } = result
setDotLineGridSettingState((prev) => {
return {
...prev,
INTERVAL: {
...prev.INTERVAL,
dimension: value,
},
}
})
}
//
const reset = () => {
canvas
?.getObjects()
.filter((obj) => obj.name === 'lineGrid')
.forEach((obj) => canvas?.remove(obj))
canvas
?.getObjects()
.filter((obj) => obj.name === 'dotGrid')
.forEach((obj) => canvas?.remove(obj))
resetDotLineGridSetting()
setSelectOption(SelectOption[0])
}
return ( return (
<WithDraggable isShow={true} pos={{ x: -150, y: 300 }}> <WithDraggable isShow={true} pos={{ x: -150, y: 300 }}>
<div className={`modal-pop-wrap ssm mount`}> <div className={`modal-pop-wrap ssm mount`}>
@ -29,55 +233,91 @@ export default function DotLineGrid(props) {
<div className="modal-body"> <div className="modal-body">
<div className="grid-check-form"> <div className="grid-check-form">
<div className="d-check-box pop"> <div className="d-check-box pop">
<input type="checkbox" id="ch01" /> <input type="checkbox" id="ch01" value={TYPE.DOT} onChange={handleCheckBoxChange} checked={dotLineGridSetting.DOT} />
<label htmlFor="ch01">{getMessage('modal.canvas.setting.grid.dot.line.setting.dot.display')}</label> <label htmlFor="ch01">{getMessage('modal.canvas.setting.grid.dot.line.setting.dot.display')}</label>
</div> </div>
<div className="d-check-box pop"> <div className="d-check-box pop">
<input type="checkbox" id="ch02" /> <input type="checkbox" id="ch02" value={TYPE.LINE} onChange={handleCheckBoxChange} checked={dotLineGridSetting.LINE} />
<label htmlFor="ch02">{getMessage('modal.canvas.setting.grid.dot.line.setting.line.display')}</label> <label htmlFor="ch02">{getMessage('modal.canvas.setting.grid.dot.line.setting.line.display')}</label>
</div> </div>
</div> </div>
<div className="grid-option-wrap"> <div className="grid-option-wrap">
<div className="grid-option-box"> <div className="grid-option-box">
<div className="d-check-radio pop no-text"> <div className="d-check-radio pop no-text">
<input type="radio" name="radio01" id="ra01" /> <input
type="radio"
name="radio01"
id="ra01"
value={1}
onChange={handleRadioChange}
checked={dotLineGridSetting.INTERVAL.type === 1}
/>
<label htmlFor="ra01"></label> <label htmlFor="ra01"></label>
</div> </div>
<div className="grid-input-form"> <div className="grid-input-form">
<span className="mr10">{getMessage('modal.canvas.setting.grid.dot.line.setting.horizon')}</span> <span className="mr10">{getMessage('modal.canvas.setting.grid.dot.line.setting.horizon')}</span>
<div className="input-grid mr5"> <div className="input-grid mr5">
<input type="text" className="input-origin" defaultValue={910} /> <input
type="text"
className="input-origin"
name={`horizontalInterval`}
value={dotLineGridSetting.INTERVAL.horizontalInterval}
onChange={(e) => onlyNumberInputChange(e, changeInput)}
/>
</div> </div>
<span>mm</span> <span>mm</span>
</div> </div>
<div className="grid-input-form"> <div className="grid-input-form">
<span className="mr10">{getMessage('modal.canvas.setting.grid.dot.line.setting.vertical')}</span> <span className="mr10">{getMessage('modal.canvas.setting.grid.dot.line.setting.vertical')}</span>
<div className="input-grid mr5"> <div className="input-grid mr5">
<input type="text" className="input-origin" defaultValue={910} /> <input
type="text"
className="input-origin"
name={`verticalInterval`}
value={dotLineGridSetting.INTERVAL.verticalInterval}
onChange={(e) => onlyNumberInputChange(e, changeInput)}
/>
</div> </div>
<span>mm</span> <span>mm</span>
</div> </div>
</div> </div>
<div className="grid-option-box"> <div className="grid-option-box">
<div className="d-check-radio pop no-text"> <div className="d-check-radio pop no-text">
<input type="radio" name="radio01" id="ra02" /> <input
type="radio"
name="radio01"
id="ra02"
value={2}
onChange={handleRadioChange}
checked={dotLineGridSetting.INTERVAL.type === 2}
/>
<label htmlFor="ra02"></label> <label htmlFor="ra02"></label>
</div> </div>
<div className="grid-input-form"> <div className="grid-input-form">
<span className="mr10">{getMessage('modal.canvas.setting.grid.dot.line.setting.ratio')}</span> <span className="mr10">{getMessage('modal.canvas.setting.grid.dot.line.setting.ratio')}</span>
<div className="input-grid mr5"> <div className="input-grid mr5">
<input type="text" className="input-origin" defaultValue={910} /> <input
type="text"
className="input-origin"
name={`ratioInterval`}
value={dotLineGridSetting.INTERVAL.ratioInterval}
onChange={(e) => onlyNumberInputChange(e, changeInput)}
/>
</div> </div>
<span>mm</span> <span>mm</span>
</div> </div>
<div className="grid-select"> <div className="grid-select">
<QSelectBox title={'原寸'} options={SelectOption} /> <QSelectBox options={SelectOption} onChange={changeDimension} value={selectOption} />
</div> </div>
</div> </div>
</div> </div>
<div className="grid-btn-wrap"> <div className="grid-btn-wrap">
<button className="btn-frame modal mr5">{getMessage('modal.canvas.setting.grid.dot.line.setting.reset')}</button> <button className="btn-frame modal mr5" onClick={reset}>
<button className="btn-frame modal act">{getMessage('modal.canvas.setting.grid.dot.line.setting.save')}</button> {getMessage('modal.canvas.setting.grid.dot.line.setting.reset')}
</button>
<button className="btn-frame modal act" onClick={handleSave}>
{getMessage('modal.canvas.setting.grid.dot.line.setting.save')}
</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -8,8 +8,10 @@ import { useEvent } from '@/hooks/useEvent'
import { import {
adsorptionPointAddModeState, adsorptionPointAddModeState,
adsorptionPointModeState, adsorptionPointModeState,
adsorptionRangeState,
canvasHistoryState, canvasHistoryState,
canvasState, canvasState,
dotLineIntervalSelector,
verticalHorizontalModeState, verticalHorizontalModeState,
} from '@/store/canvasAtom' } from '@/store/canvasAtom'
import { import {
@ -27,22 +29,22 @@ import { distanceBetweenPoints } from '@/util/canvas-util'
import { calculateAngle } from '@/util/qpolygon-utils' import { calculateAngle } from '@/util/qpolygon-utils'
import { usePolygon } from '@/hooks/usePolygon' import { usePolygon } from '@/hooks/usePolygon'
import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils' import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils'
import { useMouse } from '@/hooks/useMouse'
export default function OuterLineWall(props) { export default function OuterLineWall(props) {
const { setShowOutlineModal } = props const { setShowOutlineModal } = props
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { const { addCanvasMouseEventListener, addDocumentEventListener, removeAllMouseEventListeners, removeAllDocumentEventListeners, removeMouseEvent } =
addCanvasMouseEventListener, useEvent()
addDocumentEventListener, const { getIntersectMousePoint } = useMouse()
removeAllMouseEventListeners,
removeAllDocumentEventListeners,
removeMouseEvent,
getIntersectMousePoint,
} = useEvent()
const { addLine, removeLine } = useLine() const { addLine, removeLine } = useLine()
const { addPolygonByLines } = usePolygon() const { addPolygonByLines } = usePolygon()
const verticalHorizontalMode = useRecoilValue(verticalHorizontalModeState) const verticalHorizontalMode = useRecoilValue(verticalHorizontalModeState)
const adsorptionPointAddMode = useRecoilValue(adsorptionPointAddModeState) const adsorptionPointAddMode = useRecoilValue(adsorptionPointAddModeState)
const adsorptionPointMode = useRecoilValue(adsorptionPointModeState)
const adsorptionRange = useRecoilValue(adsorptionRangeState)
const interval = useRecoilValue(dotLineIntervalSelector) //
const length1Ref = useRef(null) const length1Ref = useRef(null)
const length2Ref = useRef(null) const length2Ref = useRef(null)
@ -72,7 +74,7 @@ export default function OuterLineWall(props) {
return () => { return () => {
removeAllMouseEventListeners() removeAllMouseEventListeners()
} }
}, [verticalHorizontalMode, points, adsorptionPointAddMode]) }, [verticalHorizontalMode, points, adsorptionPointAddMode, adsorptionPointMode, adsorptionRange, interval])
useEffect(() => { useEffect(() => {
arrow1Ref.current = arrow1 arrow1Ref.current = arrow1

View File

@ -110,11 +110,11 @@ export default function SecondOption() {
// HTTP POST // HTTP POST
await post({ url: `/api/canvas-management/canvas-settings`, data: patternData }).then((res) => { await post({ url: `/api/canvas-management/canvas-settings`, data: patternData }).then((res) => {
toastUp({ message: getMessage(res.returnMessage), type: 'success' }) toastUp({ message: getMessage(res.returnMessage), type: 'success' })
setAdsorptionRange(option.range)
}) })
} catch (error) { } catch (error) {
toastUp({ message: getMessage(res.returnMessage), type: 'error' }) toastUp({ message: getMessage(res.returnMessage), type: 'error' })
} }
setAdsorptionRange(option.range)
} }
return ( return (
<> <>

View File

@ -1,13 +1,16 @@
'use client' 'use client'
import { Fragment, useState } from 'react' import { Fragment, useCallback, useEffect, useState } from 'react'
import Link from 'next/link' import Link from 'next/link'
import { usePathname, useRouter } from 'next/navigation' import { usePathname } from 'next/navigation'
import { useRecoilState, useRecoilValue } from 'recoil'
import { dimmedStore, sessionStore } from '@/store/commonAtom'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { logout } from '@/lib/authActions' import { logout } from '@/lib/authActions'
import QSelectBox from '@/components/common/select/QSelectBox' import QSelectBox from '@/components/common/select/QSelectBox'
import { useRecoilValue } from 'recoil'
import { dimmedStore } from '@/store/commonAtom'
export const ToggleonMouse = (e, act, target) => { export const ToggleonMouse = (e, act, target) => {
const listWrap = e.target.closest(target) const listWrap = e.target.closest(target)
@ -25,7 +28,8 @@ export const ToggleonMouse = (e, act, target) => {
} }
export default function Header(props) { export default function Header(props) {
const { loginedUserNm } = props const { userSession } = props
const [sessionState, setSessionState] = useRecoilState(sessionStore)
const { getMessage } = useMessage() const { getMessage } = useMessage()
const pathName = usePathname() const pathName = usePathname()
// if (pathName.includes('login') || pathName.includes('join')) { // if (pathName.includes('login') || pathName.includes('join')) {
@ -65,6 +69,14 @@ export default function Header(props) {
}, },
] ]
const syncSession = useCallback(() => {
setSessionState({ ...userSession })
})
useEffect(() => {
syncSession()
}, [userSession])
const onChangeSelect = (option) => { const onChangeSelect = (option) => {
setSelected(option) setSelected(option)
} }
@ -125,7 +137,7 @@ export default function Header(props) {
</div> </div>
<div className="header-left"> <div className="header-left">
<div className="profile-box"> <div className="profile-box">
<button className="profile">{loginedUserNm}</button> <button className="profile">{userSession.userNm}</button>
</div> </div>
<div className="sign-out-box"> <div className="sign-out-box">
<button className="sign-out" onClick={() => logout()}> <button className="sign-out" onClick={() => logout()}>

View File

@ -0,0 +1,45 @@
import { useRecoilState, useRecoilValue } from 'recoil'
import { adsorptionPointAddModeState, adsorptionPointModeState, adsorptionRangeState, canvasState } from '@/store/canvasAtom'
import { fabric } from 'fabric'
import { useMouse } from '@/hooks/useMouse'
export function useAdsorptionPoint() {
const canvas = useRecoilValue(canvasState)
const [adsorptionPointAddMode, setAdsorptionPointAddMode] = useRecoilState(adsorptionPointAddModeState)
const [adsorptionPointMode, setAdsorptionPointMode] = useRecoilState(adsorptionPointModeState)
const [adsorptionRange, setAdsorptionRange] = useRecoilState(adsorptionRangeState)
const { getIntersectMousePoint } = useMouse()
const getAdsorptionPoints = () => {
return canvas.getObjects().filter((obj) => obj.visible && obj.name === 'adsorptionPoint')
}
const adsorptionPointAddModeStateEvent = (opt) => {
//흡착점 모드일 경우
let pointer = getIntersectMousePoint(opt)
const adsorptionPoint = new fabric.Circle({
radius: 3,
fill: 'red',
left: pointer.x - 3,
top: pointer.y - 3,
x: pointer.x,
y: pointer.y,
selectable: false,
name: 'adsorptionPoint',
})
canvas.add(adsorptionPoint)
canvas.renderAll()
}
return {
adsorptionPointAddMode,
adsorptionPointMode,
adsorptionRange,
getAdsorptionPoints,
adsorptionPointAddModeStateEvent,
}
}

View File

@ -0,0 +1,51 @@
import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'
import { canvasState, dotLineGridSettingState, dotLineIntervalSelector } from '@/store/canvasAtom'
import { calculateDistance } from '@/util/canvas-util'
import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint'
export function useDotLineGrid() {
const canvas = useRecoilValue(canvasState)
const [dotLineGridSetting, setDotLineGridSettingState] = useRecoilState(dotLineGridSettingState)
const interval = useRecoilValue(dotLineIntervalSelector) // 가로 세로 간격
const { adsorptionRange } = useAdsorptionPoint()
const resetDotLineGridSetting = useResetRecoilState(dotLineGridSettingState)
const getLineGrids = () => {
return canvas.getObjects().filter((obj) => obj.name === 'lineGrid')
}
const getClosestLineGrid = (point) => {
const lines = getLineGrids()
if (lines.length === 0) {
return null
}
let closestLine = null
let minDistance = Infinity
lines.forEach((line) => {
const distance = calculateDistance(point, line)
if (distance < minDistance) {
minDistance = distance
closestLine = line
}
})
if (minDistance > adsorptionRange) {
return null
}
return closestLine
}
return {
dotLineGridSetting,
resetDotLineGridSetting,
getLineGrids,
getClosestLineGrid,
interval,
}
}

View File

@ -9,7 +9,10 @@ import {
currentMenuState, currentMenuState,
} from '@/store/canvasAtom' } from '@/store/canvasAtom'
import { fabric } from 'fabric' import { fabric } from 'fabric'
import { calculateIntersection, distanceBetweenPoints } from '@/util/canvas-util' import { calculateDistance, calculateIntersection, distanceBetweenPoints, findClosestPoint } from '@/util/canvas-util'
import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint'
import { useMouse } from '@/hooks/useMouse'
import { useDotLineGrid } from '@/hooks/useDotLineGrid'
export function useEvent() { export function useEvent() {
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
@ -17,10 +20,9 @@ export function useEvent() {
const keyboardEventListeners = useRef([]) const keyboardEventListeners = useRef([])
const mouseEventListeners = useRef([]) const mouseEventListeners = useRef([])
const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState) const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState)
const adsorptionPointAddMode = useRecoilValue(adsorptionPointAddModeState)
const adsorptionPointMode = useRecoilValue(adsorptionPointModeState)
const adsorptionRange = useRecoilValue(adsorptionRangeState)
const { adsorptionPointAddMode, adsorptionPointMode, adsorptionRange, getAdsorptionPoints, adsorptionPointAddModeStateEvent } = useAdsorptionPoint()
const { dotLineGridSetting, interval, getClosestLineGrid } = useDotLineGrid()
useEffect(() => { useEffect(() => {
if (!canvas) { if (!canvas) {
return return
@ -35,7 +37,7 @@ export function useEvent() {
canvas?.on('mouse:wheel', wheelEvent) canvas?.on('mouse:wheel', wheelEvent)
addDefaultEvent() addDefaultEvent()
}, [currentMenu, canvas, adsorptionPointAddMode, adsorptionPointMode, adsorptionRange]) }, [currentMenu, canvas, adsorptionPointAddMode, adsorptionPointMode, adsorptionRange, dotLineGridSetting])
const addDefaultEvent = () => { const addDefaultEvent = () => {
//default Event 추가 //default Event 추가
@ -53,26 +55,6 @@ export function useEvent() {
e.stopPropagation() e.stopPropagation()
} }
const adsorptionPointAddModeStateEvent = (opt) => {
//흡착점 모드일 경우
let pointer = getIntersectMousePoint(opt)
const adsorptionPoint = new fabric.Circle({
radius: 3,
fill: 'red',
left: pointer.x - 3,
top: pointer.y - 3,
x: pointer.x,
y: pointer.y,
selectable: false,
name: 'adsorptionPoint',
})
canvas.add(adsorptionPoint)
canvas.renderAll()
}
const wheelEvent = (opt) => { const wheelEvent = (opt) => {
const delta = opt.e.deltaY // 휠 이동 값 (양수면 축소, 음수면 확대) const delta = opt.e.deltaY // 휠 이동 값 (양수면 축소, 음수면 확대)
let zoom = canvas.getZoom() // 현재 줌 값 let zoom = canvas.getZoom() // 현재 줌 값
@ -107,17 +89,61 @@ export function useEvent() {
let arrivalPoint = { x: pointer.x, y: pointer.y } let arrivalPoint = { x: pointer.x, y: pointer.y }
if (adsorptionPointMode) { if (adsorptionPointMode) {
// pointer와 adsorptionPoints의 거리를 계산하여 가장 가까운 점을 찾는다. if (dotLineGridSetting.LINE) {
let minDistance = adsorptionRange const closestLine = getClosestLineGrid(pointer)
let adsorptionPoint = null
adsorptionPoints.forEach((point) => { if (closestLine) {
const distance = distanceBetweenPoints(pointer, point) const distance = calculateDistance(pointer, closestLine)
if (distance < minDistance) {
minDistance = distance if (distance < adsorptionRange) {
adsorptionPoint = point arrivalPoint = closestLine.direction === 'vertical' ? { x: closestLine.x1, y: pointer.y } : { x: pointer.x, y: closestLine.y1 }
}
} }
}) }
if (adsorptionPoint) {
if (dotLineGridSetting.DOT) {
const horizontalInterval = interval.horizontalInterval
const verticalInterval = interval.verticalInterval
const x = pointer.x - horizontalInterval * Math.floor(pointer.x / horizontalInterval)
const y = pointer.y - verticalInterval * Math.floor(pointer.y / verticalInterval)
const xRate = (x / horizontalInterval) * 100
const yRate = (y / verticalInterval) * 100
let tempPoint
if (xRate <= adsorptionRange && yRate <= adsorptionRange) {
tempPoint = {
x: Math.round(pointer.x / horizontalInterval) * horizontalInterval + horizontalInterval / 2,
y: Math.round(pointer.y / verticalInterval) * verticalInterval + horizontalInterval / 2,
}
} else if (xRate <= adsorptionRange && yRate >= adsorptionRange) {
tempPoint = {
x: Math.round(pointer.x / horizontalInterval) * horizontalInterval + horizontalInterval / 2,
y: Math.round(pointer.y / verticalInterval) * verticalInterval - horizontalInterval / 2,
}
} else if (xRate >= adsorptionRange && yRate <= adsorptionRange) {
tempPoint = {
x: Math.round(pointer.x / horizontalInterval) * horizontalInterval - horizontalInterval / 2,
y: Math.round(pointer.y / verticalInterval) * verticalInterval + horizontalInterval / 2,
}
} else if (xRate >= adsorptionRange && yRate >= adsorptionRange) {
tempPoint = {
x: Math.round(pointer.x / horizontalInterval) * horizontalInterval - horizontalInterval / 2,
y: Math.round(pointer.y / verticalInterval) * verticalInterval - horizontalInterval / 2,
}
}
if (distanceBetweenPoints(pointer, tempPoint) <= adsorptionRange) {
arrivalPoint = tempPoint
}
}
// pointer와 adsorptionPoints의 거리를 계산하여 가장 가까운 점을 찾는다.
let adsorptionPoint = findClosestPoint(pointer, adsorptionPoints)
if (adsorptionPoint && distanceBetweenPoints(pointer, adsorptionPoint) <= adsorptionRange) {
arrivalPoint = { ...adsorptionPoint } arrivalPoint = { ...adsorptionPoint }
} }
} }
@ -203,28 +229,11 @@ export function useEvent() {
}) })
} }
const getAdsorptionPoints = () => {
return canvas.getObjects().filter((obj) => obj.visible && obj.name === 'adsorptionPoint')
}
//가로선, 세로선의 교차점을 return
const getIntersectMousePoint = (e) => {
let pointer = canvas.getPointer(e.e)
const mouseLines = canvas.getObjects().filter((obj) => obj.name === 'mouseLine')
if (mouseLines.length < 2) {
return pointer
}
return calculateIntersection(mouseLines[0], mouseLines[1]) ?? pointer
}
return { return {
addDocumentEventListener, addDocumentEventListener,
addCanvasMouseEventListener, addCanvasMouseEventListener,
removeAllMouseEventListeners, removeAllMouseEventListeners,
removeAllDocumentEventListeners, removeAllDocumentEventListeners,
removeMouseEvent, removeMouseEvent,
getIntersectMousePoint,
} }
} }

23
src/hooks/useMouse.js Normal file
View File

@ -0,0 +1,23 @@
import { useRecoilValue } from 'recoil'
import { canvasState } from '@/store/canvasAtom'
import { calculateIntersection } from '@/util/canvas-util'
export function useMouse() {
const canvas = useRecoilValue(canvasState)
//가로선, 세로선의 교차점을 return
const getIntersectMousePoint = (e) => {
let pointer = canvas.getPointer(e.e)
const mouseLines = canvas.getObjects().filter((obj) => obj.name === 'mouseLine')
if (mouseLines.length < 2) {
return pointer
}
return calculateIntersection(mouseLines[0], mouseLines[1]) || pointer
}
return {
getIntersectMousePoint,
}
}

View File

@ -205,7 +205,7 @@ export const verticalHorizontalModeState = atom({
// 흡착점 모드 // 흡착점 모드
export const adsorptionPointModeState = atom({ export const adsorptionPointModeState = atom({
key: 'adsorptionPointModeState', key: 'adsorptionPointModeState',
default: false, default: true,
}) })
// 흡착점 추가모드 // 흡착점 추가모드
export const adsorptionPointAddModeState = atom({ export const adsorptionPointAddModeState = atom({
@ -218,3 +218,35 @@ export const adsorptionRangeState = atom({
key: 'adsorptionRangeState', key: 'adsorptionRangeState',
default: 50, default: 50,
}) })
// 점,선 그리드 설정
export const dotLineGridSettingState = atom({
key: 'gridSettingState',
default: {
INTERVAL: {
type: 2, // 1: 가로,세로 간격 수동, 2: 비율 간격
ratioInterval: 910,
verticalInterval: 910,
horizontalInterval: 910,
dimension: 1, // 치수
},
DOT: false,
LINE: false,
},
})
export const dotLineIntervalSelector = selector({
key: 'dotLineIntervalSelector',
get: ({ get }) => {
const gridSetting = get(dotLineGridSettingState)
return gridSetting.INTERVAL.type === 1
? {
horizontalInterval: Math.round(gridSetting.INTERVAL.horizontalInterval / 10),
verticalInterval: Math.round(gridSetting.INTERVAL.verticalInterval / 10),
}
: {
horizontalInterval: Math.round((gridSetting.INTERVAL.ratioInterval * gridSetting.INTERVAL.dimension) / 10),
verticalInterval: Math.round((gridSetting.INTERVAL.ratioInterval * gridSetting.INTERVAL.dimension) / 10),
}
},
})

View File

@ -39,9 +39,9 @@ export const settingModalSecondOptionsState = atom({
], ],
option4: [ option4: [
{ id: 1, column: 'adsorpRangeSmall', name: 'modal.canvas.setting.font.plan.absorption.small', selected: true, range: 10 }, { id: 1, column: 'adsorpRangeSmall', name: 'modal.canvas.setting.font.plan.absorption.small', selected: true, range: 10 },
{ id: 2, column: 'adsorpRangeSmallSemi', name: 'modal.canvas.setting.font.plan.absorption.small.semi', selected: false, range: 30 }, { id: 2, column: 'adsorpRangeSmallSemi', name: 'modal.canvas.setting.font.plan.absorption.small.semi', selected: false, range: 20 },
{ id: 3, column: 'adsorpRangeMedium', name: 'modal.canvas.setting.font.plan.absorption.medium', selected: false, range: 50 }, { id: 3, column: 'adsorpRangeMedium', name: 'modal.canvas.setting.font.plan.absorption.medium', selected: false, range: 30 },
{ id: 4, column: 'adsorpRangeLarge', name: 'modal.canvas.setting.font.plan.absorption.large', selected: false, range: 80 }, { id: 4, column: 'adsorpRangeLarge', name: 'modal.canvas.setting.font.plan.absorption.large', selected: false, range: 40 },
], ],
}, },
dangerouslyAllowMutability: true, dangerouslyAllowMutability: true,

View File

@ -2,7 +2,7 @@
export const onlyNumberInputChange = (e, callback) => { export const onlyNumberInputChange = (e, callback) => {
let value = e.target.value.replace(/^0+/, '') let value = e.target.value.replace(/^0+/, '')
value = value.replace(/[^-0-9]/g, '') value = value.replace(/[^-0-9]/g, '')
callback(value) callback(value, e)
} }
//소수점 둘째자리 숫자만 입력가능 //소수점 둘째자리 숫자만 입력가능
@ -12,9 +12,9 @@ export const onlyNumberWithDotInputChange = (e, callback) => {
const pattern = /^-?(\d{1,4}([.]\d{0,2})?)?$/ const pattern = /^-?(\d{1,4}([.]\d{0,2})?)?$/
if (!pattern.test(val)) { if (!pattern.test(val)) {
// prev에서 마지막 자리 제거 // prev에서 마지막 자리 제거
callback(val.slice(0, val.length - 1)) callback(val.slice(0, val.length - 1), e)
return return
} }
callback(val) callback(val, e)
} }