지붕형상 설정 추가

This commit is contained in:
hyojun.choi 2024-10-04 16:34:26 +09:00
parent 3b5201a0ce
commit 325e5632c5
22 changed files with 757 additions and 99 deletions

View File

@ -169,8 +169,9 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
attributes: {
offset: 0,
},
parent: this,
direction: getDirectionByPoint(point, nextPoint),
idx: i,
idx: i + 1,
})
line.startPoint = point
line.endPoint = nextPoint

View File

@ -1,25 +1,73 @@
import { useMessage } from '@/hooks/useMessage'
import WithDraggable from '@/components/common/draggable/WithDraggable'
import Ridge from '@/components/floor-plan/modal/roofShape/type/Ridge'
import Pattern from '@/components/floor-plan/modal/roofShape/type/Pattern'
import Side from '@/components/floor-plan/modal/roofShape/type/Side'
import { useState } from 'react'
import Image from 'next/image'
import Direction from '@/components/floor-plan/modal/roofShape/type/Direction'
import { useRoofShapeSetting } from '@/hooks/roofcover/useRoofShapeSetting'
import { useMessage } from '@/hooks/useMessage'
export default function RoofShapeSetting({ setShowRoofShapeSettingModal }) {
const { getMessage } = useMessage()
const [shapeNum, setShapeNum] = useState(1)
const shapeMenu = [
{ id: 1, name: getMessage('modal.roof.shape.setting.ridge') }, //
{ id: 2, name: getMessage('modal.roof.shape.setting.patten.a') }, // A
{ id: 3, name: getMessage('modal.roof.shape.setting.patten.b') }, // B
{ id: 4, name: getMessage('modal.roof.shape.setting.side') }, //
{ id: 5, name: getMessage('commons.west') }, //
{ id: 6, name: getMessage('commons.east') }, //
{ id: 7, name: getMessage('commons.south') }, //
{ id: 8, name: getMessage('commons.north') }, //
]
const {
shapeNum,
setShapeNum,
shapeMenu,
handleSave,
pitch,
setPitch,
eavesOffset,
setEavesOffset,
gableOffset,
setGableOffset,
sleeveOffset,
setSleeveOffset,
jerkinHeadWidth,
setJerkinHeadWidth,
jerkinHeadPitch,
setJerkinHeadPitch,
hipAndGableWidth,
setHipAndGableWidth,
shedWidth,
setShedWidth,
hasSleeve,
setHasSleeve,
buttonAct,
setButtonAct,
buttonMenu,
handleConfirm,
handleRollBack,
} = useRoofShapeSetting()
const ridgeProps = { pitch, setPitch, eavesOffset, setEavesOffset }
const patternProps = { pitch, setPitch, eavesOffset, setEavesOffset, gableOffset, setGableOffset }
const sideProps = {
pitch,
setPitch,
eavesOffset,
setEavesOffset,
gableOffset,
setGableOffset,
sleeveOffset,
setSleeveOffset,
jerkinHeadWidth,
setJerkinHeadWidth,
jerkinHeadPitch,
setJerkinHeadPitch,
hipAndGableWidth,
setHipAndGableWidth,
shedWidth,
setShedWidth,
hasSleeve,
setHasSleeve,
buttonAct,
setButtonAct,
buttonMenu,
handleConfirm,
handleRollBack,
}
const directionProps = { pitch, setPitch, eavesOffset, setEavesOffset, gableOffset, setGableOffset, shedWidth, setShedWidth }
return (
<WithDraggable isShow={true} pos={{ x: 50, y: -950 }}>
@ -43,13 +91,15 @@ export default function RoofShapeSetting({ setShowRoofShapeSettingModal }) {
</div>
<div className="properties-setting-wrap">
<div className="setting-tit">{getMessage('setting')}</div>
{shapeNum === 1 && <Ridge />}
{(shapeNum === 2 || shapeNum === 3) && <Pattern />}
{shapeNum === 4 && <Side />}
{(shapeNum === 5 || shapeNum === 6 || shapeNum === 7 || shapeNum === 8) && <Direction />}
{shapeNum === 1 && <Ridge {...ridgeProps} />}
{(shapeNum === 2 || shapeNum === 3) && <Pattern {...patternProps} />}
{shapeNum === 4 && <Side {...sideProps} />}
{(shapeNum === 5 || shapeNum === 6 || shapeNum === 7 || shapeNum === 8) && <Direction {...directionProps} />}
</div>
<div className="grid-btn-wrap">
<button className="btn-frame modal act">{getMessage('common.setting.finish')}</button>
<button className="btn-frame modal act" onClick={() => handleSave(setShowRoofShapeSettingModal)}>
{getMessage('common.setting.finish')}
</button>
</div>
</div>
</div>

View File

@ -1,6 +1,7 @@
import { useMessage } from '@/hooks/useMessage'
import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils'
export default function Direction() {
export default function Direction({ pitch, setPitch, eavesOffset, setEavesOffset, gableOffset, setGableOffset, shedWidth, setShedWidth }) {
const { getMessage } = useMessage()
return (
<div className="setting-box">
@ -9,7 +10,7 @@ export default function Direction() {
{getMessage('slope')}
</span>
<div className="input-grid mr5" style={{ width: '100px' }}>
<input type="text" className="input-origin block" defaultValue={4} />
<input type="text" className="input-origin block" value={pitch} onChange={(e) => onlyNumberWithDotInputChange(e, setPitch)} />
</div>
<span className="thin">{getMessage('size')}</span>
</div>
@ -18,7 +19,7 @@ export default function Direction() {
{getMessage('eaves.offset')}
</span>
<div className="input-grid mr5" style={{ width: '100px' }}>
<input type="text" className="input-origin block" defaultValue={500} />
<input type="text" className="input-origin block" value={eavesOffset} onChange={(e) => onlyNumberInputChange(e, setEavesOffset)} />
</div>
<span className="thin">mm</span>
</div>
@ -27,7 +28,7 @@ export default function Direction() {
{getMessage('gable.offset')}
</span>
<div className="input-grid mr5" style={{ width: '100px' }}>
<input type="text" className="input-origin block" defaultValue={300} />
<input type="text" className="input-origin block" value={gableOffset} onChange={(e) => onlyNumberInputChange(e, setGableOffset)} />
</div>
<span className="thin">mm</span>
</div>
@ -36,7 +37,7 @@ export default function Direction() {
{getMessage('windage.width')}
</span>
<div className="input-grid mr5" style={{ width: '100px' }}>
<input type="text" className="input-origin block" defaultValue={300} />
<input type="text" className="input-origin block" value={shedWidth} onChange={(e) => onlyNumberInputChange(e, setShedWidth)} />
</div>
<span className="thin">mm</span>
</div>

View File

@ -1,7 +1,9 @@
import { useMessage } from '@/hooks/useMessage'
import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils'
export default function Pattern() {
export default function Pattern(props) {
const { getMessage } = useMessage()
const { pitch, setPitch, eavesOffset, setEavesOffset, gableOffset, setGableOffset } = props
return (
<div className="setting-box">
<div className="outline-form mb10">
@ -9,7 +11,7 @@ export default function Pattern() {
{getMessage('slope')}
</span>
<div className="input-grid mr5" style={{ width: '100px' }}>
<input type="text" className="input-origin block" defaultValue={4} />
<input type="text" className="input-origin block" value={pitch} onChange={(e) => onlyNumberWithDotInputChange(e, setPitch)} />
</div>
<span className="thin"> {getMessage('size')}</span>
</div>
@ -18,7 +20,7 @@ export default function Pattern() {
{getMessage('eaves.offset')}
</span>
<div className="input-grid mr5" style={{ width: '100px' }}>
<input type="text" className="input-origin block" defaultValue={500} />
<input type="text" className="input-origin block" value={eavesOffset} onChange={(e) => onlyNumberInputChange(e, setEavesOffset)} />
</div>
<span className="thin">mm</span>
</div>
@ -27,7 +29,7 @@ export default function Pattern() {
{getMessage('gable.offset')}
</span>
<div className="input-grid mr5" style={{ width: '100px' }}>
<input type="text" className="input-origin block" defaultValue={300} />
<input type="text" className="input-origin block" value={gableOffset} onChange={setGableOffset} />
</div>
<span className="thin">mm</span>
</div>

View File

@ -1,7 +1,11 @@
import { useMessage } from '@/hooks/useMessage'
import { useEffect } from 'react'
import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils'
export default function Ridge() {
export default function Ridge(props) {
const { getMessage } = useMessage()
const { pitch, setPitch, eavesOffset, setEavesOffset } = props
return (
<div className="setting-box">
<div className="outline-form mb10">
@ -9,7 +13,7 @@ export default function Ridge() {
{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" value={pitch} onChange={(e) => onlyNumberWithDotInputChange(e, setPitch)} />
</div>
<span className="thin">{getMessage('size')}</span>
</div>
@ -18,7 +22,7 @@ export default function Ridge() {
{getMessage('eaves.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" value={eavesOffset} onChange={(e) => onlyNumberInputChange(e, setEavesOffset)} />
</div>
<span className="thin">mm</span>
</div>

View File

@ -7,39 +7,68 @@ import Wall from '@/components/floor-plan/modal/roofShape/type/option/Wall'
import Jerkinhead from '@/components/floor-plan/modal/roofShape/type/option/Jerkinhead'
import Shed from '@/components/floor-plan/modal/roofShape/type/option/Shed'
export default function Side() {
const [buttonAct, setButtonAct] = useState(1)
export default function Side(props) {
const {
pitch,
setPitch,
eavesOffset,
setEavesOffset,
gableOffset,
setGableOffset,
sleeveOffset,
setSleeveOffset,
jerkinHeadWidth,
setJerkinHeadWidth,
jerkinHeadPitch,
setJerkinHeadPitch,
hipAndGableWidth,
setHipAndGableWidth,
shedWidth,
setShedWidth,
hasSleeve,
setHasSleeve,
buttonAct,
setButtonAct,
buttonMenu,
handleConfirm,
handleRollBack,
} = props
const eavesProps = { pitch, setPitch, eavesOffset, setEavesOffset }
const gableProps = { gableOffset, setGableOffset }
const wallProps = { sleeveOffset, setSleeveOffset, hasSleeve, setHasSleeve }
const hipAndGableProps = { pitch, setPitch, eavesOffset, setEavesOffset, hipAndGableWidth, setHipAndGableWidth }
const jerkinheadProps = { gableOffset, setGableOffset, jerkinHeadWidth, setJerkinHeadWidth, jerkinHeadPitch, setJerkinHeadPitch }
const shedProps = { shedWidth, setShedWidth }
const { getMessage } = useMessage()
const buttonMenu = [
{ id: 1, name: getMessage('eaves') },
{ id: 2, name: getMessage('gable') },
{ id: 3, name: getMessage('wall') },
{ id: 4, name: getMessage('hipandgable') },
{ id: 5, name: getMessage('jerkinhead') },
{ id: 6, name: getMessage('shed') },
]
return (
<div className="setting-box">
<div className="discrimination-tab">
<div className="modal-btn-wrap sub">
{buttonMenu.map((item) => (
<button className={`btn-frame sub-tab ${buttonAct === item.id ? 'act' : ''}`} onClick={() => setButtonAct(item.id)}>
{buttonMenu.map((item, idx) => (
<button key={idx} className={`btn-frame sub-tab ${buttonAct === item.id ? 'act' : ''}`} onClick={() => setButtonAct(item.id)}>
{item.name}
</button>
))}
</div>
</div>
<div className="discrimination-box">
{buttonAct === 1 && <Eaves />}
{buttonAct === 2 && <Gable />}
{buttonAct === 3 && <Wall />}
{buttonAct === 4 && <HipAndGable />}
{buttonAct === 5 && <Jerkinhead />}
{buttonAct === 6 && <Shed />}
{buttonAct === 1 && <Eaves {...eavesProps} />}
{buttonAct === 2 && <Gable {...gableProps} />}
{buttonAct === 3 && <Wall {...wallProps} />}
{buttonAct === 4 && <HipAndGable {...hipAndGableProps} />}
{buttonAct === 5 && <Jerkinhead {...jerkinheadProps} />}
{buttonAct === 6 && <Shed {...shedProps} />}
</div>
<div className="grid-btn-wrap">
<button className="btn-frame sub-tab mr5">{getMessage('common.setting.rollback')}</button>
<button className="btn-frame sub-tab act">{getMessage('apply')}</button>
<button className="btn-frame sub-tab mr5" onClick={handleRollBack}>
{getMessage('common.setting.rollback')}
</button>
<button className="btn-frame sub-tab act" onClick={handleConfirm}>
{getMessage('apply')}
</button>
</div>
</div>
)

View File

@ -1,6 +1,7 @@
import { useMessage } from '@/hooks/useMessage'
import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils'
export default function Eaves() {
export default function Eaves({ pitch, setPitch, eavesOffset, setEavesOffset }) {
const { getMessage } = useMessage()
return (
<>
@ -9,7 +10,7 @@ export default function Eaves() {
{getMessage('slope')}
</span>
<div className="input-grid mr5" style={{ width: '100px' }}>
<input type="text" className="input-origin block" defaultValue={4} />
<input type="text" className="input-origin block" value={pitch} onChange={(e) => onlyNumberWithDotInputChange(e, setPitch)} />
</div>
<span className="thin">{getMessage('size')}</span>
</div>
@ -18,7 +19,7 @@ export default function Eaves() {
{getMessage('eaves.offset')}
</span>
<div className="input-grid mr5" style={{ width: '100px' }}>
<input type="text" className="input-origin block" defaultValue={500} />
<input type="text" className="input-origin block" value={eavesOffset} onChange={(e) => onlyNumberInputChange(e, setEavesOffset)} />
</div>
<span className="thin">mm</span>
</div>

View File

@ -1,13 +1,16 @@
import { useMessage } from '@/hooks/useMessage'
import { useEffect } from 'react'
import { onlyNumberInputChange } from '@/util/input-utils'
export default function Gable() {
export default function Gable({ gableOffset, setGableOffset }) {
const { getMessage } = useMessage()
return (
<>
<div className="outline-form">
<span className="mr10">{getMessage('gable.offset')}</span>
<div className="input-grid mr5" style={{ width: '100px' }}>
<input type="text" className="input-origin block" defaultValue={4} />
<input type="text" className="input-origin block" value={gableOffset} onChange={(e) => onlyNumberInputChange(e, setGableOffset)} />
</div>
<span className="thin">mm</span>
</div>

View File

@ -1,6 +1,7 @@
import { useMessage } from '@/hooks/useMessage'
import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils'
export default function HipAndGable() {
export default function HipAndGable({ pitch, setPitch, eavesOffset, setEavesOffset, hipAndGableWidth, setHipAndGableWidth }) {
const { getMessage } = useMessage()
return (
<>
@ -9,7 +10,7 @@ export default function HipAndGable() {
{getMessage('slope')}
</span>
<div className="input-grid mr5" style={{ width: '100px' }}>
<input type="text" className="input-origin block" defaultValue={4} />
<input type="text" className="input-origin block" value={pitch} onChange={(e) => onlyNumberWithDotInputChange(e, setPitch)} />
</div>
<span className="thin">{getMessage('size')}</span>
</div>
@ -18,16 +19,21 @@ export default function HipAndGable() {
{getMessage('eaves.offset')}
</span>
<div className="input-grid mr5" style={{ width: '100px' }}>
<input type="text" className="input-origin block" defaultValue={500} />
<input type="text" className="input-origin block" value={eavesOffset} onChange={(e) => onlyNumberInputChange(e, setEavesOffset)} />
</div>
<span className="thin">mm</span>
</div>
<div className="outline-form">
<span className="mr10" style={{ width: '60px' }}>
{getMessage('gable.offset')}
{getMessage('hipandgable.width')}
</span>
<div className="input-grid mr5" style={{ width: '100px' }}>
<input type="text" className="input-origin block" defaultValue={300} />
<input
type="text"
className="input-origin block"
value={hipAndGableWidth}
onChange={(e) => onlyNumberInputChange(e, setHipAndGableWidth)}
/>
</div>
<span className="thin">mm</span>
</div>

View File

@ -1,6 +1,7 @@
import { useMessage } from '@/hooks/useMessage'
import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils'
export default function Jerkinhead() {
export default function Jerkinhead({ gableOffset, setGableOffset, jerkinHeadWidth, setJerkinHeadWidth, jerkinHeadPitch, setJerkinHeadPitch }) {
const { getMessage } = useMessage()
return (
<>
@ -9,7 +10,7 @@ export default function Jerkinhead() {
{getMessage('gable.offset')}
</span>
<div className="input-grid mr5" style={{ width: '100px' }}>
<input type="text" className="input-origin block" defaultValue={300} />
<input type="text" className="input-origin block" value={gableOffset} onChange={(e) => onlyNumberInputChange(e, setGableOffset)} />
</div>
<span className="thin">mm</span>
</div>
@ -18,7 +19,7 @@ export default function Jerkinhead() {
{getMessage('jerkinhead.width')}
</span>
<div className="input-grid mr5" style={{ width: '100px' }}>
<input type="text" className="input-origin block" defaultValue={800} />
<input type="text" className="input-origin block" value={jerkinHeadWidth} onChange={(e) => onlyNumberInputChange(e, setJerkinHeadWidth)} />
</div>
<span className="thin">mm</span>
</div>
@ -27,7 +28,12 @@ export default function Jerkinhead() {
{getMessage('jerkinhead.slope')}
</span>
<div className="input-grid mr5" style={{ width: '100px' }}>
<input type="text" className="input-origin block" defaultValue={4.5} />
<input
type="text"
className="input-origin block"
value={jerkinHeadPitch}
onChange={(e) => onlyNumberWithDotInputChange(e, setJerkinHeadPitch)}
/>
</div>
<span className="thin">{getMessage('size')}</span>
</div>

View File

@ -1,13 +1,14 @@
import { useMessage } from '@/hooks/useMessage'
import { onlyNumberInputChange } from '@/util/input-utils'
export default function Shed() {
export default function Shed({ shedWidth, setShedWidth }) {
const { getMessage } = useMessage()
return (
<>
<div className="outline-form">
<span className="mr10">{getMessage('shed.width')}</span>
<div className="input-grid mr5" style={{ width: '100px' }}>
<input type="text" className="input-origin block" defaultValue={600} />
<input type="text" className="input-origin block" value={shedWidth} onChange={(e) => onlyNumberInputChange(e, setShedWidth)} />
</div>
<span className="thin">mm</span>
</div>

View File

@ -1,8 +1,8 @@
import { useState } from 'react'
import { useMessage } from '@/hooks/useMessage'
import { onlyNumberInputChange } from '@/util/input-utils'
export default function Wall() {
const [hasSleeve, setHasSleeve] = useState('0')
export default function Wall({ sleeveOffset, setSleeveOffset, hasSleeve, setHasSleeve }) {
const { getMessage } = useMessage()
return (
<>
@ -26,7 +26,13 @@ export default function Wall() {
<div className="eaves-keraba-td">
<div className="outline-form">
<div className="input-grid mr5" style={{ width: '100px' }}>
<input type="text" className="input-origin block" defaultValue={300} readOnly={hasSleeve === '0'} />
<input
type="text"
className="input-origin block"
value={sleeveOffset}
onChange={(e) => onlyNumberInputChange(e, setSleeveOffset)}
readOnly={hasSleeve === '0'}
/>
</div>
<span className="thin">mm</span>
</div>

View File

@ -6,6 +6,7 @@ import {
adsorptionPointModeState,
adsorptionRangeState,
canvasState,
currentCanvasPlanState,
dotLineIntervalSelector,
verticalHorizontalModeState,
} from '@/store/canvasAtom'
@ -43,7 +44,7 @@ export function useOuterLineWall() {
const adsorptionPointMode = useRecoilValue(adsorptionPointModeState)
const adsorptionRange = useRecoilValue(adsorptionRangeState)
const interval = useRecoilValue(dotLineIntervalSelector) // 가로 세로 간격
const currentCanvasPlan = useRecoilValue(currentCanvasPlanState)
const length1Ref = useRef(null)
const length2Ref = useRef(null)
const angle1Ref = useRef(null)
@ -73,14 +74,6 @@ export function useOuterLineWall() {
return
}
if (points.length === 0) {
// 만약 포인트가 없다면 모든 라인과 텍스트를 삭제 후 outerLines에서 point를 뽑아 points에 넣어준다.
const lengthTxts = canvas?.getObjects().filter((obj) => obj.name === 'lengthTxt')
lengthTxts.forEach((txt) => {
canvas?.remove(txt)
})
}
addCanvasMouseEventListener('mouse:down', mouseDown)
clear()
}, [verticalHorizontalMode, points, adsorptionPointAddMode, adsorptionPointMode, adsorptionRange, interval, tempGridMode])
@ -98,6 +91,18 @@ export function useOuterLineWall() {
addDocumentEventListener('keydown', document, keydown[type])
}, [type])
useEffect(() => {
const outerLinePoints = canvas?.getObjects().filter((obj) => obj.name === 'outerLinePoint')
const newPoints = []
if (points.length === 0 && outerLinePoints.length > 0) {
outerLinePoints.forEach((point) => {
newPoints.push({ x: point.left, y: point.top })
})
setPoints(newPoints)
}
}, [])
const clear = () => {
setLength1(0)
setLength2(0)
@ -186,6 +191,21 @@ export function useOuterLineWall() {
canvas?.add(point)
} else {
setOuterLineFix(false)
canvas
.getObjects()
.filter((obj) => obj.name === 'outerLinePoint')
.forEach((obj) => {
canvas.remove(obj)
})
points.forEach((point, idx) => {
const circle = new fabric.Circle({
left: point.x,
top: point.y,
visible: false,
name: 'outerLinePoint',
})
canvas.add(circle)
})
points.forEach((point, idx) => {
if (idx === 0) {
return
@ -272,6 +292,10 @@ export function useOuterLineWall() {
idx: idx,
selectable: true,
name: 'outerLine',
x1: point1.x,
y1: point1.y,
x2: point2.x,
y2: point2.y,
})
}

View File

@ -1,10 +1,11 @@
import { useEffect, useRef } from 'react'
import { LINE_TYPE } from '@/common/common'
import { useRecoilValue } from 'recoil'
import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'
import { canvasState, currentObjectState } from '@/store/canvasAtom'
import { useMode } from '@/hooks/useMode'
import { usePolygon } from '@/hooks/usePolygon'
import { useLine } from '@/hooks/useLine'
import { outerLinePointsState } from '@/store/outerLineAtom'
export function usePropertiesSetting() {
const canvas = useRecoilValue(canvasState)
@ -12,9 +13,10 @@ export function usePropertiesSetting() {
const currentObject = useRecoilValue(currentObjectState)
const { drawRoofPolygon } = useMode()
const setPoints = useResetRecoilState(outerLinePointsState)
const { addPolygonByLines } = usePolygon()
const { removeLine } = useLine()
const { removeLine, hideLine } = useLine()
useEffect(() => {
if (!currentObject) {
@ -125,7 +127,8 @@ export function usePropertiesSetting() {
stroke: '#000000',
strokeWidth: 4,
})
removeLine(line)
hideLine(hideLine)
})
const wall = addPolygonByLines(lines, { name: 'WallLine', fill: 'transparent', stroke: 'black' })
@ -133,7 +136,7 @@ export function usePropertiesSetting() {
wall.lines = [...lines]
drawRoofPolygon(wall)
setPoints([])
canvas.renderAll()
}
@ -153,6 +156,7 @@ export function usePropertiesSetting() {
})
canvas.renderAll()
setPoints([])
fn(false)
}

View File

@ -0,0 +1,496 @@
import { useEffect, useRef, useState } from 'react'
import { useMessage } from '@/hooks/useMessage'
import { useRecoilValue } from 'recoil'
import { canvasState, currentObjectState } from '@/store/canvasAtom'
import { LINE_TYPE } from '@/common/common'
import { usePolygon } from '@/hooks/usePolygon'
import { useMode } from '@/hooks/useMode'
import { useLine } from '@/hooks/useLine'
export function useRoofShapeSetting() {
const [shapeNum, setShapeNum] = useState(1)
const [buttonAct, setButtonAct] = useState(1)
const { getMessage } = useMessage()
const canvas = useRecoilValue(canvasState)
const { addPolygonByLines } = usePolygon()
const [pitch, setPitch] = useState(4)
const [eavesOffset, setEavesOffset] = useState(500) // 처마출폭
const [gableOffset, setGableOffset] = useState(300) // 케라바출폭
const [sleeveOffset, setSleeveOffset] = useState(300) // 소매출폭
const [jerkinHeadWidth, setJerkinHeadWidth] = useState(800) // 반절처 폭
const [jerkinHeadPitch, setJerkinHeadPitch] = useState(4.5) // 반절처 경사
const [hipAndGableWidth, setHipAndGableWidth] = useState(800) // 팔작지붕 폭
const [shedWidth, setShedWidth] = useState(300) // 한쪽흐름 폭
const [hasSleeve, setHasSleeve] = useState('0')
const currentObject = useRecoilValue(currentObjectState)
const { drawRoofPolygon } = useMode()
const { hideLine, showLine } = useLine()
const history = useRef([])
useEffect(() => {
const wallLine = canvas.getObjects().find((obj) => obj.name === 'wallLine')
canvas?.remove(wallLine)
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
outerLines.forEach((line) => {
showLine(line)
})
canvas?.renderAll()
}, [])
useEffect(() => {
if (shapeNum !== 4) {
return
}
if (!currentObject) {
return
}
if (currentObject.name !== 'outerLine') {
return
}
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
outerLines.forEach((line) => {
line.set({
stroke: '#000000',
strokeWidth: 4,
})
})
currentObject.set({
stroke: '#EA10AC',
strokeWidth: 4,
})
canvas.renderAll()
}, [currentObject])
useEffect(() => {
if (shapeNum === 4) {
canvas?.remove(canvas.getObjects().find((obj) => obj.name === 'wallLine'))
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
outerLines.forEach((line) => {
showLine(line)
line.bringToFront()
})
canvas?.renderAll()
}
setPitch(4)
setEavesOffset(500)
setGableOffset(300)
setSleeveOffset(300)
setJerkinHeadWidth(800)
setJerkinHeadPitch(4.5)
setHipAndGableWidth(800)
setShedWidth(300)
}, [shapeNum])
const shapeMenu = [
{ id: 1, name: getMessage('modal.roof.shape.setting.ridge') }, // 용마루
{ id: 2, name: getMessage('modal.roof.shape.setting.patten.a') }, // 패턴A
{ id: 3, name: getMessage('modal.roof.shape.setting.patten.b') }, // 패턴B
{ id: 4, name: getMessage('modal.roof.shape.setting.side') }, // 변별로 설정
{ id: 5, name: getMessage('commons.west') }, // 서
{ id: 6, name: getMessage('commons.east') }, // 동
{ id: 7, name: getMessage('commons.south') }, // 남
{ id: 8, name: getMessage('commons.north') }, // 북
]
const buttonMenu = [
{ id: 1, name: getMessage('eaves') },
{ id: 2, name: getMessage('gable') },
{ id: 3, name: getMessage('wall') },
{ id: 4, name: getMessage('hipandgable') },
{ id: 5, name: getMessage('jerkinhead') },
{ id: 6, name: getMessage('shed') },
]
//모달 닫기위한 함수
const handleSave = (fn) => {
//기존 wallLine 삭제
let outerLines
canvas?.remove(canvas.getObjects().find((obj) => obj.name === 'wallLine'))
canvas?.remove(canvas.getObjects().find((obj) => obj.name === 'roofBase'))
switch (shapeNum) {
case 1: {
outerLines = saveRidge()
break
}
case 2: {
outerLines = saveAPattern()
break
}
case 3: {
outerLines = saveBPattern()
break
}
case 4: {
outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
outerLines.forEach((line) => {
hideLine(line)
})
break
}
case 5: {
// 서쪽
initLineSetting()
outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
outerLines.forEach((line) => {
setWestAndEastRoof(line)
if (line.direction === 'bottom') {
line.attributes = {
offset: eavesOffset / 10,
pitch: pitch,
type: LINE_TYPE.WALLLINE.EAVES,
}
}
if (line.direction === 'top') {
line.attributes = {
offset: shedWidth / 10,
pitch: pitch,
type: LINE_TYPE.WALLLINE.SHED,
}
}
hideLine(line)
})
break
}
case 6: {
initLineSetting()
outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
outerLines.forEach((line) => {
setWestAndEastRoof(line)
if (line.direction === 'top') {
line.attributes = {
offset: eavesOffset / 10,
pitch: pitch,
type: LINE_TYPE.WALLLINE.EAVES,
}
}
if (line.direction === 'bottom') {
line.attributes = {
offset: shedWidth / 10,
pitch: pitch,
type: LINE_TYPE.WALLLINE.SHED,
}
}
hideLine(line)
})
break
}
case 7: {
initLineSetting()
outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
outerLines.forEach((line) => {
setWestAndEastRoof(line)
if (line.direction === 'left') {
line.attributes = {
offset: eavesOffset / 10,
pitch: pitch,
type: LINE_TYPE.WALLLINE.EAVES,
}
}
if (line.direction === 'right') {
line.attributes = {
offset: shedWidth / 10,
pitch: pitch,
type: LINE_TYPE.WALLLINE.SHED,
}
}
hideLine(line)
})
break
}
case 8: {
initLineSetting()
outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
outerLines.forEach((line) => {
setWestAndEastRoof(line)
if (line.direction === 'right') {
line.attributes = {
offset: eavesOffset / 10,
pitch: pitch,
type: LINE_TYPE.WALLLINE.EAVES,
}
}
if (line.direction === 'left') {
line.attributes = {
offset: shedWidth / 10,
pitch: pitch,
type: LINE_TYPE.WALLLINE.SHED,
}
}
hideLine(line)
})
break
}
}
const polygon = addPolygonByLines(outerLines, { name: 'wallLine' })
polygon.lines = [...outerLines]
drawRoofPolygon(polygon)
canvas?.renderAll()
console.log(canvas.getObjects())
fn && fn(false)
}
const initLineSetting = () => {
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
const tempPolygon = addPolygonByLines(outerLines)
tempPolygon.lines.forEach((line) => {
outerLines.forEach((outerLine) => {
if (
(line.startPoint === outerLine.startPoint && line.endPoint === outerLine.endPoint) ||
(line.startPoint === outerLine.endPoint && line.endPoint === outerLine.startPoint)
) {
outerLine.direction = line.direction
}
})
})
// 첫번째 line의 방향이 right일 경우
if (outerLines[0].direction === 'right') {
// top과 bottom의 방향을 바꾼다.
outerLines.forEach((line) => {
if (line.direction === 'top') {
line.direction = 'bottom'
} else if (line.direction === 'bottom') {
line.direction = 'top'
}
})
}
canvas.remove(tempPolygon)
}
// 동, 서 선택 시 가로라인을 케라바로 설정
const setWestAndEastRoof = (line) => {
if (line.direction === 'left' || line.direction === 'right') {
line.attributes = {
offset: gableOffset / 10,
type: LINE_TYPE.WALLLINE.GABLE,
}
}
}
// 남, 북 선택 시 세로라인을 케라바로 설정
const setSouthAndNorthRoof = (line) => {
if (line.direction === 'top' || line.direction === 'bottom') {
line.attributes = {
offset: gableOffset / 10,
type: LINE_TYPE.WALLLINE.GABLE,
}
}
}
const saveRidge = () => {
// 용마루 저장
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
outerLines.forEach((line) => {
line.attributes = {
offset: eavesOffset / 10,
pitch: pitch,
type: LINE_TYPE.WALLLINE.EAVES,
}
hideLine(line)
})
return outerLines
}
// 패턴 A : 가로선이 모두 케라바
const saveAPattern = () => {
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
outerLines
.filter((line) => line.direction === 'left' || line.direction === 'right')
.forEach((line) => {
line.attributes = {
offset: gableOffset / 10,
type: LINE_TYPE.WALLLINE.GABLE,
}
})
outerLines
.filter((line) => line.direction === 'top' || line.direction === 'bottom')
.forEach((line) => {
line.attributes = {
offset: eavesOffset / 10,
pitch: pitch,
type: LINE_TYPE.WALLLINE.EAVES,
}
})
return outerLines
}
// 패턴 B : 세로선이 모두 케라바
const saveBPattern = () => {
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
outerLines
.filter((line) => line.direction === 'left' || line.direction === 'right')
.forEach((line) => {
line.attributes = {
offset: eavesOffset / 10,
pitch: pitch,
type: LINE_TYPE.WALLLINE.EAVES,
}
})
outerLines
.filter((line) => line.direction === 'top' || line.direction === 'bottom')
.forEach((line) => {
line.attributes = {
offset: gableOffset / 10,
type: LINE_TYPE.WALLLINE.GABLE,
}
})
return outerLines
}
// 변별로 설정 팝업 내 적용
const handleConfirm = () => {
const selectedLine = canvas?.getActiveObject()
if (!selectedLine) {
return
}
let attributes
switch (buttonAct) {
case 1: {
// 처마
attributes = {
type: LINE_TYPE.WALLLINE.EAVES,
pitch: pitch,
offset: eavesOffset / 10,
}
break
}
case 2: {
// 케라바
attributes = {
type: LINE_TYPE.WALLLINE.GABLE,
offset: gableOffset / 10,
}
break
}
case 3: {
// 벽
attributes = {
type: LINE_TYPE.WALLLINE.WALL,
offset: hasSleeve === '0' ? 0 : sleeveOffset / 10,
}
break
}
case 4: {
// 팔작지붕
attributes = {
type: LINE_TYPE.WALLLINE.HIPANDGABLE,
pitch: pitch,
offset: eavesOffset / 10,
width: hipAndGableWidth / 10,
}
break
}
case 5: {
// 반절처
attributes = {
type: LINE_TYPE.WALLLINE.JERKINHEAD,
offset: gableOffset / 10,
width: jerkinHeadWidth / 10,
pitch: jerkinHeadPitch,
}
break
}
case 6: {
// 한쪽흐름
attributes = {
type: LINE_TYPE.WALLLINE.SHED,
offset: shedWidth / 10,
}
break
}
}
selectedLine.attributes = attributes
history.current.push(selectedLine)
nextLineFocus(selectedLine)
}
const nextLineFocus = (selectedLine) => {
const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
const index = lines.findIndex((line) => line.idx === selectedLine.idx)
const nextLine = lines[index + 1] || lines[0]
canvas.setActiveObject(nextLine)
}
// 변별로 설정 내 일변 전으로 돌아가기
const handleRollBack = () => {
if (history.current.length === 0) {
return
}
const lastLine = history.current.pop()
lastLine.set({
stroke: '#000000',
strokeWidth: 4,
})
canvas.setActiveObject(lastLine)
canvas.renderAll()
}
return {
shapeNum,
setShapeNum,
shapeMenu,
handleSave,
buttonMenu,
pitch,
setPitch,
eavesOffset,
setEavesOffset,
gableOffset,
setGableOffset,
sleeveOffset,
setSleeveOffset,
jerkinHeadWidth,
setJerkinHeadWidth,
jerkinHeadPitch,
setJerkinHeadPitch,
hipAndGableWidth,
setHipAndGableWidth,
shedWidth,
setShedWidth,
hasSleeve,
setHasSleeve,
buttonAct,
setButtonAct,
handleConfirm,
handleRollBack,
}
}

View File

@ -14,26 +14,38 @@ export const useLine = () => {
fontFamily: fontFamily,
})
if (line.length === 0) {
return null
}
canvas?.add(line)
return line
}
const addLineText = (line, length = getLengthByLine(line)) => {
removeLineText(line)
const lengthTxt = isNaN(Number(length)) ? length : length.toFixed(0)
const text = new fabric.Text(lengthTxt, {
left: (line.x2 + line.x1) / 2,
top: (line.y2 + line.y1) / 2,
parent: line,
name: 'lengthTxt',
fontSize: fontSize,
fontFamily: fontFamily,
const hideLine = (line) => {
line.set({
visible: false,
})
canvas
?.getObjects()
.find((obj) => obj.parent === line)
.set({
visible: false,
})
canvas?.renderAll()
}
canvas?.add(text)
return text
const showLine = (line) => {
line.set({
visible: true,
})
canvas
?.getObjects()
.find((obj) => obj.parent === line)
.set({
visible: true,
})
canvas?.renderAll()
}
const removeLine = (line) => {
@ -62,5 +74,7 @@ export const useLine = () => {
return {
addLine,
removeLine,
hideLine,
showLine,
}
}

View File

@ -47,6 +47,11 @@ export function usePlan() {
'minY',
'x',
'y',
'x1',
'x2',
'y1',
'y2',
'attributes',
'stickeyPoint',
])

View File

@ -12,6 +12,8 @@ export const usePolygon = () => {
const addPolygon = (points, options) => {
const polygon = new QPolygon(points, {
...options,
fill: options.fill || 'transparent',
stroke: options.stroke || '#000000',
fontSize: fontSize,
fontFamily: fontFamily,
selectable: true,

View File

@ -303,6 +303,7 @@
"gable": "ケラバ",
"wall": "壁",
"hipandgable": "八作屋根",
"hipandgable.width": "八作屋根 出幅",
"jerkinhead": "半折",
"shed": "片側の流れ",
"apply": "適用",

View File

@ -34,7 +34,7 @@
"modal.roof.shape.setting": "지붕형상 설정",
"modal.roof.shape.setting.ridge": "용마루",
"modal.roof.shape.setting.patten.a": "A 패턴",
"modal.roof.shape.setting.patten.b": "A 패턴",
"modal.roof.shape.setting.patten.b": "B 패턴",
"modal.roof.shape.setting.side": "변별로 설정",
"plan.menu.roof.cover": "지붕덮개",
"plan.menu.roof.cover.outline.drawing": "외벽선 그리기",
@ -305,6 +305,7 @@
"gable": "케라바",
"wall": "벽",
"hipandgable": "팔작지붕",
"hipandgable.width": "팔작지붕의 폭",
"jerkinhead": "반절처",
"shed": "한쪽흐름",
"apply": "적용",

View File

@ -1,6 +1,6 @@
// 숫자만 입력 가능한 input onChange 함수
export const onlyNumberInputChange = (e, callback) => {
let value = e.target.value.replace(/^0+/, '')
let value = e.target.value
value = value.replace(/[^-0-9]/g, '')
callback(value, e)
}

View File

@ -1197,6 +1197,7 @@ export const drawHippedRoof = (polygon, chon) => {
alert('대각선이 존재합니다.')
return
}
drawRidgeRoof(polygon, chon)
drawHips(polygon)
connectLinePoint(polygon)