Merge branch 'dev' into dev-yj
# Conflicts: # src/components/GridSettingsModal.jsx
This commit is contained in:
commit
fd118d01b2
@ -1,6 +1,6 @@
|
|||||||
NEXT_PUBLIC_TEST="테스트변수입니다. development"
|
NEXT_PUBLIC_TEST="테스트변수입니다. development"
|
||||||
|
|
||||||
NEXT_PUBLIC_API_SERVER_PATH="http://1.248.227.176:38080"
|
NEXT_PUBLIC_API_SERVER_PATH="http://1.248.227.176:38080"
|
||||||
# NEXT_PUBLIC_API_SERVER_PATH="http://localhost:8080"
|
# NEXT_PUBLIC_API_SERVER_PATH="http://localhost:8080"
|
||||||
|
|
||||||
DATABASE_URL="sqlserver://mssql.devgrr.kr:1433;database=qcast;user=qcast;password=Qwertqaz12345;trustServerCertificate=true"
|
DATABASE_URL="sqlserver://mssql.devgrr.kr:1433;database=qcast;user=qcast;password=Qwertqaz12345;trustServerCertificate=true"
|
||||||
|
|||||||
@ -31,6 +31,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@turf/turf": "^7.0.0",
|
"@turf/turf": "^7.0.0",
|
||||||
|
"dayjs": "^1.11.13",
|
||||||
"postcss": "^8",
|
"postcss": "^8",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.3.3",
|
||||||
"prisma": "^5.18.0",
|
"prisma": "^5.18.0",
|
||||||
|
|||||||
10
qcast-front.iml
Normal file
10
qcast-front.iml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="WEB_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$/qcast-front">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/qcast-front/build" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
16
src/app/[locale]/settings/page.jsx
Normal file
16
src/app/[locale]/settings/page.jsx
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import Hero from '@/components/Hero'
|
||||||
|
import Settings from '@/components/Settings'
|
||||||
|
import { initCheck } from '@/util/session-util'
|
||||||
|
|
||||||
|
export default async function SettingsPage() {
|
||||||
|
await initCheck()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Hero title="Canvas Setting" />
|
||||||
|
<div className="flex flex-col justify-center my-8">
|
||||||
|
<Settings />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -2,7 +2,7 @@ import { useEffect, useRef, useState } from 'react'
|
|||||||
import { Button, Checkbox, CheckboxGroup, RadioGroup, Radio, Input } from '@nextui-org/react'
|
import { Button, Checkbox, CheckboxGroup, RadioGroup, Radio, Input } from '@nextui-org/react'
|
||||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||||
import { modalContent, modalState } from '@/store/modalAtom'
|
import { modalContent, modalState } from '@/store/modalAtom'
|
||||||
import { guideLineState } from '@/store/canvasAtom'
|
import { guideLineState, horiGuideLinesState, vertGuideLinesState } from '@/store/canvasAtom'
|
||||||
import { fabric } from 'fabric'
|
import { fabric } from 'fabric'
|
||||||
import { ColorPicker, useColor } from 'react-color-palette'
|
import { ColorPicker, useColor } from 'react-color-palette'
|
||||||
import 'react-color-palette/css'
|
import 'react-color-palette/css'
|
||||||
@ -18,6 +18,8 @@ export default function SettingsModal(props) {
|
|||||||
|
|
||||||
const [open, setOpen] = useRecoilState(modalState)
|
const [open, setOpen] = useRecoilState(modalState)
|
||||||
const [guideLine, setGuideLine] = useRecoilState(guideLineState)
|
const [guideLine, setGuideLine] = useRecoilState(guideLineState)
|
||||||
|
const [horiGuideLines, setHoriGuideLines] = useRecoilState(horiGuideLinesState)
|
||||||
|
const [vertGuideLines, setVertGuideLines] = useRecoilState(vertGuideLinesState)
|
||||||
|
|
||||||
const gridSettingArray = []
|
const gridSettingArray = []
|
||||||
|
|
||||||
@ -76,6 +78,7 @@ export default function SettingsModal(props) {
|
|||||||
name: 'guideLine',
|
name: 'guideLine',
|
||||||
strokeDashArray: [5, 2],
|
strokeDashArray: [5, 2],
|
||||||
opacity: 0.3,
|
opacity: 0.3,
|
||||||
|
direction: 'horizontal',
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
canvasProps.add(horizontalLine)
|
canvasProps.add(horizontalLine)
|
||||||
@ -97,6 +100,7 @@ export default function SettingsModal(props) {
|
|||||||
name: 'guideLine',
|
name: 'guideLine',
|
||||||
strokeDashArray: [5, 2],
|
strokeDashArray: [5, 2],
|
||||||
opacity: 0.3,
|
opacity: 0.3,
|
||||||
|
direction: 'vertical',
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
canvasProps.add(verticalLine)
|
canvasProps.add(verticalLine)
|
||||||
@ -114,6 +118,16 @@ export default function SettingsModal(props) {
|
|||||||
moduleHoriLength: moduleHoriLength,
|
moduleHoriLength: moduleHoriLength,
|
||||||
}
|
}
|
||||||
gridSettingArray.push(recoilObj)
|
gridSettingArray.push(recoilObj)
|
||||||
|
const newHoriGuideLines = [...horiGuideLines]
|
||||||
|
horizontalLineArray.forEach((line) => {
|
||||||
|
newHoriGuideLines.push(line)
|
||||||
|
})
|
||||||
|
const newVertGuideLines = [...vertGuideLines]
|
||||||
|
verticalLineArray.forEach((line) => {
|
||||||
|
newVertGuideLines.push(line)
|
||||||
|
})
|
||||||
|
setHoriGuideLines(newHoriGuideLines)
|
||||||
|
setVertGuideLines(newVertGuideLines)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gridCheckedValue.includes('dot')) {
|
if (gridCheckedValue.includes('dot')) {
|
||||||
@ -187,6 +201,8 @@ export default function SettingsModal(props) {
|
|||||||
guideLines?.forEach((item) => canvasProps.remove(item))
|
guideLines?.forEach((item) => canvasProps.remove(item))
|
||||||
canvasProps.renderAll()
|
canvasProps.renderAll()
|
||||||
setGuideLine([])
|
setGuideLine([])
|
||||||
|
setHoriGuideLines([])
|
||||||
|
setVertGuideLines([])
|
||||||
} else {
|
} else {
|
||||||
alert('그리드가 없습니다.')
|
alert('그리드가 없습니다.')
|
||||||
return
|
return
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
197
src/components/Settings.jsx
Normal file
197
src/components/Settings.jsx
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { Button } from '@nextui-org/react';
|
||||||
|
|
||||||
|
import { get, post } from '@/lib/Axios';
|
||||||
|
|
||||||
|
export default function Settings() {
|
||||||
|
const [objectNo, setObjectNo] = useState('test123240829010');
|
||||||
|
const [error, setError] = useState(null);
|
||||||
|
|
||||||
|
// 상태를 하나의 객체로 관리
|
||||||
|
const [settings, setSettings] = useState({
|
||||||
|
display1: Array(11).fill('N'), // 화면 표시1
|
||||||
|
display2: Array(3).fill('N'), // 화면 표시2
|
||||||
|
rangeSetting: 0, // 흡착 범위 설정
|
||||||
|
gridSettings: [] // 그리드 설정
|
||||||
|
});
|
||||||
|
|
||||||
|
const gridItems = {
|
||||||
|
display1: [
|
||||||
|
'할당 표시', '도면 표시', '그리드 표시', '문자 표시', '흐름방향 표시',
|
||||||
|
'복도치수 표시', '실제치수 표시', '치수 표시 없음', '가대 표시',
|
||||||
|
'좌표 표시', '도면전환 표시'
|
||||||
|
],
|
||||||
|
display2: ['테두리만', '라인해치', 'All Painted'],
|
||||||
|
rangeSetting: ['극소', '소', '중', '대'],
|
||||||
|
gridSettings: ['임의 그리드', '실선 그리드', '점 그리드', '그리드 색 설정', '흡착점 추가']
|
||||||
|
};
|
||||||
|
|
||||||
|
// 클릭 시 상태 변경 함수
|
||||||
|
const handleToggle = (type, index) => {
|
||||||
|
|
||||||
|
setSettings((prevSettings) => {
|
||||||
|
|
||||||
|
// prevSettings[type]이 배열인지 확인하고, 그렇지 않은 경우 빈 배열로 초기화
|
||||||
|
let updated = Array.isArray(prevSettings[type]) ? [...prevSettings[type]] : [];
|
||||||
|
|
||||||
|
if (type === 'rangeSetting') {
|
||||||
|
return { ...prevSettings, [type]: index };
|
||||||
|
}
|
||||||
|
|
||||||
|
updated[index] = updated[index] === 'N' ? 'Y' : 'N';
|
||||||
|
return { ...prevSettings, [type]: updated };
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Canvas Setting 조회 및 초기화
|
||||||
|
const handleSelect = async () => {
|
||||||
|
try {
|
||||||
|
const res = await get({ url: `/api/canvas-management/canvas-settings/by-object/${objectNo}` });
|
||||||
|
|
||||||
|
// 데이터가 없는 경우
|
||||||
|
if (!res || res.length === 0) {
|
||||||
|
|
||||||
|
console.warn('조회 결과가 없습니다.');
|
||||||
|
// 기본값을 설정하거나 사용자에게 알림 표시
|
||||||
|
setSettings({
|
||||||
|
display1: Array(11).fill('N'), // 화면 표시1 기본값
|
||||||
|
display2: Array(3).fill('N'), // 화면 표시2 기본값
|
||||||
|
rangeSetting: 0, // 흡착 범위 설정 기본값
|
||||||
|
gridSettings: [] // 그리드 설정 초기화
|
||||||
|
});
|
||||||
|
|
||||||
|
alert('조회된 데이터가 없습니다. 기본 설정이 적용됩니다.');
|
||||||
|
return; // 이후 코드 실행을 중단
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = res.map((item) => ({
|
||||||
|
display1: [
|
||||||
|
item.assignDisplay, item.drawDisplay, item.gridDisplay, item.charDisplay, item.flowDisplay,
|
||||||
|
item.hallwayDimenDisplay, item.actualDimenDisplay, item.noDimenDisplay, item.trestleDisplay,
|
||||||
|
item.coordiDisplay, item.drawConverDisplay
|
||||||
|
],
|
||||||
|
display2: [item.onlyBorder, item.lineHatch, item.allPainted],
|
||||||
|
rangeSetting: Number(item.adsorpRangeSetting)
|
||||||
|
}));
|
||||||
|
|
||||||
|
setSettings({
|
||||||
|
display1: data[0].display1,
|
||||||
|
display2: data[0].display2,
|
||||||
|
rangeSetting: data[0].rangeSetting,
|
||||||
|
gridSettings: [] // 초기화
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Data fetching error:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Canvas Setting 저장
|
||||||
|
const handleSubmit = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (!objectNo) {
|
||||||
|
alert('object_no를 입력하세요.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const patternData = {
|
||||||
|
objectNo,
|
||||||
|
assignDisplay: settings.display1[0],
|
||||||
|
drawDisplay: settings.display1[1],
|
||||||
|
gridDisplay: settings.display1[2],
|
||||||
|
charDisplay: settings.display1[3],
|
||||||
|
flowDisplay: settings.display1[4],
|
||||||
|
hallwayDimenDisplay: settings.display1[5],
|
||||||
|
actualDimenDisplay: settings.display1[6],
|
||||||
|
noDimenDisplay: settings.display1[7],
|
||||||
|
trestleDisplay: settings.display1[8],
|
||||||
|
coordiDisplay: settings.display1[9],
|
||||||
|
drawConverDisplay: settings.display1[10],
|
||||||
|
onlyBorder: settings.display2[0],
|
||||||
|
lineHatch: settings.display2[1],
|
||||||
|
allPainted: settings.display2[2],
|
||||||
|
adsorpRangeSetting: String(settings.rangeSetting)
|
||||||
|
};
|
||||||
|
|
||||||
|
await post({ url: `/api/canvas-management/canvas-settings`, data: patternData });
|
||||||
|
|
||||||
|
// 저장 후 재조회
|
||||||
|
handleSelect();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 초기 조회
|
||||||
|
useEffect(() => {
|
||||||
|
if (objectNo) {
|
||||||
|
handleSelect(objectNo);
|
||||||
|
} else {
|
||||||
|
alert('object_no를 입력하세요.');
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="container mx-auto p-4 m-4 border">
|
||||||
|
<div align="right">
|
||||||
|
<input type="text" placeholder="Object No 입력" value={objectNo} onChange={(e) => setObjectNo(e.target.value)} />
|
||||||
|
<Button onClick={handleSelect}>조회</Button>
|
||||||
|
<Button onClick={handleSubmit}>저장</Button>
|
||||||
|
</div>
|
||||||
|
<div className="container mx-auto p-4 m-4 border">
|
||||||
|
<h1>[디스플레이 설정]</h1>
|
||||||
|
<h1>* 도면에 표시할 항목을 클릭하면 적용 됩니다.</h1>
|
||||||
|
<div className="grid-container2">
|
||||||
|
{gridItems.display1.map((item, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className={`grid-item ${settings.display1[index] === 'Y' ? 'selected' : 'unselected'}`}
|
||||||
|
onClick={() => handleToggle('display1', index)}
|
||||||
|
>
|
||||||
|
{settings.display1[index]} {item}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<h1>* 화면 표시</h1>
|
||||||
|
<div className="grid-container3">
|
||||||
|
{gridItems.display2.map((item, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className={`grid-item ${settings.display2[index] === 'Y' ? 'selected' : 'unselected'}`}
|
||||||
|
onClick={() => handleToggle('display2', index)}
|
||||||
|
>
|
||||||
|
{settings.display2[index]} {item}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<h1>[글꼴/도면크기 설정]</h1>
|
||||||
|
<h1>* 흡착 범위 설정</h1>
|
||||||
|
<div className="grid-container4">
|
||||||
|
{gridItems.rangeSetting.map((item, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className={`grid-item ${settings.rangeSetting === index ? 'selected' : 'unselected'}`}
|
||||||
|
onClick={() => handleToggle('rangeSetting', index)}
|
||||||
|
>
|
||||||
|
{settings.rangeSetting === index ? 'Y' : 'N'} {item}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<h1>[그리드 설정]</h1>
|
||||||
|
<div className="grid-container2 border">
|
||||||
|
{gridItems.gridSettings.map((item, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className={`grid-item ${settings.gridSettings.includes(index) ? 'selected' : 'unselected'}`}
|
||||||
|
onClick={() => handleToggle('gridSettings', index)}
|
||||||
|
>
|
||||||
|
{settings.gridSettings.includes(index) ? 'Y' : 'N'} {item}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,108 +0,0 @@
|
|||||||
import { fabric } from 'fabric'
|
|
||||||
|
|
||||||
export class QLine extends fabric.Group {
|
|
||||||
line
|
|
||||||
text
|
|
||||||
fontSize
|
|
||||||
length = 0
|
|
||||||
x1
|
|
||||||
y1
|
|
||||||
x2
|
|
||||||
y2
|
|
||||||
direction
|
|
||||||
type = 'HelpLine'
|
|
||||||
parent
|
|
||||||
#lengthTxt = 0
|
|
||||||
|
|
||||||
constructor(points, option, lengthTxt) {
|
|
||||||
const [x1, y1, x2, y2] = points
|
|
||||||
|
|
||||||
if (!option.fontSize) {
|
|
||||||
throw new Error('Font size is required.')
|
|
||||||
}
|
|
||||||
|
|
||||||
const line = new fabric.Line(points, { ...option, strokeWidth: 1 })
|
|
||||||
super([line], {})
|
|
||||||
|
|
||||||
this.x1 = x1
|
|
||||||
this.y1 = y1
|
|
||||||
this.x2 = x2
|
|
||||||
this.y2 = y2
|
|
||||||
this.line = line
|
|
||||||
this.fontSize = option.fontSize
|
|
||||||
this.direction = option.direction
|
|
||||||
this.parent = option.parent
|
|
||||||
|
|
||||||
if (lengthTxt > 0) {
|
|
||||||
this.#lengthTxt = Number(lengthTxt)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.#init()
|
|
||||||
this.#addControl()
|
|
||||||
}
|
|
||||||
|
|
||||||
#init() {
|
|
||||||
this.#addLengthText(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
#addControl() {
|
|
||||||
this.on('moving', () => {
|
|
||||||
this.#addLengthText(false)
|
|
||||||
})
|
|
||||||
|
|
||||||
this.on('modified', (e) => {
|
|
||||||
this.#addLengthText(false)
|
|
||||||
})
|
|
||||||
|
|
||||||
this.on('selected', () => {
|
|
||||||
Object.keys(this.controls).forEach((controlKey) => {
|
|
||||||
if (controlKey !== 'ml' && controlKey !== 'mr') {
|
|
||||||
this.setControlVisible(controlKey, false)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#addLengthText(isFirst) {
|
|
||||||
if (this.text) {
|
|
||||||
this.removeWithUpdate(this.text)
|
|
||||||
this.text = null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isFirst && this.#lengthTxt > 0) {
|
|
||||||
const text = new fabric.Textbox(this.#lengthTxt.toFixed(0).toString(), {
|
|
||||||
left: (this.x1 + this.x2) / 2,
|
|
||||||
top: (this.y1 + this.y2) / 2,
|
|
||||||
fontSize: this.fontSize,
|
|
||||||
})
|
|
||||||
this.length = this.#lengthTxt
|
|
||||||
this.text = text
|
|
||||||
this.addWithUpdate(text)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const scaleX = this.scaleX
|
|
||||||
const scaleY = this.scaleY
|
|
||||||
const x1 = this.left
|
|
||||||
const y1 = this.top
|
|
||||||
const x2 = this.left + this.width * scaleX
|
|
||||||
const y2 = this.top + this.height * scaleY
|
|
||||||
const dx = x2 - x1
|
|
||||||
const dy = y2 - y1
|
|
||||||
this.length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(0))
|
|
||||||
|
|
||||||
const text = new fabric.Textbox(this.length.toFixed(0).toString(), {
|
|
||||||
left: (x1 + x2) / 2,
|
|
||||||
top: (y1 + y2) / 2,
|
|
||||||
fontSize: this.fontSize,
|
|
||||||
})
|
|
||||||
this.text = text
|
|
||||||
this.addWithUpdate(text)
|
|
||||||
}
|
|
||||||
|
|
||||||
setFontSize(fontSize) {
|
|
||||||
this.fontSize = fontSize
|
|
||||||
this.text.set({ fontSize })
|
|
||||||
this.addWithUpdate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -18,6 +18,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
|||||||
parentId: null,
|
parentId: null,
|
||||||
innerLines: [],
|
innerLines: [],
|
||||||
children: [],
|
children: [],
|
||||||
|
initOptions: null,
|
||||||
initialize: function (points, options, canvas) {
|
initialize: function (points, options, canvas) {
|
||||||
// 소수점 전부 제거
|
// 소수점 전부 제거
|
||||||
points.forEach((point) => {
|
points.forEach((point) => {
|
||||||
@ -58,6 +59,8 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
|||||||
this.canvas = canvas
|
this.canvas = canvas
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.initOptions = options
|
||||||
|
|
||||||
this.init()
|
this.init()
|
||||||
this.initLines()
|
this.initLines()
|
||||||
this.setShape()
|
this.setShape()
|
||||||
@ -177,7 +180,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
|||||||
const degree = (Math.atan2(dy, dx) * 180) / Math.PI
|
const degree = (Math.atan2(dy, dx) * 180) / Math.PI
|
||||||
|
|
||||||
// Create new text object if it doesn't exist
|
// Create new text object if it doesn't exist
|
||||||
const text = new fabric.Text(length.toFixed(0), {
|
const text = new fabric.IText(length.toFixed(0), {
|
||||||
left: midPoint.x,
|
left: midPoint.x,
|
||||||
top: midPoint.y,
|
top: midPoint.y,
|
||||||
fontSize: this.fontSize,
|
fontSize: this.fontSize,
|
||||||
@ -189,7 +192,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
|||||||
parentDirection: getDirectionByPoint(start, end),
|
parentDirection: getDirectionByPoint(start, end),
|
||||||
parentDegree: degree,
|
parentDegree: degree,
|
||||||
dirty: true,
|
dirty: true,
|
||||||
editable: false,
|
editable: true,
|
||||||
selectable: true,
|
selectable: true,
|
||||||
lockRotation: true,
|
lockRotation: true,
|
||||||
lockScalingX: true,
|
lockScalingX: true,
|
||||||
|
|||||||
@ -425,6 +425,8 @@ export function useCanvas(id) {
|
|||||||
'maxY',
|
'maxY',
|
||||||
'minX',
|
'minX',
|
||||||
'minY',
|
'minY',
|
||||||
|
'x',
|
||||||
|
'y',
|
||||||
])
|
])
|
||||||
|
|
||||||
const str = JSON.stringify(objs)
|
const str = JSON.stringify(objs)
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { useEffect, useState } from 'react'
|
|||||||
import { fabric } from 'fabric'
|
import { fabric } from 'fabric'
|
||||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||||
import { canvasSizeState, currentObjectState, modeState } from '@/store/canvasAtom'
|
import { canvasSizeState, currentObjectState, modeState } from '@/store/canvasAtom'
|
||||||
|
import { QPolygon } from '@/components/fabric/QPolygon'
|
||||||
|
|
||||||
// 캔버스에 필요한 이벤트
|
// 캔버스에 필요한 이벤트
|
||||||
export function useCanvasEvent() {
|
export function useCanvasEvent() {
|
||||||
@ -19,9 +20,9 @@ export function useCanvasEvent() {
|
|||||||
canvas?.on('selection:cleared', selectionEvent.cleared)
|
canvas?.on('selection:cleared', selectionEvent.cleared)
|
||||||
canvas?.on('selection:created', selectionEvent.created)
|
canvas?.on('selection:created', selectionEvent.created)
|
||||||
canvas?.on('selection:updated', selectionEvent.updated)
|
canvas?.on('selection:updated', selectionEvent.updated)
|
||||||
canvas?.on('object:added', () => {
|
/*canvas?.on('object:added', () => {
|
||||||
document.addEventListener('keydown', handleKeyDown)
|
document.addEventListener('keydown', handleKeyDown)
|
||||||
})
|
})*/
|
||||||
canvas?.on('object:removed', objectEvent.removed)
|
canvas?.on('object:removed', objectEvent.removed)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,11 +78,55 @@ export function useCanvasEvent() {
|
|||||||
if (target.name === 'lengthText') {
|
if (target.name === 'lengthText') {
|
||||||
const x = target.left
|
const x = target.left
|
||||||
const y = target.top
|
const y = target.top
|
||||||
|
// Add a property to store the previous value
|
||||||
|
const previousValue = target.text
|
||||||
target.on('selected', (e) => {
|
target.on('selected', (e) => {
|
||||||
Object.keys(target.controls).forEach((controlKey) => {
|
Object.keys(target.controls).forEach((controlKey) => {
|
||||||
target.setControlVisible(controlKey, false)
|
target.setControlVisible(controlKey, false)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
target.on('editing:exited', () => {
|
||||||
|
if (isNaN(target.text.trim())) {
|
||||||
|
target.set({ text: previousValue })
|
||||||
|
canvas?.renderAll()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const updatedValue = parseFloat(target.text.trim())
|
||||||
|
const targetParent = target.parent
|
||||||
|
const points = targetParent.getCurrentPoints()
|
||||||
|
const i = target.idx // Assuming target.index gives the index of the point
|
||||||
|
|
||||||
|
const startPoint = points[i]
|
||||||
|
const endPoint = points[(i + 1) % points.length]
|
||||||
|
|
||||||
|
const dx = endPoint.x - startPoint.x
|
||||||
|
const dy = endPoint.y - startPoint.y
|
||||||
|
|
||||||
|
const currentLength = Math.sqrt(dx * dx + dy * dy)
|
||||||
|
const scaleFactor = updatedValue / currentLength
|
||||||
|
|
||||||
|
const newEndPoint = {
|
||||||
|
x: startPoint.x + dx * scaleFactor,
|
||||||
|
y: startPoint.y + dy * scaleFactor,
|
||||||
|
}
|
||||||
|
|
||||||
|
const newPoints = [...points]
|
||||||
|
newPoints[(i + 1) % points.length] = newEndPoint
|
||||||
|
|
||||||
|
for (let idx = i + 1; idx < points.length; idx++) {
|
||||||
|
if (newPoints[idx].x === endPoint.x) {
|
||||||
|
newPoints[idx].x = newEndPoint.x
|
||||||
|
} else if (newPoints[idx].y === endPoint.y) {
|
||||||
|
newPoints[idx].y = newEndPoint.y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const newPolygon = new QPolygon(newPoints, targetParent.initOptions)
|
||||||
|
canvas?.add(newPolygon)
|
||||||
|
canvas?.remove(targetParent)
|
||||||
|
canvas?.renderAll()
|
||||||
|
})
|
||||||
|
|
||||||
target.on('moving', (e) => {
|
target.on('moving', (e) => {
|
||||||
if (target.parentDirection === 'left' || target.parentDirection === 'right') {
|
if (target.parentDirection === 'left' || target.parentDirection === 'right') {
|
||||||
const minX = target.minX
|
const minX = target.minX
|
||||||
@ -116,7 +161,6 @@ export function useCanvasEvent() {
|
|||||||
if (whiteList.includes(e.target.name)) {
|
if (whiteList.includes(e.target.name)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
console.log('removed', e)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useRef, useState } from 'react'
|
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||||
import {
|
import {
|
||||||
calculateIntersection,
|
calculateIntersection,
|
||||||
distanceBetweenPoints,
|
distanceBetweenPoints,
|
||||||
@ -25,6 +25,8 @@ import {
|
|||||||
templateTypeState,
|
templateTypeState,
|
||||||
wallState,
|
wallState,
|
||||||
guideLineState,
|
guideLineState,
|
||||||
|
horiGuideLinesState,
|
||||||
|
vertGuideLinesState,
|
||||||
} from '@/store/canvasAtom'
|
} from '@/store/canvasAtom'
|
||||||
import { QLine } from '@/components/fabric/QLine'
|
import { QLine } from '@/components/fabric/QLine'
|
||||||
import { fabric } from 'fabric'
|
import { fabric } from 'fabric'
|
||||||
@ -64,23 +66,22 @@ export function useMode() {
|
|||||||
const compass = useRecoilValue(compassState)
|
const compass = useRecoilValue(compassState)
|
||||||
const [isCellCenter, setIsCellCenter] = useState(false)
|
const [isCellCenter, setIsCellCenter] = useState(false)
|
||||||
|
|
||||||
const guideLineInfo = useRecoilValue(guideLineState)
|
const [guideLineInfo, setGuideLineInfo] = useRecoilState(guideLineState)
|
||||||
|
|
||||||
const [guideLineMode, setGuideLineMode] = useState(false)
|
const [guideLineMode, setGuideLineMode] = useState(false)
|
||||||
const [guideDotMode, setGuideDotMode] = useState(false)
|
const [guideDotMode, setGuideDotMode] = useState(false)
|
||||||
|
|
||||||
|
const [horiGuideLines, setHoriGuideLines] = useRecoilState(horiGuideLinesState)
|
||||||
|
const [vertGuideLines, setVertGuideLines] = useRecoilState(vertGuideLinesState)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 이벤트 리스너 추가
|
|
||||||
// if (!canvas) {
|
// if (!canvas) {
|
||||||
// canvas?.setZoom(0.8)
|
// canvas?.setZoom(0.8)
|
||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
document.addEventListener('keydown', handleKeyDown)
|
if (!canvas) return
|
||||||
|
setCanvas(canvas)
|
||||||
canvas?.on('mouse:move', drawMouseLines)
|
canvas?.on('mouse:move', drawMouseLines)
|
||||||
// 컴포넌트가 언마운트될 때 이벤트 리스너 제거
|
|
||||||
return () => {
|
|
||||||
document.removeEventListener('keydown', handleKeyDown)
|
|
||||||
}
|
|
||||||
}, [canvas]) // 빈 배열을 전달하여 컴포넌트가 마운트될 때만 실행되도록 함
|
}, [canvas]) // 빈 배열을 전달하여 컴포넌트가 마운트될 때만 실행되도록 함
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -100,15 +101,12 @@ export function useMode() {
|
|||||||
}, [endPoint])
|
}, [endPoint])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
canvas?.off('mouse:out', removeMouseLines)
|
||||||
|
canvas?.on('mouse:out', removeMouseLines)
|
||||||
|
changeMode(canvas, mode)
|
||||||
canvas?.off('mouse:move')
|
canvas?.off('mouse:move')
|
||||||
canvas?.on('mouse:move', drawMouseLines)
|
canvas?.on('mouse:move', drawMouseLines)
|
||||||
changeMode(canvas, mode)
|
}, [mode, horiGuideLines, vertGuideLines])
|
||||||
/*
|
|
||||||
if (mode === Mode.EDIT) {
|
|
||||||
canvas?.off('mouse:down')
|
|
||||||
canvas?.on('mouse:down', mouseEvent.editMode)
|
|
||||||
}*/
|
|
||||||
}, [mode])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setGuideLineMode(false)
|
setGuideLineMode(false)
|
||||||
@ -142,8 +140,8 @@ export function useMode() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isGuideLineMode) {
|
if (isGuideLineMode) {
|
||||||
horizontalLineArray = [...guideLineState[0].horizontalLineArray]
|
horizontalLineArray = [...horiGuideLines]
|
||||||
verticalLineArray = [...guideLineState[0].verticalLineArray]
|
verticalLineArray = [...vertGuideLines]
|
||||||
guideLineLengthHori = Number(guideLineState[0].moduleHoriLength)
|
guideLineLengthHori = Number(guideLineState[0].moduleHoriLength)
|
||||||
guideLineLengthVert = Number(guideLineState[0].moduleVertLength)
|
guideLineLengthVert = Number(guideLineState[0].moduleVertLength)
|
||||||
}
|
}
|
||||||
@ -163,12 +161,27 @@ export function useMode() {
|
|||||||
|
|
||||||
if (mode === Mode.EDIT || mode === Mode.ADSORPTION_POINT) {
|
if (mode === Mode.EDIT || mode === Mode.ADSORPTION_POINT) {
|
||||||
let adsorptionPoint = adsorptionPointList.length > 0 ? findClosestPoint(pointer, adsorptionPointList) : null
|
let adsorptionPoint = adsorptionPointList.length > 0 ? findClosestPoint(pointer, adsorptionPointList) : null
|
||||||
|
if ((horiGuideLines.length > 0 || vertGuideLines.length > 0) && guideDotMode) {
|
||||||
|
const closestHorizontalLine = getClosestHorizontalLine(pointer, horiGuideLines)
|
||||||
|
const closetVerticalLine = getClosestVerticalLine(pointer, vertGuideLines)
|
||||||
|
let intersection = null
|
||||||
|
let intersectionDistance = Infinity
|
||||||
|
|
||||||
if (isGuideLineMode && isGuideDotMode) {
|
if (closestHorizontalLine && closetVerticalLine) {
|
||||||
const closestHorizontalLine = getClosestHorizontalLine(pointer, horizontalLineArray)
|
intersection = calculateIntersection(closestHorizontalLine, closetVerticalLine)
|
||||||
const closetVerticalLine = getClosestVerticalLine(pointer, verticalLineArray)
|
if (intersection) {
|
||||||
const xDiff = Math.abs(pointer.x - closetVerticalLine.x1)
|
intersectionDistance = distanceBetweenPoints(pointer, intersection)
|
||||||
const yDiff = Math.abs(pointer.y - closestHorizontalLine.y1)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let xDiff, yDiff
|
||||||
|
|
||||||
|
if (closetVerticalLine) {
|
||||||
|
xDiff = Math.abs(pointer.x - closetVerticalLine.x1)
|
||||||
|
}
|
||||||
|
if (closestHorizontalLine) {
|
||||||
|
yDiff = Math.abs(pointer.y - closestHorizontalLine.y1)
|
||||||
|
}
|
||||||
|
|
||||||
const x = pointer.x - guideLineLengthHori * Math.floor(pointer.x / guideLineLengthHori)
|
const x = pointer.x - guideLineLengthHori * Math.floor(pointer.x / guideLineLengthHori)
|
||||||
const y = pointer.y - guideLineLengthVert * Math.floor(pointer.y / guideLineLengthVert)
|
const y = pointer.y - guideLineLengthVert * Math.floor(pointer.y / guideLineLengthVert)
|
||||||
@ -177,51 +190,13 @@ export function useMode() {
|
|||||||
const yRate = y / guideLineLengthVert
|
const yRate = y / guideLineLengthVert
|
||||||
const isAttachX = xRate >= 0.4 && xRate <= 0.7
|
const isAttachX = xRate >= 0.4 && xRate <= 0.7
|
||||||
const isAttachY = yRate >= 0.4 && yRate <= 0.7
|
const isAttachY = yRate >= 0.4 && yRate <= 0.7
|
||||||
|
|
||||||
if (isAttachX && isAttachY) {
|
if (isAttachX && isAttachY) {
|
||||||
newX = Math.floor(pointer.x / guideLineLengthHori) * guideLineLengthHori + guideLineLengthHori / 2
|
newX = Math.floor(pointer.x / guideLineLengthHori) * guideLineLengthHori + guideLineLengthHori / 2
|
||||||
newY = Math.floor(pointer.y / guideLineLengthVert) * guideLineLengthVert + guideLineLengthVert / 2
|
newY = Math.floor(pointer.y / guideLineLengthVert) * guideLineLengthVert + guideLineLengthVert / 2
|
||||||
} else {
|
} else {
|
||||||
if (Math.min(xDiff, yDiff) <= 20) {
|
if (intersection && intersectionDistance < 20) {
|
||||||
if (xDiff < yDiff) {
|
newX = intersection.x
|
||||||
newX = closetVerticalLine.x1
|
newY = intersection.y
|
||||||
newY = pointer.y
|
|
||||||
} else {
|
|
||||||
newX = pointer.x
|
|
||||||
newY = closestHorizontalLine.y1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (isGuideDotMode) {
|
|
||||||
const x = pointer.x - guideLineLengthHori * Math.floor(pointer.x / guideLineLengthHori)
|
|
||||||
const y = pointer.y - guideLineLengthVert * Math.floor(pointer.y / guideLineLengthVert)
|
|
||||||
|
|
||||||
const xRate = x / guideLineLengthHori
|
|
||||||
const yRate = y / guideLineLengthVert
|
|
||||||
const isAttachX = xRate >= 0.4 && xRate <= 0.7
|
|
||||||
const isAttachY = yRate >= 0.4 && yRate <= 0.7
|
|
||||||
|
|
||||||
if (isAttachX && isAttachY) {
|
|
||||||
newX = Math.floor(pointer.x / guideLineLengthHori) * guideLineLengthHori + guideLineLengthHori / 2
|
|
||||||
newY = Math.floor(pointer.y / guideLineLengthVert) * guideLineLengthVert + guideLineLengthVert / 2
|
|
||||||
}
|
|
||||||
} else if (isGuideLineMode) {
|
|
||||||
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 - guideLineLengthHori * Math.floor(pointer.x / guideLineLengthHori)
|
|
||||||
const y = pointer.y - guideLineLengthVert * Math.floor(pointer.y / guideLineLengthVert)
|
|
||||||
|
|
||||||
const xRate = x / guideLineLengthHori
|
|
||||||
const yRate = y / guideLineLengthVert
|
|
||||||
const isAttachX = xRate >= 0.4 && xRate <= 0.7
|
|
||||||
const isAttachY = yRate >= 0.4 && yRate <= 0.7
|
|
||||||
|
|
||||||
if (isAttachX && isAttachY) {
|
|
||||||
newX = Math.floor(pointer.x / guideLineLengthHori) * guideLineLengthHori + guideLineLengthHori / 2
|
|
||||||
newY = Math.floor(pointer.y / guideLineLengthVert) * guideLineLengthVert + guideLineLengthVert / 2
|
|
||||||
} else {
|
} else {
|
||||||
if (Math.min(xDiff, yDiff) <= 20) {
|
if (Math.min(xDiff, yDiff) <= 20) {
|
||||||
if (xDiff < yDiff) {
|
if (xDiff < yDiff) {
|
||||||
@ -234,7 +209,68 @@ export function useMode() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (guideDotMode) {
|
||||||
|
const x = pointer.x - guideLineLengthHori * Math.floor(pointer.x / guideLineLengthHori)
|
||||||
|
const y = pointer.y - guideLineLengthVert * Math.floor(pointer.y / guideLineLengthVert)
|
||||||
|
|
||||||
|
const xRate = x / guideLineLengthHori
|
||||||
|
const yRate = y / guideLineLengthVert
|
||||||
|
const isAttachX = xRate >= 0.4 && xRate <= 0.7
|
||||||
|
const isAttachY = yRate >= 0.4 && yRate <= 0.7
|
||||||
|
|
||||||
|
if (isAttachX && isAttachY) {
|
||||||
|
newX = Math.floor(pointer.x / guideLineLengthHori) * guideLineLengthHori + guideLineLengthHori / 2
|
||||||
|
newY = Math.floor(pointer.y / guideLineLengthVert) * guideLineLengthVert + guideLineLengthVert / 2
|
||||||
|
}
|
||||||
|
} else if (horiGuideLines.length > 0 || vertGuideLines.length > 0) {
|
||||||
|
const closestHorizontalLine = getClosestHorizontalLine(pointer, horiGuideLines)
|
||||||
|
const closetVerticalLine = getClosestVerticalLine(pointer, vertGuideLines)
|
||||||
|
let intersection = null
|
||||||
|
let intersectionDistance = Infinity
|
||||||
|
|
||||||
|
if (closestHorizontalLine && closetVerticalLine) {
|
||||||
|
intersection = calculateIntersection(closestHorizontalLine, closetVerticalLine)
|
||||||
|
if (intersection) {
|
||||||
|
intersectionDistance = distanceBetweenPoints(pointer, intersection)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let xDiff, yDiff
|
||||||
|
|
||||||
|
if (closetVerticalLine) {
|
||||||
|
xDiff = Math.abs(pointer.x - closetVerticalLine.x1)
|
||||||
|
}
|
||||||
|
if (closestHorizontalLine) {
|
||||||
|
yDiff = Math.abs(pointer.y - closestHorizontalLine.y1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const x = pointer.x - guideLineLengthHori * Math.floor(pointer.x / guideLineLengthHori)
|
||||||
|
const y = pointer.y - guideLineLengthVert * Math.floor(pointer.y / guideLineLengthVert)
|
||||||
|
|
||||||
|
const xRate = x / guideLineLengthHori
|
||||||
|
const yRate = y / guideLineLengthVert
|
||||||
|
const isAttachX = xRate >= 0.4 && xRate <= 0.7
|
||||||
|
const isAttachY = yRate >= 0.4 && yRate <= 0.7
|
||||||
|
if (isAttachX && isAttachY) {
|
||||||
|
newX = Math.floor(pointer.x / guideLineLengthHori) * guideLineLengthHori + guideLineLengthHori / 2
|
||||||
|
newY = Math.floor(pointer.y / guideLineLengthVert) * guideLineLengthVert + guideLineLengthVert / 2
|
||||||
|
} else {
|
||||||
|
if (intersection && intersectionDistance < 20) {
|
||||||
|
newX = intersection.x
|
||||||
|
newY = intersection.y
|
||||||
|
} else {
|
||||||
|
if (Math.min(xDiff, yDiff) <= 20) {
|
||||||
|
if (xDiff < yDiff) {
|
||||||
|
newX = closetVerticalLine.x1
|
||||||
|
newY = pointer.y
|
||||||
|
} else {
|
||||||
|
newX = pointer.x
|
||||||
|
newY = closestHorizontalLine.y1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (adsorptionPoint && distanceBetweenPoints(pointer, adsorptionPoint) < 20) {
|
if (adsorptionPoint && distanceBetweenPoints(pointer, adsorptionPoint) < 20) {
|
||||||
newX = adsorptionPoint.left
|
newX = adsorptionPoint.left
|
||||||
newY = adsorptionPoint.top
|
newY = adsorptionPoint.top
|
||||||
@ -362,16 +398,13 @@ export function useMode() {
|
|||||||
|
|
||||||
// 모드에 따른 마우스 이벤트 변경
|
// 모드에 따른 마우스 이벤트 변경
|
||||||
const changeMouseEvent = (mode) => {
|
const changeMouseEvent = (mode) => {
|
||||||
canvas?.off('mouse:down')
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case 'drawLine':
|
case 'drawLine':
|
||||||
canvas?.on('mouse:down', mouseEvent.drawLineModeLeftClick)
|
canvas?.on('mouse:down', mouseEvent.drawLineModeLeftClick)
|
||||||
window.document.removeEventListener('contextmenu', mouseEvent.drawLineModeRightClick)
|
document.addEventListener('contextmenu', mouseEvent.drawLineModeRightClick)
|
||||||
window.document.addEventListener('contextmenu', mouseEvent.drawLineModeRightClick)
|
|
||||||
break
|
break
|
||||||
case 'edit':
|
case 'edit':
|
||||||
canvas?.on('mouse:down', mouseEvent.editMode)
|
canvas?.on('mouse:down', mouseEvent.editMode)
|
||||||
|
|
||||||
break
|
break
|
||||||
case 'textbox':
|
case 'textbox':
|
||||||
canvas?.on('mouse:down', mouseEvent.textboxMode)
|
canvas?.on('mouse:down', mouseEvent.textboxMode)
|
||||||
@ -394,9 +427,6 @@ export function useMode() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 모드에 따른 키보드 이벤트 변경
|
|
||||||
const changeKeyboardEvent = (mode) => {}
|
|
||||||
|
|
||||||
const keyValid = () => {
|
const keyValid = () => {
|
||||||
if (points.current.length === 0) {
|
if (points.current.length === 0) {
|
||||||
alert('시작점을 선택해주세요')
|
alert('시작점을 선택해주세요')
|
||||||
@ -552,7 +582,23 @@ export function useMode() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleKeyDown = (e) => {
|
const mouseAndkeyboardEventClear = () => {
|
||||||
|
canvas?.off('mouse:down')
|
||||||
|
Object.keys(mouseEvent).forEach((key) => {
|
||||||
|
canvas?.off('mouse:down', mouseEvent[key])
|
||||||
|
document.removeEventListener('contextmenu', mouseEvent[key])
|
||||||
|
})
|
||||||
|
|
||||||
|
Object.keys(keyboardEvent).forEach((key) => {
|
||||||
|
document.removeEventListener('keydown', keyboardEvent[key])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const keyboardEvent = {
|
||||||
|
// rerendering을 막기 위해 useCallback 사용
|
||||||
|
editMode: useCallback(
|
||||||
|
(e) => {
|
||||||
|
e.preventDefault()
|
||||||
switch (e.key) {
|
switch (e.key) {
|
||||||
case 'ArrowDown': {
|
case 'ArrowDown': {
|
||||||
if (!keyValid()) {
|
if (!keyValid()) {
|
||||||
@ -616,13 +662,18 @@ export function useMode() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
[canvas],
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
const changeMode = (canvas, mode) => {
|
const changeMode = (canvas, mode) => {
|
||||||
|
mouseAndkeyboardEventClear()
|
||||||
setMode(mode)
|
setMode(mode)
|
||||||
setCanvas(canvas)
|
setCanvas(canvas)
|
||||||
|
|
||||||
// mode별 이벤트 변경
|
// mode별 이벤트 변경
|
||||||
|
|
||||||
changeMouseEvent(mode)
|
changeMouseEvent(mode)
|
||||||
changeKeyboardEvent(mode)
|
changeKeyboardEvent(mode)
|
||||||
|
|
||||||
@ -657,40 +708,82 @@ export function useMode() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const changeKeyboardEvent = (mode) => {
|
||||||
|
if (mode === Mode.EDIT) {
|
||||||
|
switch (mode) {
|
||||||
|
case 'edit':
|
||||||
|
document.addEventListener('keydown', keyboardEvent.editMode)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const mouseEvent = {
|
const mouseEvent = {
|
||||||
drawLineModeLeftClick: (options) => {
|
drawLineModeLeftClick: (options) => {
|
||||||
|
if (mode !== Mode.DRAW_LINE) {
|
||||||
|
return
|
||||||
|
}
|
||||||
const pointer = canvas?.getPointer(options.e)
|
const pointer = canvas?.getPointer(options.e)
|
||||||
|
|
||||||
const line = new QLine(
|
const line = new QLine(
|
||||||
[pointer.x, 0, pointer.x, canvas.height], // y축에 1자 선을 그립니다.
|
[pointer.x, 0, pointer.x, canvasSize.vertical], // y축에 1자 선을 그립니다.
|
||||||
{
|
{
|
||||||
stroke: 'black',
|
stroke: 'gray',
|
||||||
strokeWidth: 2,
|
strokeWidth: 1,
|
||||||
viewLengthText: true,
|
selectable: true,
|
||||||
selectable: false,
|
lockMovementX: true,
|
||||||
fontSize: fontSize,
|
lockMovementY: true,
|
||||||
|
lockRotation: true,
|
||||||
|
lockScalingX: true,
|
||||||
|
lockScalingY: true,
|
||||||
|
name: 'guideLine',
|
||||||
|
direction: 'vertical',
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
canvas?.add(line)
|
canvas?.add(line)
|
||||||
canvas?.renderAll()
|
canvas?.renderAll()
|
||||||
|
|
||||||
|
const newVerticalLineArray = [...vertGuideLines]
|
||||||
|
newVerticalLineArray.push(line)
|
||||||
|
|
||||||
|
setVertGuideLines(newVerticalLineArray)
|
||||||
},
|
},
|
||||||
drawLineModeRightClick: (options) => {
|
drawLineModeRightClick: useCallback(
|
||||||
|
(options) => {
|
||||||
|
document.removeEventListener('contextmenu', mouseEvent.drawLineModeRightClick)
|
||||||
|
if (mode !== Mode.DRAW_LINE) {
|
||||||
|
return
|
||||||
|
}
|
||||||
const line = new fabric.Line(
|
const line = new fabric.Line(
|
||||||
[0, options.offsetY, canvas.width, options.offsetY], // y축에 1자 선을 그립니다.
|
[0, options.offsetY, canvasSize.horizontal, options.offsetY], // y축에 1자 선을 그립니다.
|
||||||
{
|
{
|
||||||
stroke: 'black',
|
stroke: 'gray',
|
||||||
strokeWidth: 2,
|
strokeWidth: 1,
|
||||||
viewLengthText: true,
|
selectable: true,
|
||||||
selectable: false,
|
lockMovementX: true,
|
||||||
fontSize: fontSize,
|
lockMovementY: true,
|
||||||
|
lockRotation: true,
|
||||||
|
lockScalingX: true,
|
||||||
|
lockScalingY: true,
|
||||||
|
name: 'guideLine',
|
||||||
|
direction: 'horizontal',
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
canvas?.add(line)
|
canvas?.add(line)
|
||||||
canvas?.renderAll()
|
canvas?.renderAll()
|
||||||
|
|
||||||
|
const newHorizontalLineArray = [...horiGuideLines]
|
||||||
|
newHorizontalLineArray.push(line)
|
||||||
|
setHoriGuideLines(newHorizontalLineArray)
|
||||||
},
|
},
|
||||||
|
[canvas, mode, horiGuideLines],
|
||||||
|
),
|
||||||
editMode: (options) => {
|
editMode: (options) => {
|
||||||
|
if (mode !== Mode.EDIT) {
|
||||||
|
return
|
||||||
|
}
|
||||||
let pointer = canvas?.getPointer(options.e)
|
let pointer = canvas?.getPointer(options.e)
|
||||||
|
|
||||||
if (getInterSectPointByMouseLine()) {
|
if (getInterSectPointByMouseLine()) {
|
||||||
@ -801,7 +894,9 @@ export function useMode() {
|
|||||||
|
|
||||||
canvas?.renderAll()
|
canvas?.renderAll()
|
||||||
},
|
},
|
||||||
|
|
||||||
textboxMode: (options) => {
|
textboxMode: (options) => {
|
||||||
|
if (mode !== Mode.TEXTBOX) return
|
||||||
if (canvas?.getActiveObject()?.type === 'textbox') return
|
if (canvas?.getActiveObject()?.type === 'textbox') return
|
||||||
const pointer = canvas?.getPointer(options.e)
|
const pointer = canvas?.getPointer(options.e)
|
||||||
|
|
||||||
@ -821,6 +916,7 @@ export function useMode() {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
drawRectMode: (o) => {
|
drawRectMode: (o) => {
|
||||||
|
if (mode !== Mode.DRAW_RECT) return
|
||||||
let rect, isDown, origX, origY
|
let rect, isDown, origX, origY
|
||||||
isDown = true
|
isDown = true
|
||||||
const pointer = canvas.getPointer(o.e)
|
const pointer = canvas.getPointer(o.e)
|
||||||
@ -862,7 +958,8 @@ export function useMode() {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
// 흡착점 추가
|
// 흡착점 추가
|
||||||
adsorptionPoint(o) {
|
adsorptionPoint: (o) => {
|
||||||
|
if (mode !== Mode.ADSORPTION_POINT) return
|
||||||
const pointer = canvas.getPointer(o.e)
|
const pointer = canvas.getPointer(o.e)
|
||||||
let newX = pointer.x
|
let newX = pointer.x
|
||||||
let newY = pointer.y
|
let newY = pointer.y
|
||||||
@ -4484,7 +4581,6 @@ export function useMode() {
|
|||||||
|
|
||||||
canvas?.off('mouse:move')
|
canvas?.off('mouse:move')
|
||||||
canvas?.off('mouse:out')
|
canvas?.off('mouse:out')
|
||||||
document.removeEventListener('keydown', handleKeyDown)
|
|
||||||
const roofs = canvas?.getObjects().filter((obj) => obj.name === 'roof')
|
const roofs = canvas?.getObjects().filter((obj) => obj.name === 'roof')
|
||||||
roofs.forEach((roof, index) => {
|
roofs.forEach((roof, index) => {
|
||||||
const offsetPolygonPoint = offsetPolygon(roof.points, -20)
|
const offsetPolygonPoint = offsetPolygon(roof.points, -20)
|
||||||
|
|||||||
@ -95,3 +95,15 @@ export const currentObjectState = atom({
|
|||||||
default: null,
|
default: null,
|
||||||
dangerouslyAllowMutability: true,
|
dangerouslyAllowMutability: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const horiGuideLinesState = atom({
|
||||||
|
key: 'horiGuideLines',
|
||||||
|
default: [],
|
||||||
|
dangerouslyAllowMutability: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const vertGuideLinesState = atom({
|
||||||
|
key: 'vertGuideLines',
|
||||||
|
default: [],
|
||||||
|
dangerouslyAllowMutability: true,
|
||||||
|
})
|
||||||
|
|||||||
@ -1,3 +1,81 @@
|
|||||||
.test {
|
.test {
|
||||||
background-color: #121212;
|
background-color: #121212;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.grid-container2 {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr); /* 2개의 열 */
|
||||||
|
grid-template-rows: repeat(6, 30px); /* 6개의 행 */
|
||||||
|
justify-items: center; /* 각 그리드 아이템을 수평 가운데 정렬 */
|
||||||
|
align-items: center; /* 각 그리드 아이템을 수직 가운데 정렬 */
|
||||||
|
gap: 5px; /* 그리드 아이템 사이의 간격 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-container3 {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr); /* 3개의 열 */
|
||||||
|
grid-template-rows: repeat(6, 30px); /* 6개의 행 */
|
||||||
|
justify-items: center; /* 각 그리드 아이템을 수평 가운데 정렬 */
|
||||||
|
align-items: center; /* 각 그리드 아이템을 수직 가운데 정렬 */
|
||||||
|
gap: 5px; /* 그리드 아이템 사이의 간격 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-container4 {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, 1fr); /* 4개의 열 */
|
||||||
|
grid-template-rows: repeat(6, 30px); /* 6개의 행 */
|
||||||
|
justify-items: center; /* 각 그리드 아이템을 수평 가운데 정렬 */
|
||||||
|
align-items: center; /* 각 그리드 아이템을 수직 가운데 정렬 */
|
||||||
|
gap: 5px; /* 그리드 아이템 사이의 간격 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-container5 {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(5, 1fr); /* 5개의 열 */
|
||||||
|
grid-template-rows: repeat(5, 30px); /* 5개의 행 */
|
||||||
|
justify-items: center; /* 각 그리드 아이템을 수평 가운데 정렬 */
|
||||||
|
align-items: center; /* 각 그리드 아이템을 수직 가운데 정렬 */
|
||||||
|
gap: 0px; /* 그리드 아이템 사이의 간격 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-item {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border: 1px solid black; /* 그리드 외각선 */
|
||||||
|
text-align: center; /* 그리드 내 가운데 정렬 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-item2 {
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 1px solid #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-item3 {
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 1px solid #000;
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-item.Y {
|
||||||
|
background-color: #d3d0d0;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-item.N {
|
||||||
|
background-color: white;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-item.selected {
|
||||||
|
background-color: #d3d0d0;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-item.unselected {
|
||||||
|
background-color: white;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|||||||
@ -9,3 +9,45 @@ export const isObjectNotEmpty = (obj) => {
|
|||||||
}
|
}
|
||||||
return Object.keys(obj).length > 0
|
return Object.keys(obj).length > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ex) const params = {page:10, searchDvsnCd: 20}
|
||||||
|
* @param {*} params
|
||||||
|
* @returns page=10&searchDvsnCd=20
|
||||||
|
*/
|
||||||
|
export const queryStringFormatter = (params = {}) => {
|
||||||
|
const queries = []
|
||||||
|
Object.keys(params).forEach((parameterKey) => {
|
||||||
|
const parameterValue = params[parameterKey]
|
||||||
|
|
||||||
|
if (parameterValue === undefined || parameterValue === null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// string trim
|
||||||
|
if (typeof parameterValue === 'string' && !parameterValue.trim()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// array to query string
|
||||||
|
if (Array.isArray(parameterValue)) {
|
||||||
|
// primitive type
|
||||||
|
if (parameterValue.every((v) => typeof v === 'number' || typeof v === 'string')) {
|
||||||
|
queries.push(`${encodeURIComponent(parameterKey)}=${parameterValue.map((v) => encodeURIComponent(v)).join(',')}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// reference type
|
||||||
|
if (parameterValue.every((v) => typeof v === 'object' && v !== null)) {
|
||||||
|
parameterValue.map((pv, i) => {
|
||||||
|
return Object.keys(pv).forEach((valueKey) => {
|
||||||
|
queries.push(`${encodeURIComponent(`${parameterKey}[${i}].${valueKey}`)}=${encodeURIComponent(pv[valueKey])}`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 나머지
|
||||||
|
queries.push(`${encodeURIComponent(parameterKey)}=${encodeURIComponent(parameterValue)}`)
|
||||||
|
})
|
||||||
|
return queries.join('&')
|
||||||
|
}
|
||||||
|
|||||||
@ -4319,6 +4319,11 @@ date-fns@^3.3.1:
|
|||||||
resolved "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz"
|
resolved "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz"
|
||||||
integrity sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==
|
integrity sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==
|
||||||
|
|
||||||
|
dayjs@^1.11.13:
|
||||||
|
version "1.11.13"
|
||||||
|
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.13.tgz#92430b0139055c3ebb60150aa13e860a4b5a366c"
|
||||||
|
integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==
|
||||||
|
|
||||||
debug@4, debug@^4.3.3, debug@^4.3.4:
|
debug@4, debug@^4.3.3, debug@^4.3.4:
|
||||||
version "4.3.5"
|
version "4.3.5"
|
||||||
resolved "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz"
|
resolved "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user