외벽선 편집 및 오프셋 추가

This commit is contained in:
hyojun.choi 2024-10-08 15:21:34 +09:00
parent d460b3f0ab
commit dd3797993b
6 changed files with 269 additions and 34 deletions

View File

@ -5,16 +5,35 @@ import Eaves from '@/components/floor-plan/modal/eavesGable/type/Eaves'
import Gable from '@/components/floor-plan/modal/eavesGable/type/Gable'
import WallMerge from '@/components/floor-plan/modal/eavesGable/type/WallMerge'
import Shed from '@/components/floor-plan/modal/eavesGable/type/Shed'
import { useEavesGableEdit } from '@/hooks/roofcover/useEavesGableEdit'
export default function EavesGableEdit({ setShowEavesGableEditModal }) {
const { getMessage } = useMessage()
const [buttonAct, setButtonAct] = useState(1)
const buttonMenu = [
{ id: 1, name: getMessage('eaves') },
{ id: 2, name: getMessage('gable') },
{ id: 3, name: getMessage('wall.merge') },
{ id: 4, name: getMessage('shed') },
]
const { type, setType, buttonMenu, TYPES, pitchRef, offsetRef, widthRef, radioTypeRef } = useEavesGableEdit()
const eavesProps = {
pitchRef,
offsetRef,
widthRef,
radioTypeRef,
}
const gableProps = {
pitchRef,
offsetRef,
widthRef,
radioTypeRef,
}
const wallMergeProps = {
offsetRef,
radioTypeRef,
}
const shedProps = {
offsetRef,
}
return (
<WithDraggable isShow={true} pos={{ x: 50, y: -950 }}>
<div className={`modal-pop-wrap r`}>
@ -27,17 +46,17 @@ export default function EavesGableEdit({ setShowEavesGableEditModal }) {
<div className="modal-body">
<div className="modal-btn-wrap">
{buttonMenu.map((item) => (
<button key={item.id} className={`btn-frame modal ${buttonAct === item.id ? 'act' : ''}`} onClick={() => setButtonAct(item.id)}>
<button key={item.id} className={`btn-frame modal ${type === item.type ? 'act' : ''}`} onClick={() => setType(item.type)}>
{item.name}
</button>
))}
</div>
<div className="properties-setting-wrap outer">
<div className="setting-tit">{getMessage('setting')}</div>
{buttonAct === 1 && <Eaves />}
{buttonAct === 2 && <Gable />}
{buttonAct === 3 && <WallMerge />}
{buttonAct === 4 && <Shed />}
{type === TYPES.EAVES && <Eaves {...eavesProps} />}
{type === TYPES.GABLE && <Gable {...gableProps} />}
{type === TYPES.WALL_MERGE && <WallMerge {...wallMergeProps} />}
{type === TYPES.SHED && <Shed {...shedProps} />}
</div>
</div>
</div>

View File

@ -2,12 +2,12 @@ import { useMessage } from '@/hooks/useMessage'
import Image from 'next/image'
import { useState } from 'react'
export default function Eaves() {
export default function Eaves({ pitchRef, offsetRef, widthRef, radioTypeRef }) {
const { getMessage } = useMessage()
const [type, setType] = useState()
const [type, setType] = useState('1')
const onChange = (e) => {
console.log(e)
setType(e.target.value)
radioTypeRef.current = e.target.value
}
return (
<>
@ -17,7 +17,7 @@ export default function Eaves() {
{getMessage('slope')}
</span>
<div className="input-grid mr5" style={{ width: '100px' }}>
<input type="text" className="input-origin block" defaultValue={100} />
<input type="number" className="input-origin block" defaultValue={4} ref={pitchRef} />
</div>
<span className="thin"></span>
</div>
@ -26,7 +26,7 @@ export default function Eaves() {
{getMessage('offset')}
</span>
<div className="input-grid mr5" style={{ width: '100px' }}>
<input type="text" className="input-origin block" defaultValue={100} />
<input type="number" className="input-origin block" defaultValue={500} ref={offsetRef} />
</div>
<span className="thin">mm</span>
</div>
@ -63,7 +63,7 @@ export default function Eaves() {
<div className="eaves-keraba-th">
<div className="outline-form">
<div className="input-grid mr5" style={{ width: '100px' }}>
<input type="text" className="input-origin block" defaultValue={100} readOnly={type === '1'} />
<input type="number" min={0} className="input-origin block" defaultValue={500} ref={widthRef} readOnly={type === '1'} />
</div>
<span className="thin">mm</span>
</div>

View File

@ -1,8 +1,14 @@
import { useMessage } from '@/hooks/useMessage'
import Image from 'next/image'
import { useState } from 'react'
export default function Gable() {
export default function Gable({ pitchRef, offsetRef, widthRef, radioTypeRef }) {
const { getMessage } = useMessage()
const [type, setType] = useState('1')
const onChange = (e) => {
setType(e.target.value)
radioTypeRef.current = e.target.value
}
return (
<>
<div className="outline-wrap">
@ -11,7 +17,7 @@ export default function Gable() {
{getMessage('offset')}
</span>
<div className="input-grid mr5" style={{ width: '100px' }}>
<input type="text" className="input-origin block" defaultValue={100} />
<input type="text" className="input-origin block" defaultValue={300} ref={offsetRef} />
</div>
<span className="thin">mm</span>
</div>
@ -21,12 +27,12 @@ export default function Gable() {
<div className="eaves-keraba-item">
<div className="eaves-keraba-th">
<div className="d-check-radio pop">
<input type="radio" name="radio01" id="ra01" />
<input type="radio" name="radio01" id="ra01" value="1" checked={type === '1'} onChange={(e) => onChange(e)} />
<label htmlFor="ra01">{getMessage('modal.eaves.gable.edit.basic')}</label>
</div>
</div>
<div className="eaves-keraba-td">
<div className="eaves-keraba-ico act">
<div className={`eaves-keraba-ico ${type === '1' ? 'act' : ''}`}>
<Image src="/static/images/canvas/eaves_icon04.svg" alt="react" width={30} height={30} />
</div>
</div>
@ -34,12 +40,12 @@ export default function Gable() {
<div className="eaves-keraba-item">
<div className="eaves-keraba-th">
<div className="d-check-radio pop">
<input type="radio" name="radio01" id="ra02" />
<input type="radio" name="radio01" id="ra02" value="2" checked={type === '2'} onChange={(e) => onChange(e)} />
<label htmlFor="ra02">{getMessage('jerkinhead')}</label>
</div>
</div>
<div className="eaves-keraba-td">
<div className="eaves-keraba-ico">
<div className={`eaves-keraba-ico ${type === '2' ? 'act' : ''}`}>
<Image src="/static/images/canvas/eaves_icon09.svg" alt="react" width={30} height={30} />
</div>
</div>
@ -51,7 +57,7 @@ export default function Gable() {
{getMessage('slope')}
</span>
<div className="input-grid mr5" style={{ width: '100px' }}>
<input type="text" className="input-origin block" defaultValue={100} />
<input type="text" className="input-origin block" defaultValue={4.5} ref={pitchRef} readOnly={type === '1'} />
</div>
<span className="thin"></span>
</div>
@ -69,7 +75,7 @@ export default function Gable() {
{getMessage('offset')}
</span>
<div className="input-grid mr5" style={{ width: '100px' }}>
<input type="text" className="input-origin block" defaultValue={100} />
<input type="text" className="input-origin block" defaultValue={800} ref={widthRef} readOnly={type === '1'} />
</div>
<span className="thin">mm</span>
</div>

View File

@ -1,6 +1,6 @@
import { useMessage } from '@/hooks/useMessage'
export default function Shed() {
export default function Shed({ offsetRef }) {
const { getMessage } = useMessage()
return (
<>
@ -10,7 +10,7 @@ export default function Shed() {
{getMessage('offset')}
</span>
<div className="input-grid mr5" style={{ width: '100px' }}>
<input type="text" className="input-origin block" defaultValue={100} />
<input type="text" className="input-origin block" ref={offsetRef} defaultValue={300} />
</div>
<span className="thin">mm</span>
</div>

View File

@ -1,8 +1,14 @@
import { useMessage } from '@/hooks/useMessage'
import Image from 'next/image'
import { useState } from 'react'
export default function WallMerge() {
export default function WallMerge({ offsetRef, radioTypeRef }) {
const { getMessage } = useMessage()
const [type, setType] = useState('1')
const onChange = (e) => {
setType(e.target.value)
radioTypeRef.current = e.target.value
}
return (
<>
<div className="outline-wrap">
@ -11,12 +17,12 @@ export default function WallMerge() {
<div className="eaves-keraba-item">
<div className="eaves-keraba-th">
<div className="d-check-radio pop">
<input type="radio" name="radio01" id="ra01" />
<input type="radio" name="radio01" id="ra01" value="1" checked={type === '1'} onChange={(e) => onChange(e)} />
<label htmlFor="ra01">{getMessage('has.sleeve')}</label>
</div>
</div>
<div className="eaves-keraba-td">
<div className="eaves-keraba-ico act">
<div className={`eaves-keraba-ico ${type === '1' ? 'act' : ''}`}>
<Image src="/static/images/canvas/eaves_icon06.svg" alt="react" width={30} height={30} />
</div>
</div>
@ -24,12 +30,12 @@ export default function WallMerge() {
<div className="eaves-keraba-item">
<div className="eaves-keraba-th">
<div className="d-check-radio pop">
<input type="radio" name="radio01" id="ra02" />
<input type="radio" name="radio01" id="ra02" value="2" checked={type === '2'} onChange={(e) => onChange(e)} />
<label htmlFor="ra02">{getMessage('has.not.sleeve')}</label>
</div>
</div>
<div className="eaves-keraba-td">
<div className="eaves-keraba-ico">
<div className={`eaves-keraba-ico ${type === '2' ? 'act' : ''}`}>
<Image src="/static/images/canvas/eaves_icon07.svg" alt="react" width={30} height={30} />
</div>
</div>
@ -41,7 +47,7 @@ export default function WallMerge() {
{getMessage('offset')}
</span>
<div className="input-grid mr5" style={{ width: '100px' }}>
<input type="text" className="input-origin block" defaultValue={100} />
<input type="text" className="input-origin block" defaultValue={300} ref={offsetRef} readOnly={type === '1'} />
</div>
<span className="thin">mm</span>
</div>

View File

@ -0,0 +1,204 @@
import { useEffect, useRef, useState } from 'react'
import { useRecoilValue } from 'recoil'
import { canvasState } from '@/store/canvasAtom'
import { useMessage } from '@/hooks/useMessage'
import { useEvent } from '@/hooks/useEvent'
import { LINE_TYPE } from '@/common/common'
import { useLine } from '@/hooks/useLine'
import { useMode } from '@/hooks/useMode'
export function useEavesGableEdit() {
const canvas = useRecoilValue(canvasState)
const { getMessage } = useMessage()
const { addCanvasMouseEventListener, initEvent } = useEvent()
const TYPES = {
EAVES: 'eaves',
GABLE: 'gable',
WALL_MERGE: 'wall.merge',
SHED: 'shed',
}
const [type, setType] = useState(TYPES.EAVES)
const typeRef = useRef(TYPES.EAVES)
const { removeLine } = useLine()
const { drawRoofPolygon } = useMode()
const pitchRef = useRef(null)
const offsetRef = useRef(null)
const widthRef = useRef(null)
const radioTypeRef = useRef('1') // 각 페이지에서 사용하는 radio type
const buttonMenu = [
{ id: 1, name: getMessage('eaves'), type: TYPES.EAVES },
{ id: 2, name: getMessage('gable'), type: TYPES.GABLE },
{ id: 3, name: getMessage('wall.merge'), type: TYPES.WALL_MERGE },
{ id: 4, name: getMessage('shed'), type: TYPES.SHED },
]
useEffect(() => {
const wallLines = canvas.getObjects().filter((obj) => obj.name === 'wallLine')
wallLines.forEach((wallLine) => {
convertPolygonToLines(wallLine)
})
addCanvasMouseEventListener('mouse:over', mouseOverEvent)
addCanvasMouseEventListener('mouse:down', mouseDownEvent)
return () => {
wallLines.forEach((wallLine) => {
convertLinesToPolygon(wallLine)
})
initEvent()
}
}, [])
useEffect(() => {
typeRef.current = type
}, [type])
const mouseOverEvent = (e) => {
if (e.target && e.target.name === 'outerLine') {
e.target.set({
stroke: 'red',
})
canvas.renderAll()
} else {
canvas
?.getObjects()
.filter((obj) => obj.name === 'outerLine')
.forEach((line) => {
line.set({
stroke: 'black',
})
})
}
canvas.renderAll()
}
const mouseDownEvent = (e) => {
if (!e.target || (e.target && e.target.name !== 'outerLine')) {
return
}
const target = e.target
let attributes = target.get('attributes')
switch (typeRef.current) {
case TYPES.EAVES:
if (radioTypeRef.current === '1') {
attributes = {
type: LINE_TYPE.WALLLINE.EAVES,
pitch: pitchRef.current.value,
offset: offsetRef.current.value / 10,
}
} else {
attributes = {
type: LINE_TYPE.WALLLINE.HIPANDGABLE,
pitch: pitchRef.current.value,
offset: offsetRef.current.value / 10,
width: widthRef.current.value / 10,
}
}
break
case TYPES.GABLE:
if (radioTypeRef.current === '1') {
attributes = {
type: LINE_TYPE.WALLLINE.GABLE,
offset: offsetRef.current.value / 10,
}
} else {
attributes = {
type: LINE_TYPE.WALLLINE.JERKINHEAD,
pitch: pitchRef.current.value,
offset: offsetRef.current.value / 10,
width: widthRef.current.value / 10,
}
}
break
case TYPES.WALL_MERGE:
if (radioTypeRef.current === '1') {
attributes = {
type: LINE_TYPE.WALLLINE.WALL,
offset: 0,
}
} else {
attributes = {
type: LINE_TYPE.WALLLINE.WALL,
offset: offsetRef.current.value / 10,
}
}
break
case TYPES.SHED:
attributes = {
type: LINE_TYPE.WALLLINE.SHED,
offset: offsetRef.current.value / 10,
}
break
}
target.set({
attributes,
})
const roofBases = canvas?.getObjects().filter((obj) => obj.name === 'roofBase')
roofBases.forEach((roof) => {
roof.innerLines.forEach((line) => {
removeLine(line)
})
canvas.remove(roof)
})
const wallLines = canvas.getObjects().filter((obj) => obj.name === 'wallLine')
wallLines.forEach((wallLine) => {
const roof = drawRoofPolygon(wallLine)
canvas?.renderAll()
roof.drawHelpLine()
})
wallLines.forEach((wallLine) => {
convertPolygonToLines(wallLine)
})
addCanvasMouseEventListener('mouse:over', mouseOverEvent)
addCanvasMouseEventListener('mouse:down', mouseDownEvent)
}
// polygon의 lines를 이용해 line으로 변경하기
const convertPolygonToLines = (polygon) => {
polygon.set({ visible: false })
polygon.lines.forEach((line) => {
line.set({ visible: true })
line.set({ selectable: true })
line.set({ strokeWidth: 5 })
line.set({ parent: polygon })
line.bringToFront()
})
// canvas objects에서 polygon.lines를 제외한 다른 line의 selectable을 false로 변경
canvas
.getObjects()
.filter((obj) => obj.name !== 'outerLine' && obj.type === 'QLine')
.forEach((obj) => {
obj.set({ selectable: false })
})
canvas?.renderAll()
}
// 다시 다각형으로 변경하기
const convertLinesToPolygon = (polygon) => {
polygon.set({ visible: true })
polygon.lines.forEach((line) => {
line.set({ visible: false })
line.set({ selectable: false })
})
canvas?.renderAll()
}
return { type, setType, buttonMenu, TYPES, pitchRef, offsetRef, widthRef, radioTypeRef }
}