Merge branch 'dev' into dev-ds
This commit is contained in:
commit
48f5d8194d
@ -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 />
|
||||||
|
|||||||
@ -4,19 +4,27 @@ export const MENU = {
|
|||||||
ROOF_COVERING: {
|
ROOF_COVERING: {
|
||||||
EXTERIOR_WALL_LINE: 'exteriorWallLine', // 외벽선 그리기
|
EXTERIOR_WALL_LINE: 'exteriorWallLine', // 외벽선 그리기
|
||||||
ROOF_SHAPE_SETTINGS: 'roofShapeSettings', // 지붕형상 설정
|
ROOF_SHAPE_SETTINGS: 'roofShapeSettings', // 지붕형상 설정
|
||||||
|
ROOF_SHAPE_PASSIVITY_SETTINGS: 'roofShapePassivitySettings', // 지붕형상 수동설정
|
||||||
ROOF_SHAPE_EDITING: 'roofShapeEditing', // 지붕형상 편집
|
ROOF_SHAPE_EDITING: 'roofShapeEditing', // 지붕형상 편집
|
||||||
HELP_LINE_DRAWING: 'helpLineDrawing', // 보조선 그리기
|
HELP_LINE_DRAWING: 'helpLineDrawing', // 보조선 그리기
|
||||||
|
EAVES_KERAVA_EDIT: 'eavesKeravaEdit', // 처마.케라마 변경
|
||||||
|
MOVEMENT_SHAPE_UPDOWN: 'movementShapeUpdown', // 동선이동.형올림내림
|
||||||
|
OUTLINE_EDIT_OFFSET: 'outlineEditOffset', // 외벽선 편집 및 오프셋
|
||||||
|
ROOF_SHAPE_ALLOC: 'rootShapeAlloc', // 지붕면 항당
|
||||||
DEFAULT: 'roofCoveringDefault', // 아무것도 선택 안할 경우
|
DEFAULT: 'roofCoveringDefault', // 아무것도 선택 안할 경우
|
||||||
}, // 지붕덮개
|
}, // 지붕덮개
|
||||||
BATCH_CANVAS: {
|
BATCH_CANVAS: {
|
||||||
|
SLOPE_SETTING: 'slopeSetting', // 경사 설정
|
||||||
BATCH_DRAWING: 'batchDrawing', // 배치면 그리기
|
BATCH_DRAWING: 'batchDrawing', // 배치면 그리기
|
||||||
SURFACE_SHAPE_BATCH: 'surfaceShapeBatch', // 면형상 배치
|
SURFACE_SHAPE_BATCH: 'surfaceShapeBatch', // 면형상 배치
|
||||||
OBJECT_BATCH: 'objectBatch', // 오브젝트 배치
|
OBJECT_BATCH: 'objectBatch', // 오브젝트 배치
|
||||||
|
ALL_REMOVE: 'allRemove', // 전체 삭제
|
||||||
DEFAULT: 'batchCanvasDefault', // default
|
DEFAULT: 'batchCanvasDefault', // default
|
||||||
}, // 배치면
|
}, // 배치면
|
||||||
MODULE_CIRCUIT_SETTING: {
|
MODULE_CIRCUIT_SETTING: {
|
||||||
BASIC_SETTING: 'basicSetting', // 기본설정
|
BASIC_SETTING: 'basicSetting', // 기본설정
|
||||||
CIRCUIT_TRESTLE_SETTING: 'circuitTrestleSetting', // 회로가대설정
|
CIRCUIT_TRESTLE_SETTING: 'circuitTrestleSetting', // 회로가대설정
|
||||||
|
PLAN_ORIENTATION: 'planOrientation', // 도면 방위적용
|
||||||
DEFAULT: 'moduleCircuitSettingDefault',
|
DEFAULT: 'moduleCircuitSettingDefault',
|
||||||
}, // 모듈회로구성
|
}, // 모듈회로구성
|
||||||
ESTIMATE: 'estimate', // todo 견적서
|
ESTIMATE: 'estimate', // todo 견적서
|
||||||
|
|||||||
@ -119,17 +119,17 @@ export default function CanvasMenu(props) {
|
|||||||
<div className="canvas-side-btn-wrap">
|
<div className="canvas-side-btn-wrap">
|
||||||
{menuNumber !== 6 && menuNumber !== 5 && (
|
{menuNumber !== 6 && menuNumber !== 5 && (
|
||||||
<>
|
<>
|
||||||
|
{
|
||||||
|
<div className={`vertical-horizontal ${verticalHorizontalMode ? 'on' : ''}`}>
|
||||||
|
<span>{getMessage('plan.mode.vertical.horizontal')}</span>
|
||||||
|
<button onClick={() => setVerticalHorizontalMode(!verticalHorizontalMode)}>{verticalHorizontalMode ? 'ON' : 'OFF'}</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
<div className="btn-from">
|
<div className="btn-from">
|
||||||
<button className="btn01"></button>
|
<button className="btn01"></button>
|
||||||
<button className="btn02 active"></button>
|
<button className="btn02 active"></button>
|
||||||
<button className="btn03 "></button>
|
<button className="btn03 "></button>
|
||||||
</div>
|
</div>
|
||||||
{menuNumber !== 4 && (
|
|
||||||
<div className={`vertical-horizontal ${verticalHorizontalMode ? 'on' : ''}`}>
|
|
||||||
<span>{getMessage('plan.mode.vertical.horizontal')}</span>
|
|
||||||
<button onClick={() => setVerticalHorizontalMode(!verticalHorizontalMode)}>{verticalHorizontalMode ? 'ON' : 'OFF'}</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className="select-box">
|
<div className="select-box">
|
||||||
<QSelectBox title={'瓦53A'} option={SelectOption} />
|
<QSelectBox title={'瓦53A'} option={SelectOption} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { ToggleonMouse } from '@/components/header/Header'
|
|
||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { MENU } from '@/common/common'
|
import { MENU } from '@/common/common'
|
||||||
@ -30,23 +29,39 @@ export default function MenuDepth01(props) {
|
|||||||
// 지붕덮개
|
// 지붕덮개
|
||||||
{ id: 0, name: 'plan.menu.roof.cover.outline.drawing', menu: MENU.ROOF_COVERING.EXTERIOR_WALL_LINE },
|
{ id: 0, name: 'plan.menu.roof.cover.outline.drawing', menu: MENU.ROOF_COVERING.EXTERIOR_WALL_LINE },
|
||||||
{ id: 1, name: 'plan.menu.roof.cover.roof.shape.setting', menu: MENU.ROOF_COVERING.ROOF_SHAPE_SETTINGS },
|
{ id: 1, name: 'plan.menu.roof.cover.roof.shape.setting', menu: MENU.ROOF_COVERING.ROOF_SHAPE_SETTINGS },
|
||||||
{ id: 2, name: 'plan.menu.roof.cover.roof.shape.edit', menu: MENU.ROOF_COVERING.ROOF_SHAPE_EDITING },
|
{
|
||||||
|
id: 2,
|
||||||
|
name: 'plan.menu.roof.cover.roof.shape.passivity.setting',
|
||||||
|
menu: MENU.ROOF_COVERING.ROOF_SHAPE_PASSIVITY_SETTINGS,
|
||||||
|
},
|
||||||
{ id: 3, name: 'plan.menu.roof.cover.auxiliary.line.drawing', menu: MENU.ROOF_COVERING.HELP_LINE_DRAWING },
|
{ id: 3, name: 'plan.menu.roof.cover.auxiliary.line.drawing', menu: MENU.ROOF_COVERING.HELP_LINE_DRAWING },
|
||||||
|
{ id: 4, name: 'plan.menu.roof.cover.eaves.kerava.edit', menu: MENU.ROOF_COVERING.EAVES_KERAVA_EDIT },
|
||||||
|
{ id: 5, name: 'plan.menu.roof.cover.movement.shape.updown', menu: MENU.ROOF_COVERING.MOVEMENT_SHAPE_UPDOWN },
|
||||||
|
{ id: 6, name: 'plan.menu.roof.cover.outline.edit.offset', menu: MENU.ROOF_COVERING.OUTLINE_EDIT_OFFSET },
|
||||||
|
{ id: 7, name: 'plan.menu.roof.cover.roof.surface.alloc', menu: MENU.ROOF_COVERING.ROOF_SHAPE_ALLOC },
|
||||||
],
|
],
|
||||||
surface: [
|
surface: [
|
||||||
// 배치면
|
// 배치면
|
||||||
{ id: 0, name: 'plan.menu.placement.surface.drawing', menu: MENU.BATCH_CANVAS.BATCH_DRAWING },
|
{ id: 0, name: 'plan.menu.placement.surface.slope.setting', menu: MENU.BATCH_CANVAS.SLOPE_SETTING },
|
||||||
{ id: 1, name: 'plan.menu.placement.surface.surface', menu: MENU.BATCH_CANVAS.SURFACE_SHAPE_BATCH },
|
{ id: 1, name: 'plan.menu.placement.surface.drawing', menu: MENU.BATCH_CANVAS.BATCH_DRAWING },
|
||||||
{ id: 2, name: 'plan.menu.placement.surface.object', menu: MENU.BATCH_CANVAS.OBJECT_BATCH },
|
{ id: 2, name: 'plan.menu.placement.surface.arrangement', menu: MENU.BATCH_CANVAS.SURFACE_SHAPE_BATCH },
|
||||||
|
{ id: 3, name: 'plan.menu.placement.surface.object', menu: MENU.BATCH_CANVAS.OBJECT_BATCH },
|
||||||
|
{ id: 4, name: 'plan.menu.placement.surface.all.remove', menu: MENU.BATCH_CANVAS.ALL_REMOVE },
|
||||||
],
|
],
|
||||||
module: [
|
module: [
|
||||||
// 모듈, 회로 구성
|
// 모듈, 회로 구성
|
||||||
|
|
||||||
{ id: 0, name: 'plan.menu.module.circuit.setting.default', menu: MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING },
|
{ id: 0, name: 'plan.menu.module.circuit.setting.default', menu: MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING },
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
name: 'plan.menu.module.circuit.setting.circuit.trestle.setting',
|
name: 'plan.menu.module.circuit.setting.circuit.trestle.setting',
|
||||||
menu: MENU.MODULE_CIRCUIT_SETTING.CIRCUIT_TRESTLE_SETTING,
|
menu: MENU.MODULE_CIRCUIT_SETTING.CIRCUIT_TRESTLE_SETTING,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: 'plan.menu.module.circuit.setting.plan.orientation',
|
||||||
|
menu: MENU.MODULE_CIRCUIT_SETTING.PLAN_ORIENTATION,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
@ -60,17 +75,6 @@ export default function MenuDepth01(props) {
|
|||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
<ul className="canvas-depth2-btn-list">
|
|
||||||
<li className="depth2-btn-box" onMouseEnter={(e) => ToggleonMouse(e, 'add', 'ul')} onMouseLeave={(e) => ToggleonMouse(e, 'remove', 'ul')}>
|
|
||||||
<button>{getMessage('plan.menu.estimate.roof.alloc')}</button>
|
|
||||||
</li>
|
|
||||||
<li className="depth2-btn-box" onMouseEnter={(e) => ToggleonMouse(e, 'add', 'ul')} onMouseLeave={(e) => ToggleonMouse(e, 'remove', 'ul')}>
|
|
||||||
<button>屋根材の設定と変更</button>
|
|
||||||
</li>
|
|
||||||
<li className="depth2-btn-box" onMouseEnter={(e) => ToggleonMouse(e, 'add', 'ul')} onMouseLeave={(e) => ToggleonMouse(e, 'remove', 'ul')}>
|
|
||||||
<button>屋根面全体削除</button>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -113,11 +113,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 (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@ -14,7 +14,7 @@ export default function SettingModal01(props) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<WithDraggable isShow={true} pos={{ x: 50, y: 30 }}>
|
<WithDraggable isShow={true} pos={{ x: 50, y: 30 }}>
|
||||||
<div className={`modal-pop-wrap sm`}>
|
<div className={`modal-pop-wrap sm mount`}>
|
||||||
<div className="modal-head">
|
<div className="modal-head">
|
||||||
<h1 className="title">{getMessage('modal.canvas.setting')}</h1>
|
<h1 className="title">{getMessage('modal.canvas.setting')}</h1>
|
||||||
<button className="modal-close" onClick={() => setShowCanvasSettingModal(false)}>
|
<button className="modal-close" onClick={() => setShowCanvasSettingModal(false)}>
|
||||||
|
|||||||
@ -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()}>
|
||||||
|
|||||||
45
src/hooks/useAdsorptionPoint.js
Normal file
45
src/hooks/useAdsorptionPoint.js
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
51
src/hooks/useDotLineGrid.js
Normal file
51
src/hooks/useDotLineGrid.js
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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
23
src/hooks/useMouse.js
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -38,12 +38,15 @@
|
|||||||
"plan.menu.roof.cover.roof.edit": "지붕형상 편집",
|
"plan.menu.roof.cover.roof.edit": "지붕형상 편집",
|
||||||
"plan.menu.roof.cover.sub.line": "補助線を描",
|
"plan.menu.roof.cover.sub.line": "補助線を描",
|
||||||
"plan.menu.placement.surface": "配置面",
|
"plan.menu.placement.surface": "配置面",
|
||||||
"plan.menu.placement.surface.drawing": "배치면 그리기",
|
"plan.menu.placement.surface.slope.setting": "傾斜設定",
|
||||||
"plan.menu.placement.surface.surface": "면형상 배치",
|
"plan.menu.placement.surface.drawing": "配置面の描画",
|
||||||
"plan.menu.placement.surface.object": "오브젝트 배치",
|
"plan.menu.placement.surface.arrangement": "面形状の配置",
|
||||||
|
"plan.menu.placement.surface.object": "オブジェクトの配置",
|
||||||
|
"plan.menu.placement.surface.all.remove": "配置面全体を削除",
|
||||||
"plan.menu.module.circuit.setting": "モジュール回路構成",
|
"plan.menu.module.circuit.setting": "モジュール回路構成",
|
||||||
"plan.menu.module.circuit.setting.default": "기본 설정",
|
"plan.menu.module.circuit.setting.default": "基本設定",
|
||||||
"plan.menu.module.circuit.setting.circuit.trestle.setting": "회로 및 가대 설정",
|
"plan.menu.module.circuit.setting.circuit.trestle.setting": "回路と架台の設定",
|
||||||
|
"plan.menu.module.circuit.setting.plan.orientation": "図面方位の適用",
|
||||||
"plan.menu.estimate": "見積",
|
"plan.menu.estimate": "見積",
|
||||||
"plan.menu.estimate.roof.alloc": "屋根面の割り当て",
|
"plan.menu.estimate.roof.alloc": "屋根面の割り当て",
|
||||||
"plan.menu.estimate.save": "保存",
|
"plan.menu.estimate.save": "保存",
|
||||||
|
|||||||
@ -18,9 +18,13 @@
|
|||||||
"plan.menu.roof.cover": "지붕덮개",
|
"plan.menu.roof.cover": "지붕덮개",
|
||||||
"plan.menu.roof.cover.outline.drawing": "외벽선 그리기",
|
"plan.menu.roof.cover.outline.drawing": "외벽선 그리기",
|
||||||
"plan.menu.roof.cover.roof.shape.setting": "지붕형상 설정",
|
"plan.menu.roof.cover.roof.shape.setting": "지붕형상 설정",
|
||||||
"plan.menu.roof.cover.roof.shape.edit": "지붕형상 편집",
|
"plan.menu.roof.cover.roof.shape.passivity.setting": "지붕형상 수동 설정",
|
||||||
"plan.menu.roof.cover.auxiliary.line.drawing": "보조선 그리기",
|
"plan.menu.roof.cover.eaves.kerava.edit": "처마·케라바 변경",
|
||||||
|
"plan.menu.roof.cover.movement.shape.updown": "동선이동·형올림내림",
|
||||||
|
"plan.menu.roof.cover.outline.edit.offset": "외벽선 편집 및 오프셋",
|
||||||
"plan.menu.roof.cover.roof.surface.alloc": "지붕면 할당",
|
"plan.menu.roof.cover.roof.surface.alloc": "지붕면 할당",
|
||||||
|
"plan.menu.roof.cover.roof.shape.edit": "지붕형상 편집",
|
||||||
|
"plan.menu.roof.cover.auxiliary.line.drawing": "보조선 작성",
|
||||||
"modal.cover.outline.drawing": "외벽선 작성",
|
"modal.cover.outline.drawing": "외벽선 작성",
|
||||||
"modal.cover.outline": "외벽선",
|
"modal.cover.outline": "외벽선",
|
||||||
"modal.cover.outline.right.angle": "직각",
|
"modal.cover.outline.right.angle": "직각",
|
||||||
@ -35,12 +39,15 @@
|
|||||||
"modal.cover.outline.remove": "외벽 제거",
|
"modal.cover.outline.remove": "외벽 제거",
|
||||||
"modal.cover.outline.select.move": "외벽 선택, 이동",
|
"modal.cover.outline.select.move": "외벽 선택, 이동",
|
||||||
"plan.menu.placement.surface": "배치면",
|
"plan.menu.placement.surface": "배치면",
|
||||||
|
"plan.menu.placement.surface.slope.setting": "경사설정",
|
||||||
"plan.menu.placement.surface.drawing": "배치면 그리기",
|
"plan.menu.placement.surface.drawing": "배치면 그리기",
|
||||||
"plan.menu.placement.surface.surface": "면형상 배치",
|
"plan.menu.placement.surface.arrangement": "면형상 배치",
|
||||||
"plan.menu.placement.surface.object": "오브젝트 배치",
|
"plan.menu.placement.surface.object": "오브젝트 배치",
|
||||||
|
"plan.menu.placement.surface.all.remove": "배치면 전체 삭제",
|
||||||
"plan.menu.module.circuit.setting": "모듈,회로 구성",
|
"plan.menu.module.circuit.setting": "모듈,회로 구성",
|
||||||
"plan.menu.module.circuit.setting.default": "기본 설정",
|
"plan.menu.module.circuit.setting.default": "기본 설정",
|
||||||
"plan.menu.module.circuit.setting.circuit.trestle.setting": "회로 및 가대 설정",
|
"plan.menu.module.circuit.setting.circuit.trestle.setting": "회로 및 가대 설정",
|
||||||
|
"plan.menu.module.circuit.setting.plan.orientation": "도면 방위 적용",
|
||||||
"plan.menu.estimate": "견적서",
|
"plan.menu.estimate": "견적서",
|
||||||
"plan.menu.estimate.roof.alloc": "지붕면 할당",
|
"plan.menu.estimate.roof.alloc": "지붕면 할당",
|
||||||
"plan.menu.estimate.save": "저장",
|
"plan.menu.estimate.save": "저장",
|
||||||
|
|||||||
@ -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({
|
||||||
@ -219,6 +219,38 @@ export const adsorptionRangeState = atom({
|
|||||||
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),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
// canvas plan 초기 목록
|
// canvas plan 초기 목록
|
||||||
export const initCanvasPlansState = atom({
|
export const initCanvasPlansState = atom({
|
||||||
key: 'initCanvasPlans',
|
key: 'initCanvasPlans',
|
||||||
|
|||||||
@ -50,9 +50,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,
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user