Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/redo-undo
This commit is contained in:
commit
1d449737f6
@ -8,14 +8,17 @@ import { OUTER_LINE_TYPE } from '@/store/outerLineAtom'
|
||||
import OuterLineWall from '@/components/floor-plan/modal/lineTypes/OuterLineWall'
|
||||
import { useAuxiliaryDrawing } from '@/hooks/roofcover/useAuxiliaryDrawing'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
import { useEffect } from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { canvasState } from '@/store/canvasAtom'
|
||||
import Image from 'next/image'
|
||||
|
||||
export default function AuxiliaryDrawing({ id, pos = { x: 50, y: 230 } }) {
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
const { getMessage } = useMessage()
|
||||
const { closePopup } = usePopup()
|
||||
const [useCalcPad, setUseCalcPad] = useState(false)
|
||||
const disableKeypad = !useCalcPad
|
||||
|
||||
const types = [
|
||||
{ id: 1, name: getMessage('straight.line'), type: OUTER_LINE_TYPE.OUTER_LINE },
|
||||
@ -71,6 +74,7 @@ export default function AuxiliaryDrawing({ id, pos = { x: 50, y: 230 } }) {
|
||||
length1Ref,
|
||||
arrow1,
|
||||
setArrow1,
|
||||
disableKeypad,
|
||||
}
|
||||
|
||||
const rightAngleProps = {
|
||||
@ -84,6 +88,7 @@ export default function AuxiliaryDrawing({ id, pos = { x: 50, y: 230 } }) {
|
||||
setArrow1,
|
||||
arrow2,
|
||||
setArrow2,
|
||||
disableKeypad,
|
||||
}
|
||||
|
||||
const doublePitchProps = {
|
||||
@ -105,6 +110,7 @@ export default function AuxiliaryDrawing({ id, pos = { x: 50, y: 230 } }) {
|
||||
setArrow2,
|
||||
arrow1Ref,
|
||||
arrow2Ref,
|
||||
disableKeypad,
|
||||
}
|
||||
|
||||
const angleProps = {
|
||||
@ -114,6 +120,7 @@ export default function AuxiliaryDrawing({ id, pos = { x: 50, y: 230 } }) {
|
||||
length1,
|
||||
setLength1,
|
||||
length1Ref,
|
||||
disableKeypad,
|
||||
}
|
||||
|
||||
const diagonalLineProps = {
|
||||
@ -130,6 +137,7 @@ export default function AuxiliaryDrawing({ id, pos = { x: 50, y: 230 } }) {
|
||||
setArrow1,
|
||||
arrow2,
|
||||
setArrow2,
|
||||
disableKeypad,
|
||||
}
|
||||
|
||||
const onClickButton = (button) => {
|
||||
@ -160,7 +168,10 @@ export default function AuxiliaryDrawing({ id, pos = { x: 50, y: 230 } }) {
|
||||
{buttonAct === 4 && <Angle props={angleProps} />}
|
||||
{buttonAct === 5 && <Diagonal props={diagonalLineProps} />}
|
||||
</div>
|
||||
<div className="grid-btn-wrap">
|
||||
<div className="grid-btn-wrap" style={{ display: 'flex', alignItems: 'center', justifyContent: 'flex-end', gap: '5px' }}>
|
||||
<button style={{ width: 'auto' }} type="button" onClick={() => setUseCalcPad((prev) => !prev)}>
|
||||
<Image src={useCalcPad ? '/static/images/common/Icon_ON_Black.png' : '/static/images/common/Icon_OFF_Black.png'} alt="toggle" width={34} height={34} />
|
||||
</button>
|
||||
<button className="btn-frame modal mr5" onClick={handleRollback}>
|
||||
{getMessage('modal.cover.outline.rollback')}
|
||||
</button>
|
||||
|
||||
@ -18,6 +18,7 @@ import {
|
||||
currentCanvasPlanState,
|
||||
isManualModuleLayoutSetupState,
|
||||
isManualModuleSetupState,
|
||||
moduleSetupOptionState,
|
||||
toggleManualSetupModeState,
|
||||
} from '@/store/canvasAtom'
|
||||
import { loginUserStore } from '@/store/commonAtom'
|
||||
@ -49,6 +50,7 @@ export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) {
|
||||
const [checkedModules, setCheckedModules] = useRecoilState(checkedModuleState)
|
||||
const [roofs, setRoofs] = useState(addedRoofs)
|
||||
const [manualSetupMode, setManualSetupMode] = useRecoilState(toggleManualSetupModeState)
|
||||
const [moduleSetupOption, setModuleSetupOption] = useRecoilState(moduleSetupOptionState)
|
||||
const [layoutSetup, setLayoutSetup] = useState([{}])
|
||||
const {
|
||||
selectedModules,
|
||||
@ -218,8 +220,12 @@ export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) {
|
||||
}
|
||||
|
||||
const handleManualModuleSetup = () => {
|
||||
setManualSetupMode(`manualSetup_${!isManualModuleSetup}`)
|
||||
setIsManualModuleSetup(!isManualModuleSetup)
|
||||
const nextManual = !isManualModuleSetup
|
||||
setManualSetupMode(`manualSetup_${nextManual}`)
|
||||
setIsManualModuleSetup(nextManual)
|
||||
if (nextManual) {
|
||||
setModuleSetupOption((prev) => ({ ...prev, isChidori: true }))
|
||||
}
|
||||
}
|
||||
|
||||
const handleManualModuleLayoutSetup = () => {
|
||||
|
||||
@ -428,7 +428,7 @@ const Trestle = forwardRef((props, ref) => {
|
||||
const newCvrYn = constructionList[index].cvrYn
|
||||
const newCvrChecked = newCvrYn === 'Y'
|
||||
const newSnowGdPossYn = constructionList[index].snowGdPossYn
|
||||
const newSnowGdChecked = newSnowGdPossYn === 'Y'
|
||||
const newSnowGdChecked = false
|
||||
|
||||
setCvrYn(newCvrYn)
|
||||
setSnowGdPossYn(newSnowGdPossYn)
|
||||
|
||||
@ -6,10 +6,14 @@ 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'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
import { useState } from 'react'
|
||||
import Image from 'next/image'
|
||||
|
||||
export default function EavesGableEdit({ id, pos = { x: 50, y: 230 } }) {
|
||||
const { getMessage } = useMessage()
|
||||
const { closePopup } = usePopup()
|
||||
const [useCalcPad, setUseCalcPad] = useState(false)
|
||||
const disableKeypad = !useCalcPad
|
||||
|
||||
const { type, setType, buttonMenu, TYPES, pitchRef, offsetRef, widthRef, radioTypeRef, pitchText } = useEavesGableEdit(id)
|
||||
const eavesProps = {
|
||||
@ -18,6 +22,7 @@ export default function EavesGableEdit({ id, pos = { x: 50, y: 230 } }) {
|
||||
widthRef,
|
||||
radioTypeRef,
|
||||
pitchText,
|
||||
disableKeypad,
|
||||
}
|
||||
|
||||
const gableProps = {
|
||||
@ -26,15 +31,18 @@ export default function EavesGableEdit({ id, pos = { x: 50, y: 230 } }) {
|
||||
widthRef,
|
||||
radioTypeRef,
|
||||
pitchText,
|
||||
disableKeypad,
|
||||
}
|
||||
|
||||
const wallMergeProps = {
|
||||
offsetRef,
|
||||
radioTypeRef,
|
||||
disableKeypad,
|
||||
}
|
||||
|
||||
const shedProps = {
|
||||
offsetRef,
|
||||
disableKeypad,
|
||||
}
|
||||
|
||||
return (
|
||||
@ -48,8 +56,13 @@ export default function EavesGableEdit({ id, pos = { x: 50, y: 230 } }) {
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<div className="properties-setting-wrap outer">
|
||||
<div className="setting-tit">{getMessage('setting')}</div>
|
||||
<div className="properties-setting-wrap outer" style={{margin: '10px'}}>
|
||||
<div className="setting-tit" style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<span>{getMessage('setting')}</span>
|
||||
<button style={{ width: 'auto' }} type="button" onClick={() => setUseCalcPad((prev) => !prev)}>
|
||||
<Image src={useCalcPad ? '/static/images/common/Icon_ON_Black.png' : '/static/images/common/Icon_OFF_Black.png'} alt="toggle" width={34} height={34} />
|
||||
</button>
|
||||
</div>
|
||||
{type === TYPES.EAVES && <Eaves {...eavesProps} />}
|
||||
{type === TYPES.GABLE && <Gable {...gableProps} />}
|
||||
{type === TYPES.WALL_MERGE && <WallMerge {...wallMergeProps} />}
|
||||
|
||||
@ -6,7 +6,7 @@ import { ANGLE_TYPE, currentAngleTypeSelector } from '@/store/canvasAtom'
|
||||
import { normalizeDecimalLimit, normalizeDigits } from '@/util/input-utils'
|
||||
import { CalculatorInput } from '@/components/common/input/CalcInput'
|
||||
|
||||
export default function Eaves({ pitchRef, offsetRef, widthRef, radioTypeRef, pitchText }) {
|
||||
export default function Eaves({ pitchRef, offsetRef, widthRef, radioTypeRef, pitchText, disableKeypad }) {
|
||||
const { getMessage } = useMessage()
|
||||
const [type, setType] = useState('1')
|
||||
const onChange = (e) => {
|
||||
@ -43,6 +43,7 @@ export default function Eaves({ pitchRef, offsetRef, widthRef, radioTypeRef, pit
|
||||
onChange={(value) => {
|
||||
if (pitchRef?.current) pitchRef.current.value = value
|
||||
}}
|
||||
disableKeypad={disableKeypad}
|
||||
options={{
|
||||
allowNegative: false,
|
||||
allowDecimal: true //(index !== 0),
|
||||
@ -77,6 +78,7 @@ export default function Eaves({ pitchRef, offsetRef, widthRef, radioTypeRef, pit
|
||||
onChange={(value) => {
|
||||
if (offsetRef?.current) offsetRef.current.value = value
|
||||
}}
|
||||
disableKeypad={disableKeypad}
|
||||
options={{
|
||||
allowNegative: false,
|
||||
allowDecimal: false //(index !== 0),
|
||||
@ -144,6 +146,7 @@ export default function Eaves({ pitchRef, offsetRef, widthRef, radioTypeRef, pit
|
||||
onChange={(value) => {
|
||||
if (widthRef?.current) widthRef.current.value = value
|
||||
}}
|
||||
disableKeypad={disableKeypad}
|
||||
options={{
|
||||
allowNegative: false,
|
||||
allowDecimal: false //(index !== 0),
|
||||
|
||||
@ -5,7 +5,7 @@ import { useRecoilValue } from 'recoil'
|
||||
import { ANGLE_TYPE, currentAngleTypeSelector } from '@/store/canvasAtom'
|
||||
import { CalculatorInput } from '@/components/common/input/CalcInput'
|
||||
|
||||
export default function Gable({ pitchRef, offsetRef, widthRef, radioTypeRef, pitchText }) {
|
||||
export default function Gable({ pitchRef, offsetRef, widthRef, radioTypeRef, pitchText, disableKeypad }) {
|
||||
const { getMessage } = useMessage()
|
||||
const [type, setType] = useState('1')
|
||||
const onChange = (e) => {
|
||||
@ -30,6 +30,7 @@ export default function Gable({ pitchRef, offsetRef, widthRef, radioTypeRef, pit
|
||||
className="input-origin block"
|
||||
ref={offsetRef}
|
||||
value={300}
|
||||
disableKeypad={disableKeypad}
|
||||
options={{
|
||||
allowNegative: false,
|
||||
allowDecimal: false //(index !== 0),
|
||||
@ -96,6 +97,7 @@ export default function Gable({ pitchRef, offsetRef, widthRef, radioTypeRef, pit
|
||||
onChange={(value) => {
|
||||
if (pitchRef?.current) pitchRef.current.value = value
|
||||
}}
|
||||
disableKeypad={disableKeypad}
|
||||
options={{
|
||||
allowNegative: false,
|
||||
allowDecimal: true //(index !== 0),
|
||||
@ -129,6 +131,7 @@ export default function Gable({ pitchRef, offsetRef, widthRef, radioTypeRef, pit
|
||||
ref={widthRef}
|
||||
value={800}
|
||||
readOnly={type === '1'}
|
||||
disableKeypad={disableKeypad}
|
||||
options={{
|
||||
allowNegative: false,
|
||||
allowDecimal: false //(index !== 0),
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { CalculatorInput } from '@/components/common/input/CalcInput'
|
||||
|
||||
export default function Shed({ offsetRef }) {
|
||||
export default function Shed({ offsetRef, disableKeypad }) {
|
||||
const { getMessage } = useMessage()
|
||||
return (
|
||||
<>
|
||||
@ -19,6 +19,7 @@ export default function Shed({ offsetRef }) {
|
||||
className="input-origin block"
|
||||
ref={offsetRef}
|
||||
value={300}
|
||||
disableKeypad={disableKeypad}
|
||||
options={{
|
||||
allowNegative: false,
|
||||
allowDecimal: false //(index !== 0),
|
||||
|
||||
@ -3,7 +3,7 @@ import Image from 'next/image'
|
||||
import { useState } from 'react'
|
||||
import { CalculatorInput } from '@/components/common/input/CalcInput'
|
||||
|
||||
export default function WallMerge({ offsetRef, radioTypeRef }) {
|
||||
export default function WallMerge({ offsetRef, radioTypeRef, disableKeypad }) {
|
||||
const { getMessage } = useMessage()
|
||||
const [type, setType] = useState('1')
|
||||
const onChange = (e) => {
|
||||
@ -61,6 +61,7 @@ export default function WallMerge({ offsetRef, radioTypeRef }) {
|
||||
ref={offsetRef}
|
||||
value={300}
|
||||
readOnly={type === '1'}
|
||||
disableKeypad={disableKeypad}
|
||||
options={{
|
||||
allowNegative: false,
|
||||
allowDecimal: false //(index !== 0),
|
||||
|
||||
@ -3,16 +3,22 @@ import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||
import FlowLine from '@/components/floor-plan/modal/movement/type/FlowLine'
|
||||
import Updown from '@/components/floor-plan/modal/movement/type/Updown'
|
||||
import { useMovementSetting } from '@/hooks/roofcover/useMovementSetting'
|
||||
import { useState } from 'react'
|
||||
import Image from 'next/image'
|
||||
|
||||
export default function MovementSetting({ id, pos = { x: 50, y: 230 } }) {
|
||||
const { getMessage } = useMessage()
|
||||
const { TYPE, closePopup, buttonType, type, setType, FLOW_LINE_REF, UP_DOWN_REF, handleSave } = useMovementSetting(id)
|
||||
const [useCalcPad, setUseCalcPad] = useState(false)
|
||||
const disableKeypad = !useCalcPad
|
||||
|
||||
const flowLineProps = {
|
||||
FLOW_LINE_REF,
|
||||
disableKeypad,
|
||||
}
|
||||
const updownProps = {
|
||||
UP_DOWN_REF,
|
||||
disableKeypad,
|
||||
}
|
||||
|
||||
return (
|
||||
@ -30,7 +36,10 @@ export default function MovementSetting({ id, pos = { x: 50, y: 230 } }) {
|
||||
{type === TYPE.FLOW_LINE && <FlowLine {...flowLineProps} />}
|
||||
{type === TYPE.UP_DOWN && <Updown {...updownProps} />}
|
||||
</div>
|
||||
<div className="grid-btn-wrap">
|
||||
<div className="grid-btn-wrap" style={{ display: 'flex', alignItems: 'center', justifyContent: 'flex-end', gap: '5px' }}>
|
||||
<button style={{ width: 'auto' }} type="button" onClick={() => setUseCalcPad((prev) => !prev)}>
|
||||
<Image src={useCalcPad ? '/static/images/common/Icon_ON_Black.png' : '/static/images/common/Icon_OFF_Black.png'} alt="toggle" width={34} height={34} />
|
||||
</button>
|
||||
<button className="btn-frame modal act" onClick={handleSave}>
|
||||
{getMessage('modal.common.save')}
|
||||
</button>
|
||||
|
||||
@ -9,7 +9,7 @@ const FLOW_LINE_TYPE = {
|
||||
UP_RIGHT: 'upRight',
|
||||
}
|
||||
|
||||
export default function FlowLine({ FLOW_LINE_REF }) {
|
||||
export default function FlowLine({ FLOW_LINE_REF, disableKeypad }) {
|
||||
const { getMessage } = useMessage()
|
||||
const [type, setType] = useState(FLOW_LINE_TYPE.DOWN_LEFT)
|
||||
const [filledInput, setFilledInput] = useState('')
|
||||
@ -87,6 +87,7 @@ export default function FlowLine({ FLOW_LINE_REF }) {
|
||||
value={filledInput}
|
||||
onFocus={handleFocus}
|
||||
onChange={(value)=>{setFilledInput(value)}}
|
||||
disableKeypad={disableKeypad}
|
||||
options={{
|
||||
allowNegative: false,
|
||||
allowDecimal: false
|
||||
|
||||
@ -10,7 +10,7 @@ const UP_DOWN_TYPE = {
|
||||
DOWN: 'down',
|
||||
}
|
||||
|
||||
export default function Updown({ UP_DOWN_REF }) {
|
||||
export default function Updown({ UP_DOWN_REF, disableKeypad }) {
|
||||
const { getMessage } = useMessage()
|
||||
const [type, setType] = useState(UP_DOWN_TYPE.UP)
|
||||
const [filledInput, setFilledInput] = useState('100')
|
||||
@ -87,6 +87,7 @@ export default function Updown({ UP_DOWN_REF }) {
|
||||
value={filledInput}
|
||||
onFocus={handleFocus}
|
||||
onChange={(value)=>{setFilledInput(value)}}
|
||||
disableKeypad={disableKeypad}
|
||||
options={{
|
||||
allowNegative: false,
|
||||
allowDecimal: false
|
||||
|
||||
@ -266,14 +266,15 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
|
||||
}, { skipSideEffects: true })
|
||||
|
||||
const roofs = canvas.getObjects().filter((obj) => obj.roofMaterial?.index === 0)
|
||||
const firstRoof = roofs[0]
|
||||
|
||||
roofs.forEach((roof) => {
|
||||
if (firstRoof) {
|
||||
/** 모양 패턴 설정 */
|
||||
setSurfaceShapePattern(roof, roofDisplay.column, false, { ...roofInfo })
|
||||
roof.roofMaterial = { ...roofInfo }
|
||||
drawDirectionArrow(roof)
|
||||
setPolygonLinesActualSize(roof, true)
|
||||
})
|
||||
setSurfaceShapePattern(firstRoof, roofDisplay.column, false, { ...roofInfo })
|
||||
firstRoof.roofMaterial = { ...roofInfo }
|
||||
drawDirectionArrow(firstRoof)
|
||||
setPolygonLinesActualSize(firstRoof, true)
|
||||
}
|
||||
canvas.renderAll()
|
||||
|
||||
/** 지붕면 존재 여부에 따라 메뉴 설정 */
|
||||
|
||||
@ -5,27 +5,34 @@ import Shed from '@/components/floor-plan/modal/roofShape/passivity/Shed'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useRoofShapePassivitySetting } from '@/hooks/roofcover/useRoofShapePassivitySetting'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
import { useState } from 'react'
|
||||
import Image from 'next/image'
|
||||
|
||||
export default function RoofShapePassivitySetting({ id, pos = { x: 50, y: 230 } }) {
|
||||
const { handleSave, handleConfirm, handleRollback, buttons, type, setType, TYPES, offsetRef, pitchRef, pitchText } =
|
||||
useRoofShapePassivitySetting(id)
|
||||
const { getMessage } = useMessage()
|
||||
const { closePopup } = usePopup()
|
||||
const [useCalcPad, setUseCalcPad] = useState(false)
|
||||
const disableKeypad = !useCalcPad
|
||||
|
||||
const eavesProps = {
|
||||
offsetRef,
|
||||
pitchRef,
|
||||
pitchText,
|
||||
disableKeypad,
|
||||
}
|
||||
|
||||
const gableProps = {
|
||||
offsetRef,
|
||||
pitchRef,
|
||||
pitchText,
|
||||
disableKeypad,
|
||||
}
|
||||
|
||||
const shedProps = {
|
||||
offsetRef,
|
||||
disableKeypad,
|
||||
}
|
||||
|
||||
return (
|
||||
@ -55,7 +62,10 @@ export default function RoofShapePassivitySetting({ id, pos = { x: 50, y: 230 }
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid-btn-wrap">
|
||||
<div className="grid-btn-wrap" style={{ display: 'flex', alignItems: 'center', justifyContent: 'flex-end', gap: '5px' }}>
|
||||
<button style={{ width: 'auto' }} type="button" onClick={() => setUseCalcPad((prev) => !prev)}>
|
||||
<Image src={useCalcPad ? '/static/images/common/Icon_ON_Black.png' : '/static/images/common/Icon_OFF_Black.png'} alt="toggle" width={34} height={34} />
|
||||
</button>
|
||||
<button className="btn-frame modal act" onClick={() => handleSave(id)}>
|
||||
{getMessage('common.setting.finish')}
|
||||
</button>
|
||||
|
||||
@ -5,7 +5,7 @@ import { selectedRoofMaterialSelector } from '@/store/settingAtom'
|
||||
import { useEffect } from 'react'
|
||||
import { CalculatorInput } from '@/components/common/input/CalcInput'
|
||||
|
||||
export default function Eaves({ offsetRef, pitchRef, pitchText }) {
|
||||
export default function Eaves({ offsetRef, pitchRef, pitchText, disableKeypad }) {
|
||||
const { getMessage } = useMessage()
|
||||
const currentAngleType = useRecoilValue(currentAngleTypeSelector)
|
||||
const selectedRoofMaterial = useRecoilValue(selectedRoofMaterialSelector)
|
||||
@ -30,6 +30,7 @@ export default function Eaves({ offsetRef, pitchRef, pitchText }) {
|
||||
className="input-origin block"
|
||||
ref={pitchRef}
|
||||
value={currentAngleType === ANGLE_TYPE.SLOPE ? selectedRoofMaterial.pitch : selectedRoofMaterial.angle}
|
||||
disableKeypad={disableKeypad}
|
||||
options={{
|
||||
allowNegative: false,
|
||||
allowDecimal: true //(index !== 0),
|
||||
@ -52,6 +53,7 @@ export default function Eaves({ offsetRef, pitchRef, pitchText }) {
|
||||
value={offsetRef.current?.value ?? 500} // Set default value to 500
|
||||
ref={offsetRef}
|
||||
onChange={() => {}}
|
||||
disableKeypad={disableKeypad}
|
||||
options={{
|
||||
allowNegative: false,
|
||||
allowDecimal: false
|
||||
|
||||
@ -3,7 +3,7 @@ import { useRecoilValue } from 'recoil'
|
||||
import { currentAngleTypeSelector } from '@/store/canvasAtom'
|
||||
import { CalculatorInput } from '@/components/common/input/CalcInput'
|
||||
|
||||
export default function Gable({ offsetRef }) {
|
||||
export default function Gable({ offsetRef, disableKeypad }) {
|
||||
const { getMessage } = useMessage()
|
||||
const currentAngleType = useRecoilValue(currentAngleTypeSelector)
|
||||
return (
|
||||
@ -22,6 +22,7 @@ export default function Gable({ offsetRef }) {
|
||||
value={offsetRef.current?.value ?? 300} // Set default value to 500
|
||||
ref={offsetRef}
|
||||
onChange={() => {}}
|
||||
disableKeypad={disableKeypad}
|
||||
options={{
|
||||
allowNegative: false,
|
||||
allowDecimal: false
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { CalculatorInput } from '@/components/common/input/CalcInput'
|
||||
|
||||
export default function Shed({ offsetRef }) {
|
||||
export default function Shed({ offsetRef, disableKeypad }) {
|
||||
const { getMessage } = useMessage()
|
||||
return (
|
||||
<>
|
||||
@ -19,6 +19,7 @@ export default function Shed({ offsetRef }) {
|
||||
value={offsetRef.current?.value ?? 300} // Set default value to 500
|
||||
ref={offsetRef}
|
||||
onChange={() => {}}
|
||||
disableKeypad={disableKeypad}
|
||||
options={{
|
||||
allowNegative: false,
|
||||
allowDecimal: false
|
||||
|
||||
@ -4,10 +4,14 @@ import WallLine from '@/components/floor-plan/modal/wallLineOffset/type/WallLine
|
||||
import Offset from '@/components/floor-plan/modal/wallLineOffset/type/Offset'
|
||||
import { useWallLineOffsetSetting } from '@/hooks/roofcover/useWallLineOffsetSetting'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
import { useState } from 'react'
|
||||
import Image from 'next/image'
|
||||
|
||||
export default function WallLineOffsetSetting({ id, pos = { x: 50, y: 230 } }) {
|
||||
const { getMessage } = useMessage()
|
||||
const { closePopup } = usePopup()
|
||||
const [useCalcPad, setUseCalcPad] = useState(false)
|
||||
const disableKeypad = !useCalcPad
|
||||
const {
|
||||
type,
|
||||
setType,
|
||||
@ -30,12 +34,14 @@ export default function WallLineOffsetSetting({ id, pos = { x: 50, y: 230 } }) {
|
||||
arrow2Ref,
|
||||
radioTypeRef,
|
||||
currentWallLineRef,
|
||||
disableKeypad,
|
||||
}
|
||||
|
||||
const offsetProps = {
|
||||
length1Ref,
|
||||
arrow1Ref,
|
||||
currentWallLineRef,
|
||||
disableKeypad,
|
||||
}
|
||||
|
||||
return (
|
||||
@ -54,7 +60,10 @@ export default function WallLineOffsetSetting({ id, pos = { x: 50, y: 230 } }) {
|
||||
{type === TYPES.WALL_LINE_EDIT && <WallLine ref={wallLineEditRef} {...wallLineProps} />}
|
||||
{type === TYPES.OFFSET && <Offset {...offsetProps} />}
|
||||
</div>
|
||||
<div className="grid-btn-wrap">
|
||||
<div className="grid-btn-wrap" style={{ display: 'flex', alignItems: 'center', justifyContent: 'flex-end', gap: '5px' }}>
|
||||
<button style={{ width: 'auto' }} type="button" onClick={() => setUseCalcPad((prev) => !prev)}>
|
||||
<Image src={useCalcPad ? '/static/images/common/Icon_ON_Black.png' : '/static/images/common/Icon_OFF_Black.png'} alt="toggle" width={34} height={34} />
|
||||
</button>
|
||||
<button className="btn-frame modal act" onClick={handleSave}>
|
||||
{getMessage('modal.common.save')}
|
||||
</button>
|
||||
|
||||
@ -3,7 +3,7 @@ import { useEffect, useState } from 'react'
|
||||
import { useEvent } from '@/hooks/useEvent'
|
||||
import { CalculatorInput } from '@/components/common/input/CalcInput'
|
||||
|
||||
export default function Offset({ length1Ref, arrow1Ref, currentWallLineRef }) {
|
||||
export default function Offset({ length1Ref, arrow1Ref, currentWallLineRef, disableKeypad }) {
|
||||
const { getMessage } = useMessage()
|
||||
const { addDocumentEventListener, initEvent } = useEvent()
|
||||
// const { addDocumentEventListener, initEvent } = useContext(EventContext)
|
||||
@ -84,6 +84,7 @@ export default function Offset({ length1Ref, arrow1Ref, currentWallLineRef }) {
|
||||
value={length1Ref.current?.value ?? 0}
|
||||
ref={length1Ref}
|
||||
onChange={() => {}}
|
||||
disableKeypad={disableKeypad}
|
||||
options={{
|
||||
allowNegative: false,
|
||||
allowDecimal: false
|
||||
|
||||
@ -3,7 +3,7 @@ import { forwardRef, useEffect, useImperativeHandle, useState } from 'react'
|
||||
import { useEvent } from '@/hooks/useEvent'
|
||||
import { CalculatorInput } from '@/components/common/input/CalcInput'
|
||||
|
||||
export default forwardRef(function WallLine({ length1Ref, length2Ref, arrow1Ref, arrow2Ref, radioTypeRef, currentWallLineRef }, ref) {
|
||||
export default forwardRef(function WallLine({ length1Ref, length2Ref, arrow1Ref, arrow2Ref, radioTypeRef, currentWallLineRef, disableKeypad }, ref) {
|
||||
const { getMessage } = useMessage()
|
||||
const { addDocumentEventListener, initEvent } = useEvent()
|
||||
// const { addDocumentEventListener, initEvent } = useContext(EventContext)
|
||||
@ -57,6 +57,7 @@ export default forwardRef(function WallLine({ length1Ref, length2Ref, arrow1Ref,
|
||||
ref={length1Ref}
|
||||
onChange={() => {}}
|
||||
readOnly={type !== 1}
|
||||
disableKeypad={disableKeypad}
|
||||
options={{
|
||||
allowNegative: false,
|
||||
allowDecimal: false
|
||||
@ -105,6 +106,7 @@ export default forwardRef(function WallLine({ length1Ref, length2Ref, arrow1Ref,
|
||||
ref={length2Ref}
|
||||
onChange={() => {}}
|
||||
readOnly={type !== 2}
|
||||
disableKeypad={disableKeypad}
|
||||
options={{
|
||||
allowNegative: false,
|
||||
allowDecimal: false
|
||||
|
||||
@ -1046,6 +1046,33 @@ export default function StuffDetail() {
|
||||
form.setValue('otherSaleStoreId', info.saleStoreId)
|
||||
form.setValue('otherSaleStoreName', info.saleStoreName)
|
||||
form.setValue('otherSaleStoreLevel', info.saleStoreLevel)
|
||||
get({ url: `/api/object/saleStore/${info.saleStoreId}/firstAgent` }).then((res) => {
|
||||
if (res?.firstAgentId) {
|
||||
const firstAgent = { saleStoreId: res.firstAgentId, saleStoreName: res.firstAgentName }
|
||||
setSaleStoreList((prev) => {
|
||||
const exists = prev.some((s) => s.saleStoreId === res.firstAgentId)
|
||||
return exists ? prev : [...prev, firstAgent]
|
||||
})
|
||||
setShowSaleStoreList((prev) => {
|
||||
const exists = prev.some((s) => s.saleStoreId === res.firstAgentId)
|
||||
return exists ? prev : [...prev, firstAgent]
|
||||
})
|
||||
setSelOptions(res.firstAgentId)
|
||||
form.setValue('saleStoreId', res.firstAgentId)
|
||||
form.setValue('saleStoreName', res.firstAgentName)
|
||||
form.setValue('saleStoreLevel', '1')
|
||||
} else {
|
||||
setSelOptions('')
|
||||
form.setValue('saleStoreId', '')
|
||||
form.setValue('saleStoreName', '')
|
||||
form.setValue('saleStoreLevel', '')
|
||||
}
|
||||
}).catch(() => {
|
||||
setSelOptions('')
|
||||
form.setValue('saleStoreId', '')
|
||||
form.setValue('saleStoreName', '')
|
||||
form.setValue('saleStoreLevel', '')
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1166,7 +1166,7 @@ export default function StuffSearchCondition() {
|
||||
<th>{getMessage('stuff.search.schSelSaleStoreId')}</th>
|
||||
<td colSpan={5}>
|
||||
<div className="form-flex-wrap">
|
||||
<div className="select-wrap mr5" style={{ flex: 1 }}>
|
||||
<div className="select-wrap mr5" style={{ flex: 1, display: session?.storeId !== 'T01' && session?.storeLvl !== '1' ? 'none' : undefined }}>
|
||||
{session?.storeId === 'T01' && (
|
||||
<Select
|
||||
id="long-value-select1"
|
||||
@ -1235,7 +1235,7 @@ export default function StuffSearchCondition() {
|
||||
isClearable={false}
|
||||
/>
|
||||
)}
|
||||
{session?.storeId !== 'T01' && session?.storeLvl !== '1' && (
|
||||
{false && session?.storeId !== 'T01' && session?.storeLvl !== '1' && (
|
||||
<Select
|
||||
id="long-value-select1"
|
||||
instanceId="long-value-select1"
|
||||
@ -1267,7 +1267,7 @@ export default function StuffSearchCondition() {
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="select-wrap mr10" style={{ flex: 1 }}>
|
||||
<div className="select-wrap mr10" style={{ flex: session?.storeId !== 'T01' && session?.storeLvl !== '1' ? '0 0 50%' : 1 }}>
|
||||
<Select
|
||||
id="long-value-select2"
|
||||
instanceId="long-value-select2"
|
||||
|
||||
@ -11,7 +11,7 @@ import { isObjectNotEmpty, queryStringFormatter } from '@/util/common-utils'
|
||||
import QPagination from '@/components/common/pagination/QPagination'
|
||||
import { useSwal } from '@/hooks/useSwal'
|
||||
import { QcastContext } from '@/app/QcastProvider'
|
||||
import { sanitizeDecimalInputEvent } from '@/util/input-utils'
|
||||
import { sanitizeDecimalInputEvent, sanitizeIntegerInputEvent } from '@/util/input-utils'
|
||||
|
||||
export default function PlanRequestPop(props) {
|
||||
const [pageNo, setPageNo] = useState(1) //현재 페이지 번호
|
||||
@ -212,6 +212,7 @@ export default function PlanRequestPop(props) {
|
||||
//설계의뢰 그리드에서 선택한 설계의뢰 정보
|
||||
const getSelectedRowdata = (data) => {
|
||||
if (isNotEmptyArray(data)) {
|
||||
console.log('planReqObject fields:', data[0])
|
||||
setPlanReqObject(data[0])
|
||||
} else {
|
||||
setPlanReqObject({})
|
||||
|
||||
@ -47,12 +47,12 @@ export function useCanvasConfigInitialize() {
|
||||
offsetTexts.forEach((obj) => {
|
||||
let text = `${!obj.originText ? '' : obj.originText}`
|
||||
if (!obj.onlyOffset) {
|
||||
text = text + `-∠${getDegreeByChon(obj.pitch)}${angleUnit}`
|
||||
text = text + `-∠${obj.roofAngle ?? getDegreeByChon(obj.pitch)}${angleUnit}`
|
||||
}
|
||||
obj.set({ text: text })
|
||||
})
|
||||
flowTexts.forEach((obj) => {
|
||||
obj.set({ text: `${!obj.originText ? '' : obj.originText + '-'}∠${getDegreeByChon(obj.pitch)}${pitchText}` })
|
||||
obj.set({ text: `${!obj.originText ? '' : obj.originText + '-'}∠${obj.roofAngle ?? getDegreeByChon(obj.pitch)}${pitchText}` })
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -7,7 +7,10 @@ import { atom, useRecoilState, useRecoilValue } from 'recoil'
|
||||
|
||||
|
||||
// API 요청을 저장할 모듈 레벨 변수 (Hook 외부)
|
||||
let roofMaterialPromise = null;
|
||||
// 전역 Promise 캐시
|
||||
const roofMaterialPromiseCache = new Map()
|
||||
let cacheKey = 'default'
|
||||
|
||||
/**
|
||||
* 마스터 컨트롤러 훅
|
||||
* @returns
|
||||
@ -22,24 +25,117 @@ export function useMasterController() {
|
||||
* 지붕재 목록 조회
|
||||
* @returns
|
||||
*/
|
||||
|
||||
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
|
||||
|
||||
const getRoofMaterialList = async () => {
|
||||
// 1. 이미 진행 중이거나 완료된 Promise가 있으면 그것을 반환
|
||||
if (roofMaterialPromise) {
|
||||
return roofMaterialPromise;
|
||||
if (roofMaterialPromiseCache.has(cacheKey)) {
|
||||
console.log('🔄 getRoofMaterialList: Returning cached promise')
|
||||
return roofMaterialPromiseCache.get(cacheKey)
|
||||
}
|
||||
|
||||
// 2. 처음 호출될 때 Promise를 생성하여 변수에 할당
|
||||
roofMaterialPromise = get({ url: '/api/v1/master/getRoofMaterialList' })
|
||||
.then((res) => {
|
||||
return res;
|
||||
})
|
||||
.catch((error) => {
|
||||
// 에러 발생 시 다음 호출을 위해 초기화
|
||||
roofMaterialPromise = null;
|
||||
throw error;
|
||||
});
|
||||
console.log('🚀 getRoofMaterialList: Creating new promise')
|
||||
|
||||
return roofMaterialPromise;
|
||||
const MAX_RETRY = 15 // 15번으로 증가 (일본 지역 대응)
|
||||
const RETRY_DELAY = 2000 // 2초 간격
|
||||
const MAX_BACKOFF = 10000 // 최대 10초까지 지수 백오프
|
||||
|
||||
const fetchWithRetry = async () => {
|
||||
let lastError = null
|
||||
let retryCount = 0
|
||||
|
||||
for (let attempt = 0; attempt < MAX_RETRY; attempt++) {
|
||||
try {
|
||||
// 지수 백오프 적용: 2초, 4초, 8초, ... 최대 10초
|
||||
const backoffDelay = Math.min(RETRY_DELAY * Math.pow(2, attempt), MAX_BACKOFF)
|
||||
if (attempt > 0) {
|
||||
console.log(`⏳ Waiting ${backoffDelay/1000}s before retry ${attempt + 1}/${MAX_RETRY}`)
|
||||
await delay(backoffDelay)
|
||||
}
|
||||
|
||||
console.log(`🌐 API Request ${attempt + 1}/${MAX_RETRY}: /api/v1/master/getRoofMaterialList`)
|
||||
const res = await get({ url: '/api/v1/master/getRoofMaterialList' })
|
||||
console.log('✅ getRoofMaterialList: Success on attempt', attempt + 1)
|
||||
|
||||
// 재시도 중이었으면 성공 메시지 표시
|
||||
if (retryCount > 0) {
|
||||
swalFire({
|
||||
icon: 'success',
|
||||
title: getMessage('server.restored.title'),
|
||||
text: `${getMessage('server.restored.text')} (${retryCount}${getMessage('server.retries')})`,
|
||||
timer: 3000, // 메시지 표시 시간 증가
|
||||
showConfirmButton: false
|
||||
})
|
||||
}
|
||||
|
||||
return res
|
||||
} catch (error) {
|
||||
lastError = error
|
||||
retryCount++
|
||||
|
||||
// 500 계열만 기다렸다가 재시도
|
||||
const status = error?.response?.status
|
||||
if (status && status >= 500) {
|
||||
console.log(`⚠️ getRoofMaterialList: 500 error on attempt ${attempt + 1}, retrying...`)
|
||||
|
||||
// 첫 번째 500 에러에만 메시지 표시
|
||||
if (attempt === 0) {
|
||||
swalFire({
|
||||
icon: 'warning',
|
||||
title: getMessage('server.error.title'),
|
||||
text: getMessage('server.error.text'),
|
||||
timer: 4000, // 경고 메시지 시간 증가
|
||||
showConfirmButton: false
|
||||
})
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// 네트워크 에러(ECONNRESET, ETIMEDOUT 등)도 재시도
|
||||
if (error.code === 'ECONNRESET' || error.code === 'ETIMEDOUT' || error.code === 'ENOTFOUND') {
|
||||
console.log(`🌐 Network error on attempt ${attempt + 1}:`, error.code)
|
||||
if (attempt === 0) {
|
||||
swalFire({
|
||||
icon: 'warning',
|
||||
title: getMessage('network.error.title'),
|
||||
text: getMessage('network.error.text'),
|
||||
timer: 4000,
|
||||
showConfirmButton: false
|
||||
})
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// 500이나 네트워크 에러가 아니면 즉시 실패
|
||||
console.log(`❌ getRoofMaterialList: Non-retryable error on attempt ${attempt + 1}:`, error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// 모든 재시도 실패 시
|
||||
console.log('💥 getRoofMaterialList: All retries failed')
|
||||
swalFire({
|
||||
icon: 'error',
|
||||
title: getMessage('server.failed.title'),
|
||||
text: `${getMessage('server.failed.text')} (${MAX_RETRY}${getMessage('server.retries')})`,
|
||||
confirmButtonText: getMessage('common.ok')
|
||||
})
|
||||
|
||||
throw lastError
|
||||
}
|
||||
|
||||
const promise = fetchWithRetry()
|
||||
.finally(() => {
|
||||
// 성공/실패 모두 끝나면 캐시에서 제거
|
||||
console.log('🏁 getRoofMaterialList: Promise resolved, removing from cache')
|
||||
roofMaterialPromiseCache.delete(cacheKey)
|
||||
})
|
||||
|
||||
// 캐시에 Promise 저장
|
||||
roofMaterialPromiseCache.set(cacheKey, promise)
|
||||
return promise
|
||||
}
|
||||
|
||||
/**
|
||||
@ -263,6 +359,10 @@ export function useMasterController() {
|
||||
})
|
||||
}
|
||||
|
||||
const quotationErrSave = async (params) => {
|
||||
return await post({ url: '/api/v1/master/quotationErrSave', data: params })
|
||||
}
|
||||
|
||||
return {
|
||||
getRoofMaterialList,
|
||||
getModuleTypeItemList,
|
||||
@ -278,5 +378,6 @@ export function useMasterController() {
|
||||
updateObjectDate,
|
||||
getQuotationItem,
|
||||
getPcsConnOptionItemList,
|
||||
quotationErrSave,
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@ import {
|
||||
import { calculateVisibleModuleHeight, getDegreeByChon, polygonToTurfPolygon, rectToPolygon, toFixedWithoutRounding } from '@/util/canvas-util'
|
||||
import '@/util/fabric-extensions' // fabric 객체들에 getCurrentPoints 메서드 추가
|
||||
import { basicSettingState, roofDisplaySelector } from '@/store/settingAtom'
|
||||
import offsetPolygon, { calculateAngle, cleanSelfIntersectingPolygon, createLinesFromPolygon } from '@/util/qpolygon-utils'
|
||||
import offsetPolygon, { calculateAngle, cleanSelfIntersectingPolygon, clipOffsetToOriginal, createLinesFromPolygon } from '@/util/qpolygon-utils'
|
||||
import { QPolygon } from '@/components/fabric/QPolygon'
|
||||
import { useEvent } from '@/hooks/useEvent'
|
||||
import { BATCH_TYPE, LINE_TYPE, MODULE_SETUP_TYPE, POLYGON_TYPE } from '@/common/common'
|
||||
@ -56,7 +56,7 @@ export function useModuleBasicSetting(tabNum) {
|
||||
|
||||
const setCurrentObject = useSetRecoilState(currentObjectState)
|
||||
const { setModuleStatisticsData } = useCircuitTrestle()
|
||||
const { createRoofPolygon, createMarginPolygon, createPaddingPolygon } = useMode()
|
||||
const { createRoofPolygon, createMarginPolygon, createPaddingPolygon, removeShortEdges } = useMode()
|
||||
|
||||
const moduleSetupOption = useRecoilValue(moduleSetupOptionState)
|
||||
const setManualSetupMode = useSetRecoilState(toggleManualSetupModeState)
|
||||
@ -390,10 +390,14 @@ export function useModuleBasicSetting(tabNum) {
|
||||
setSurfaceShapePattern(roof, roofDisplay.column, true, roof.roofMaterial) //패턴 변경
|
||||
// let offsetPoints = createPaddingPolygon(createRoofPolygon(roof.points), roof.lines).vertices //안쪽 offset
|
||||
let offsetPoints = null
|
||||
const polygon = createRoofPolygon(roof.getCurrentPoints())
|
||||
|
||||
// 겹치는 선(같은 방향) 중 짧은 edge를 제거하여 폴리곤 단순화
|
||||
const cleaned = removeShortEdges(roof.getCurrentPoints(), roof.lines)
|
||||
const polygon = createRoofPolygon(cleaned.vertices)
|
||||
const cleanedLines = cleaned.lines
|
||||
const originPolygon = new QPolygon(roof.getCurrentPoints(), { fontSize: 0 })
|
||||
|
||||
let result = createPaddingPolygon(polygon, roof.lines).vertices
|
||||
let result = createPaddingPolygon(polygon, cleanedLines, 0, true).vertices
|
||||
|
||||
//margin polygon 의 point가 기준 polygon의 밖에 있는지 판단한다.
|
||||
const allPointsOutside = result.every((point) => !originPolygon.inPolygon(point))
|
||||
@ -406,12 +410,14 @@ export function useModuleBasicSetting(tabNum) {
|
||||
} else {
|
||||
//육지붕이 아닐때
|
||||
if (allPointsOutside) {
|
||||
offsetPoints = createMarginPolygon(polygon, roof.lines).vertices
|
||||
offsetPoints = createMarginPolygon(polygon, cleanedLines, 0, true).vertices
|
||||
} else {
|
||||
offsetPoints = createPaddingPolygon(polygon, roof.lines).vertices
|
||||
offsetPoints = createPaddingPolygon(polygon, cleanedLines, 0, true).vertices
|
||||
}
|
||||
// 자기교차(꼬임) 제거
|
||||
offsetPoints = cleanSelfIntersectingPolygon(offsetPoints)
|
||||
// 둔각 꼭짓점에서 offset 선이 지붕 바깥으로 튀어나가는 문제 수정
|
||||
offsetPoints = clipOffsetToOriginal(offsetPoints, roof.getCurrentPoints())
|
||||
}
|
||||
|
||||
//모듈설치영역?? 생성
|
||||
|
||||
@ -310,7 +310,7 @@ export function useModuleTabContents({ tabIndex, addRoof, setAddedRoofs, roofTab
|
||||
} else {
|
||||
// 최초 선택: 설치 가능하면 디폴트 체크
|
||||
const defaultCvr = selectedConstruction.cvrYn === 'Y'
|
||||
const defaultSnowGd = selectedConstruction.snowGdPossYn === 'Y'
|
||||
const defaultSnowGd = false
|
||||
selectedConstruction.setupCover = defaultCvr
|
||||
selectedConstruction.setupSnowCover = defaultSnowGd
|
||||
setCvrChecked(defaultCvr)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { canvasState, currentAngleTypeSelector } from '@/store/canvasAtom'
|
||||
import { canvasState, currentAngleTypeSelector, currentCanvasPlanState } from '@/store/canvasAtom'
|
||||
import { POLYGON_TYPE, TRESTLE_MATERIAL } from '@/common/common'
|
||||
import { moduleSelectionDataState } from '@/store/selectedModuleOptions'
|
||||
import { getDegreeByChon } from '@/util/canvas-util'
|
||||
@ -11,6 +11,7 @@ import { useContext } from 'react'
|
||||
import { QcastContext } from '@/app/QcastProvider'
|
||||
import { useCircuitTrestle } from '@/hooks/useCirCuitTrestle'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { loginUserStore } from '@/store/commonAtom'
|
||||
|
||||
// 모듈간 같은 행, 열의 마진이 10 이하인 경우는 같은 행, 열로 간주
|
||||
const MODULE_MARGIN = 10
|
||||
@ -18,7 +19,9 @@ const MODULE_MARGIN = 10
|
||||
export const useTrestle = () => {
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
const moduleSelectionData = useRecoilValue(moduleSelectionDataState) //다음으로 넘어가는 최종 데이터
|
||||
const { getQuotationItem } = useMasterController()
|
||||
const { getQuotationItem, quotationErrSave } = useMasterController()
|
||||
const currentCanvasPlan = useRecoilValue(currentCanvasPlanState)
|
||||
const loginUserState = useRecoilValue(loginUserStore)
|
||||
const currentAngleType = useRecoilValue(currentAngleTypeSelector)
|
||||
const roofSizeSet = useRecoilValue(basicSettingState).roofSizeSet
|
||||
const isTrestleDisplay = useRecoilValue(trestleDisplaySelector)
|
||||
@ -757,6 +760,12 @@ export const useTrestle = () => {
|
||||
text: result.resultMsg || getMessage('common.message.noData'),
|
||||
icon: 'error'
|
||||
})
|
||||
quotationErrSave({
|
||||
objectNo: currentCanvasPlan.objectNo,
|
||||
planNo: currentCanvasPlan.planNo,
|
||||
rejectRsn: result.resultMsg,
|
||||
regId: loginUserState.userId,
|
||||
})
|
||||
clear()
|
||||
setViewCircuitNumberTexts(true)
|
||||
setIsGlobalLoading(false)
|
||||
|
||||
@ -153,12 +153,12 @@ export function useCanvasSetting(executeEffect = true) {
|
||||
return
|
||||
}
|
||||
|
||||
/** 초 1회만 실행하도록 처리 */
|
||||
addRoofMaterials()
|
||||
// Roof materials are now initialized in Header.jsx to avoid duplicate calls
|
||||
}, [executeEffect])
|
||||
|
||||
/**
|
||||
* 지붕재 초기세팅
|
||||
* 지붕재 초기세팅 (fallback - primary initialization is in Header.jsx)
|
||||
* This function is only called when roof materials are needed but not yet loaded
|
||||
*/
|
||||
const addRoofMaterials = async () => {
|
||||
if (roofMaterials.length !== 0) {
|
||||
@ -320,12 +320,26 @@ export function useCanvasSetting(executeEffect = true) {
|
||||
// objectNo가 전달되면 사용, 아니면 Recoil state의 correntObjectNo 사용
|
||||
const targetObjectNo = objectNo || correntObjectNo
|
||||
|
||||
// 지붕재 데이터가 없으면 먼저 로드
|
||||
// 지붕재 데이터가 없으면 Header.jsx에서 로드될 때까지 기다림
|
||||
let materials = roofMaterials
|
||||
if (!materials || materials.length === 0) {
|
||||
logger.log('Waiting for roofMaterials to be loaded...')
|
||||
materials = await addRoofMaterials()
|
||||
logger.log('roofMaterials loaded:', materials)
|
||||
logger.log('Waiting for roofMaterials to be loaded from Header.jsx...')
|
||||
// 최대 2초간 기다림 (20번 × 100ms)
|
||||
let waitCount = 0
|
||||
while ((!materials || materials.length === 0) && waitCount < 20) {
|
||||
await new Promise(resolve => setTimeout(resolve, 100))
|
||||
materials = roofMaterials
|
||||
waitCount++
|
||||
}
|
||||
|
||||
if (!materials || materials.length === 0) {
|
||||
logger.log('roofMaterials still not loaded after 2 seconds, proceeding without them')
|
||||
// 비상시에만 addRoofMaterials 호출 (fallback)
|
||||
materials = await addRoofMaterials()
|
||||
logger.log('roofMaterials loaded via fallback:', materials)
|
||||
} else {
|
||||
logger.log('roofMaterials loaded from Header.jsx:', materials)
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@ -447,15 +447,30 @@ export function useRoofAllocationSetting(id) {
|
||||
const wallLines = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL)
|
||||
roofBases.forEach((roofBase) => {
|
||||
try {
|
||||
const roofEaveHelpLines = canvas.getObjects().filter((obj) => obj.lineName === 'eaveHelpLine' && obj.roofId === roofBase.id)
|
||||
// 지붕 할당 로직에 extensionLine 추가
|
||||
const roofEaveHelpLines = canvas.getObjects().filter((obj) =>
|
||||
(obj.lineName === 'eaveHelpLine' || obj.lineName === 'extensionLine') &&
|
||||
obj.roofId === roofBase.id
|
||||
)
|
||||
// console.log('roofBase.id:', roofBase.id)
|
||||
// console.log('roofEaveHelpLines:', roofEaveHelpLines)
|
||||
// console.log('extensionLines found:', roofEaveHelpLines.filter(l => l.lineName === 'extensionLine'))
|
||||
if (roofEaveHelpLines.length > 0) {
|
||||
if (roofBase.lines) {
|
||||
// Filter out any eaveHelpLines that are already in lines to avoid duplicates
|
||||
const existingEaveLineIds = new Set(roofBase.lines.map((line) => line.id))
|
||||
const newEaveLines = roofEaveHelpLines.filter((line) => !existingEaveLineIds.has(line.id))
|
||||
// Filter out lines from roofBase.lines that share any points with newEaveLines
|
||||
|
||||
// extensionLine과 일반 eaveHelpLine 분리
|
||||
const extensionLines = newEaveLines.filter(line => line.lineName === 'extensionLine')
|
||||
const normalEaveLines = newEaveLines.filter(line => line.lineName === 'eaveHelpLine')
|
||||
// console.log('extensionLines count:', extensionLines.length)
|
||||
// console.log('normalEaveLines count:', normalEaveLines.length)
|
||||
|
||||
// 일반 eaveHelpLine만 Overlap 판단에 사용
|
||||
const linesToKeep = roofBase.lines.filter(roofLine => {
|
||||
const shouldRemove = newEaveLines.some(eaveLine => {
|
||||
const shouldRemove = normalEaveLines.some(eaveLine => {
|
||||
// 1. 기본적인 포인트 일치 확인
|
||||
const rX1 = roofLine.x1, rY1 = roofLine.y1, rX2 = roofLine.x2, rY2 = roofLine.y2;
|
||||
const eX1 = eaveLine.x1, eY1 = eaveLine.y1, eX2 = eaveLine.x2, eY2 = eaveLine.y2;
|
||||
@ -504,7 +519,9 @@ export function useRoofAllocationSetting(id) {
|
||||
return !shouldRemove;
|
||||
});
|
||||
// Combine remaining lines with newEaveLines
|
||||
roofBase.lines = [...linesToKeep, ...newEaveLines];
|
||||
roofBase.lines = [...linesToKeep, ...normalEaveLines, ...extensionLines];
|
||||
// console.log('Final roofBase.lines count:', roofBase.lines.length)
|
||||
// console.log('extensionLines in final:', roofBase.lines.filter(l => l.lineName === 'extensionLine').length)
|
||||
} else {
|
||||
roofBase.lines = [...roofEaveHelpLines]
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ import { useEffect, useRef } from 'react'
|
||||
import { fabric } from 'fabric'
|
||||
import { calculateAngle } from '@/util/qpolygon-utils'
|
||||
import {
|
||||
OUTER_LINE_TYPE,
|
||||
placementShapeDrawingAngle1State,
|
||||
placementShapeDrawingAngle2State,
|
||||
placementShapeDrawingArrow1State,
|
||||
@ -92,6 +93,7 @@ export function usePlacementShapeDrawing(id) {
|
||||
|
||||
useEffect(() => {
|
||||
setPoints([])
|
||||
setType(OUTER_LINE_TYPE.OUTER_LINE)
|
||||
|
||||
return () => {
|
||||
const placementShapeDrawingStartPoint = canvas.getObjects().find((obj) => obj.name === 'placementShapeDrawingStartPoint')
|
||||
|
||||
@ -86,15 +86,21 @@ export function useAxios(lang = '') {
|
||||
.get(url, option)
|
||||
.then((res) => res.data)
|
||||
.catch((error) => {
|
||||
console.error('🚨 GET Error:', {
|
||||
url,
|
||||
method: 'GET',
|
||||
timestamp: new Date().toISOString(),
|
||||
errorMessage: error.message,
|
||||
status: error.response?.status,
|
||||
statusText: error.response?.statusText,
|
||||
config: error.config
|
||||
})
|
||||
// 500 에러는 재시도되므로 로깅을 줄임
|
||||
const status = error?.response?.status
|
||||
if (status === 500) {
|
||||
console.warn('🔄 Server Error (500): Will retry...', url)
|
||||
} else {
|
||||
console.error('🚨 GET Error:', {
|
||||
url,
|
||||
method: 'GET',
|
||||
timestamp: new Date().toISOString(),
|
||||
errorMessage: error.message,
|
||||
status: error.response?.status,
|
||||
statusText: error.response?.statusText,
|
||||
config: error.config
|
||||
})
|
||||
}
|
||||
throw error
|
||||
})
|
||||
}
|
||||
|
||||
@ -16,6 +16,7 @@ export function useCanvasEvent() {
|
||||
const [currentObject, setCurrentObject] = useRecoilState(currentObjectState)
|
||||
const canvasSize = useRecoilValue(canvasSizeState)
|
||||
const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState)
|
||||
const [shouldCenter, setShouldCenter] = useState(false) // 중앙 정렬 플래그 추가
|
||||
const lengthTextOption = useRecoilValue(fontSelector('lengthText'))
|
||||
const currentMenu = useRecoilValue(currentMenuState)
|
||||
const { changeCorridorDimensionText } = useText()
|
||||
@ -25,6 +26,17 @@ export function useCanvasEvent() {
|
||||
useEffect(() => {
|
||||
canvas?.setZoom(canvasZoom / 100)
|
||||
}, [canvasZoom])
|
||||
|
||||
// canvasZoom이 100으로 변경되고 shouldCenter가 true일 때만 중앙 정렬 실행
|
||||
useEffect(() => {
|
||||
if (canvasZoom === 100 && canvas && shouldCenter) {
|
||||
setTimeout(() => {
|
||||
zoomToAllObjects()
|
||||
canvas.renderAll()
|
||||
setShouldCenter(false) // 실행 후 플래그 초기화
|
||||
}, 100)
|
||||
}
|
||||
}, [canvasZoom, shouldCenter])
|
||||
|
||||
useEffect(() => {
|
||||
attachDefaultEventOnCanvas()
|
||||
@ -433,15 +445,35 @@ export function useCanvasEvent() {
|
||||
}
|
||||
|
||||
const handleZoomClear = () => {
|
||||
setShouldCenter(true) // 중앙 정렬 플래그 설정
|
||||
setCanvasZoom(100)
|
||||
|
||||
zoomToAllObjects()
|
||||
canvas.renderAll()
|
||||
}
|
||||
|
||||
const zoomToAllObjects = () => {
|
||||
const objects = canvas.getObjects().filter((obj) => obj.visible)
|
||||
if (objects.length === 0) return
|
||||
const allObjects = canvas.getObjects()
|
||||
// 주요 도형 객체만 필터링 (roof, arrow 등)
|
||||
const objects = allObjects.filter((obj) => {
|
||||
// 주요 도형 객체만 포함
|
||||
return obj.name === 'roof' ||
|
||||
obj.name === 'arrow' ||
|
||||
obj.name === 'module' ||
|
||||
obj.name === 'dormer' ||
|
||||
obj.name === 'object'
|
||||
})
|
||||
|
||||
console.log('🔍 All objects:', allObjects.length)
|
||||
console.log('🔍 Main objects for centering:', objects.length)
|
||||
console.log('🔍 Main object details:', objects.map(obj => ({
|
||||
name: obj.name,
|
||||
type: obj.type,
|
||||
visible: obj.visible,
|
||||
id: obj.id
|
||||
})))
|
||||
|
||||
if (objects.length === 0) {
|
||||
console.log('❌ No main objects found for centering')
|
||||
return
|
||||
}
|
||||
|
||||
let minX = Infinity,
|
||||
minY = Infinity
|
||||
@ -456,11 +488,52 @@ export function useCanvasEvent() {
|
||||
maxY = Math.max(maxY, bounds.top + bounds.height)
|
||||
})
|
||||
|
||||
const centerX = (minX + maxX) / 2
|
||||
const centerY = (minY + maxY) / 2
|
||||
const centerPoint = new fabric.Point(centerX, centerY)
|
||||
|
||||
canvas.zoomToPoint(centerPoint, 1)
|
||||
const objectsCenterX = (minX + maxX) / 2
|
||||
const objectsCenterY = (minY + maxY) / 2
|
||||
|
||||
// 화면 중앙 좌표 (메뉴 높이 고려하여 Y 위치 조정)
|
||||
const screenCenterX = canvas.getWidth() / 2
|
||||
const menuHeightOffset = 80 // 위쪽 메뉴 높이만큼 위로 조정
|
||||
const screenCenterY = canvas.getHeight() / 2 - menuHeightOffset
|
||||
|
||||
// 객체 중심을 화면 중앙으로 이동
|
||||
const deltaX = screenCenterX - objectsCenterX
|
||||
const deltaY = screenCenterY - objectsCenterY
|
||||
|
||||
// canvas viewport transform 조정
|
||||
const viewportTransform = canvas.viewportTransform
|
||||
viewportTransform[4] += deltaX
|
||||
viewportTransform[5] += deltaY
|
||||
|
||||
canvas.setViewportTransform(viewportTransform)
|
||||
|
||||
// 브라우저 스크롤을 중앙으로 조정하여 전체 canvas를 모니터 중앙에 위치
|
||||
const canvasElement = canvas.getElement()
|
||||
const canvasRect = canvasElement.getBoundingClientRect()
|
||||
|
||||
// 현재 스크롤 위치
|
||||
const currentScrollTop = window.pageYOffset || document.documentElement.scrollTop
|
||||
const currentScrollLeft = window.pageXOffset || document.documentElement.scrollLeft
|
||||
|
||||
// canvas의 현재 화면 위치
|
||||
const canvasScreenTop = canvasRect.top + currentScrollTop
|
||||
const canvasScreenLeft = canvasRect.left + currentScrollLeft
|
||||
|
||||
// 모니터 중앙
|
||||
const monitorCenterX = window.innerWidth / 2
|
||||
const monitorCenterY = window.innerHeight / 2
|
||||
|
||||
// canvas가 모니터 중앙에 오도록 스크롤 위치 계산
|
||||
const targetScrollLeft = canvasScreenLeft + (canvasRect.width / 2) - monitorCenterX
|
||||
const targetScrollTop = canvasScreenTop + (canvasRect.height / 2) - monitorCenterY + menuHeightOffset
|
||||
|
||||
// 스크롤을 중앙으로 이동
|
||||
window.scrollTo({
|
||||
top: targetScrollTop,
|
||||
left: targetScrollLeft,
|
||||
behavior: 'smooth' // 부드러운 스크롤
|
||||
})
|
||||
|
||||
canvas.renderAll()
|
||||
}
|
||||
|
||||
|
||||
@ -1675,14 +1675,81 @@ export function useMode() {
|
||||
* @param arcSegments
|
||||
* @returns {{vertices, edges: *[], minX: *, minY: *, maxX: *, maxY: *}}
|
||||
*/
|
||||
function createMarginPolygon(polygon, lines, arcSegments = 0) {
|
||||
/**
|
||||
* 같은 방향(각도)으로 겹치는 edge들을 감지하여, 가장 긴 선만 유지한다.
|
||||
* 겹치는 짧은 edge의 꼭짓점을 제거하고 폴리곤을 단순화한다.
|
||||
* @param {Array} vertices - 폴리곤 꼭짓점 배열
|
||||
* @param {Array} lines - 대응하는 QLine 배열
|
||||
* @returns {{ vertices: Array, lines: Array }} 정리된 꼭짓점과 lines
|
||||
*/
|
||||
function removeShortEdges(vertices, lines) {
|
||||
if (!vertices || vertices.length < 4) return { vertices, lines }
|
||||
|
||||
// 각 edge의 각도와 길이를 계산
|
||||
function edgeAngle(v1, v2) {
|
||||
const rad = Math.atan2(v2.y - v1.y, v2.x - v1.x)
|
||||
return Math.round(rad * (180 / Math.PI))
|
||||
}
|
||||
function edgeLength(v1, v2) {
|
||||
const dx = v2.x - v1.x
|
||||
const dy = v2.y - v1.y
|
||||
return Math.sqrt(dx * dx + dy * dy)
|
||||
}
|
||||
|
||||
// 같은 방향인지 판단 (동일 각도 또는 정반대 180도)
|
||||
function isSameDirection(angle1, angle2) {
|
||||
const diff = Math.abs(angle1 - angle2) % 360
|
||||
return diff <= 1 || Math.abs(diff - 180) <= 1
|
||||
}
|
||||
|
||||
// 연속된 같은 방향 edge를 그룹으로 묶기
|
||||
const n = vertices.length
|
||||
const edges = []
|
||||
for (let i = 0; i < n; i++) {
|
||||
const next = (i + 1) % n
|
||||
edges.push({
|
||||
index: i,
|
||||
angle: edgeAngle(vertices[i], vertices[next]),
|
||||
length: edgeLength(vertices[i], vertices[next]),
|
||||
})
|
||||
}
|
||||
|
||||
const removeIndices = new Set()
|
||||
|
||||
for (let i = 0; i < n; i++) {
|
||||
const next = (i + 1) % n
|
||||
// 인접한 두 edge가 같은 방향이면 겹치는 선
|
||||
if (isSameDirection(edges[i].angle, edges[next].angle)) {
|
||||
// 짧은 쪽을 제거 대상으로 표시
|
||||
if (edges[i].length <= edges[next].length) {
|
||||
removeIndices.add(i)
|
||||
} else {
|
||||
removeIndices.add(next)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (removeIndices.size === 0) return { vertices, lines }
|
||||
|
||||
// 폴리곤이 3개 미만이 되지 않도록 보호
|
||||
if (n - removeIndices.size < 3) return { vertices, lines }
|
||||
|
||||
const cleanedVertices = []
|
||||
const cleanedLines = []
|
||||
|
||||
for (let i = 0; i < n; i++) {
|
||||
if (removeIndices.has(i)) continue
|
||||
cleanedVertices.push(vertices[i])
|
||||
cleanedLines.push(lines[i % lines.length])
|
||||
}
|
||||
|
||||
return { vertices: cleanedVertices, lines: cleanedLines }
|
||||
}
|
||||
|
||||
function createMarginPolygon(polygon, lines, arcSegments = 0, bevelJoin = false) {
|
||||
const offsetEdges = []
|
||||
|
||||
polygon.edges.forEach((edge, i) => {
|
||||
/* const offset =
|
||||
lines[i % lines.length].attributes.offset === undefined || lines[i % lines.length].attributes.offset === 0
|
||||
? 0.1
|
||||
: lines[i % lines.length].attributes.offset*/
|
||||
const offset = lines[i % lines.length].attributes.offset
|
||||
const dx = edge.outwardNormal.x * offset
|
||||
const dy = edge.outwardNormal.y * offset
|
||||
@ -1694,11 +1761,14 @@ export function useMode() {
|
||||
offsetEdges.forEach((thisEdge, i) => {
|
||||
const prevEdge = offsetEdges[(i + offsetEdges.length - 1) % offsetEdges.length]
|
||||
const vertex = edgesIntersection(prevEdge, thisEdge)
|
||||
if (vertex && (!vertex.isIntersectionOutside || arcSegments < 1)) {
|
||||
vertices.push({
|
||||
x: vertex.x,
|
||||
y: vertex.y,
|
||||
})
|
||||
if (vertex) {
|
||||
if (!vertex.isIntersectionOutside || !bevelJoin) {
|
||||
vertices.push({ x: vertex.x, y: vertex.y })
|
||||
} else {
|
||||
// 둔각 + bevelJoin: offset edge 끝점 사용
|
||||
vertices.push({ x: prevEdge.vertex2.x, y: prevEdge.vertex2.y })
|
||||
vertices.push({ x: thisEdge.vertex1.x, y: thisEdge.vertex1.y })
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@ -1714,14 +1784,10 @@ export function useMode() {
|
||||
* @param arcSegments
|
||||
* @returns {{vertices, edges: *[], minX: *, minY: *, maxX: *, maxY: *}}
|
||||
*/
|
||||
function createPaddingPolygon(polygon, lines, arcSegments = 0) {
|
||||
function createPaddingPolygon(polygon, lines, arcSegments = 0, bevelJoin = false) {
|
||||
const offsetEdges = []
|
||||
|
||||
polygon.edges.forEach((edge, i) => {
|
||||
/*const offset =
|
||||
lines[i % lines.length].attributes.offset === undefined || lines[i % lines.length].attributes.offset === 0
|
||||
? 0.1
|
||||
: lines[i % lines.length].attributes.offset*/
|
||||
const offset = lines[i % lines.length].attributes.offset
|
||||
|
||||
const dx = edge.inwardNormal.x * offset
|
||||
@ -1734,11 +1800,14 @@ export function useMode() {
|
||||
offsetEdges.forEach((thisEdge, i) => {
|
||||
const prevEdge = offsetEdges[(i + offsetEdges.length - 1) % offsetEdges.length]
|
||||
const vertex = edgesIntersection(prevEdge, thisEdge)
|
||||
if (vertex && (!vertex.isIntersectionOutside || arcSegments < 1)) {
|
||||
vertices.push({
|
||||
x: vertex.x,
|
||||
y: vertex.y,
|
||||
})
|
||||
if (vertex) {
|
||||
if (!vertex.isIntersectionOutside || !bevelJoin) {
|
||||
vertices.push({ x: vertex.x, y: vertex.y })
|
||||
} else {
|
||||
// 둔각 + bevelJoin: offset edge 끝점 사용
|
||||
vertices.push({ x: prevEdge.vertex2.x, y: prevEdge.vertex2.y })
|
||||
vertices.push({ x: thisEdge.vertex1.x, y: thisEdge.vertex1.y })
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@ -5774,5 +5843,6 @@ export function useMode() {
|
||||
createRoofPolygon,
|
||||
createMarginPolygon,
|
||||
createPaddingPolygon,
|
||||
removeShortEdges,
|
||||
}
|
||||
}
|
||||
|
||||
@ -333,6 +333,7 @@ export const usePolygon = () => {
|
||||
moduleCompass: polygon.moduleCompass,
|
||||
visible: isFlowDisplay,
|
||||
pitch: polygon.roofMaterial?.pitch ?? 4,
|
||||
roofAngle: polygon.roofMaterial?.angle,
|
||||
parentId: polygon.id,
|
||||
})
|
||||
arrow.setViewLengthText(false)
|
||||
@ -348,7 +349,7 @@ export const usePolygon = () => {
|
||||
const drawDirectionStringToArrow2 = (polygon, showDirectionText) => {
|
||||
let { direction, surfaceCompass, moduleCompass, arrow } = polygon
|
||||
if (moduleCompass === null || moduleCompass === undefined) {
|
||||
const textObj = new fabric.Text(`${currentAngleType === ANGLE_TYPE.SLOPE ? arrow.pitch : getDegreeByChon(arrow.pitch)}${pitchText}`, {
|
||||
const textObj = new fabric.Text(`${currentAngleType === ANGLE_TYPE.SLOPE ? arrow.pitch : (arrow.roofAngle ?? getDegreeByChon(arrow.pitch))}${pitchText}`, {
|
||||
fontFamily: flowFontOptions.fontFamily.value,
|
||||
fontWeight: flowFontOptions.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
|
||||
fontStyle: flowFontOptions.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
|
||||
@ -357,6 +358,7 @@ export const usePolygon = () => {
|
||||
originX: 'center',
|
||||
originY: 'center',
|
||||
pitch: arrow.pitch,
|
||||
roofAngle: arrow.roofAngle,
|
||||
name: 'flowText',
|
||||
selectable: false,
|
||||
left: arrow.stickeyPoint.x,
|
||||
@ -490,7 +492,7 @@ export const usePolygon = () => {
|
||||
text = text + (sameDirectionCnt.length + 1)
|
||||
polygon.set('directionText', text)
|
||||
const textObj = new fabric.Text(
|
||||
`${showDirectionText && text} (${currentAngleType === ANGLE_TYPE.SLOPE ? arrow.pitch : getDegreeByChon(arrow.pitch)}${pitchText})`,
|
||||
`${showDirectionText && text} (${currentAngleType === ANGLE_TYPE.SLOPE ? arrow.pitch : (arrow.roofAngle ?? getDegreeByChon(arrow.pitch))}${pitchText})`,
|
||||
{
|
||||
fontFamily: flowFontOptions.fontFamily.value,
|
||||
fontWeight: flowFontOptions.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
|
||||
@ -498,6 +500,7 @@ export const usePolygon = () => {
|
||||
fontSize: flowFontOptions.fontSize.value,
|
||||
fill: flowFontOptions.fontColor.value,
|
||||
pitch: arrow.pitch,
|
||||
roofAngle: arrow.roofAngle,
|
||||
originX: 'center',
|
||||
originY: 'center',
|
||||
name: 'flowText',
|
||||
@ -707,7 +710,7 @@ export const usePolygon = () => {
|
||||
const addTextByArrows = (arrows, txt, canvas) => {
|
||||
arrows.forEach((arrow, index) => {
|
||||
// const textStr = `${txt}${index + 1} (${currentAngleType === ANGLE_TYPE.SLOPE ? arrow.pitch : getDegreeByChon(arrow.pitch)}${pitchText})`
|
||||
const textStr = `${txt} (${currentAngleType === ANGLE_TYPE.SLOPE ? arrow.pitch : getDegreeByChon(arrow.pitch)}${pitchText})`
|
||||
const textStr = `${txt} (${currentAngleType === ANGLE_TYPE.SLOPE ? arrow.pitch : (arrow.roofAngle ?? getDegreeByChon(arrow.pitch))}${pitchText})`
|
||||
|
||||
const text = new fabric.Text(`${textStr}`, {
|
||||
fontFamily: flowFontOptions.fontFamily.value,
|
||||
@ -716,6 +719,7 @@ export const usePolygon = () => {
|
||||
fontSize: flowFontOptions.fontSize.value,
|
||||
fill: flowFontOptions.fontColor.value,
|
||||
pitch: arrow.pitch,
|
||||
roofAngle: arrow.roofAngle,
|
||||
originX: 'center',
|
||||
originY: 'center',
|
||||
name: 'flowText',
|
||||
@ -821,7 +825,7 @@ export const usePolygon = () => {
|
||||
if (innerLine.attributes && polygonLine.attributes.type) {
|
||||
// innerLine이 polygonLine보다 긴 경우 polygonLine.need를 false로 변경
|
||||
if (polygonLine.length < innerLine.length) {
|
||||
if (polygonLine.lineName !== 'eaveHelpLine' || polygonLine.lineName !== 'eaveHelpLine') {
|
||||
if (polygonLine.lineName !== 'eaveHelpLine' && polygonLine.lineName !== 'extensionLine') {
|
||||
polygonLine.need = false
|
||||
}
|
||||
}
|
||||
|
||||
@ -567,6 +567,15 @@
|
||||
"common.finish": "完了",
|
||||
"common.ok": "確認",
|
||||
"common.cancel": "キャンセル",
|
||||
"server.error.title": "サーバーエラー",
|
||||
"server.error.text": "サーバーで一時的な問題が発生しました。自動的に再試行します...",
|
||||
"server.restored.title": "サーバー接続復旧",
|
||||
"server.restored.text": "サーバー接続が復旧しました",
|
||||
"server.failed.title": "サーバー接続失敗",
|
||||
"server.failed.text": "サーバー接続に失敗しました。しばらくしてから再度お試しください",
|
||||
"server.retries": "回再試行",
|
||||
"network.error.title": "ネットワーク接続不安定",
|
||||
"network.error.text": "インターネット接続が不安定です。自動的に再試行します...",
|
||||
"commons.west": "西",
|
||||
"commons.east": "東",
|
||||
"commons.south": "南",
|
||||
|
||||
@ -567,6 +567,15 @@
|
||||
"common.finish": "완료",
|
||||
"common.ok": "확인",
|
||||
"common.cancel": "취소",
|
||||
"server.error.title": "서버 오류",
|
||||
"server.error.text": "서버에 일시적인 문제가 발생했습니다. 자동으로 재시도합니다...",
|
||||
"server.restored.title": "서버 연결 복구",
|
||||
"server.restored.text": "서버 연결이 복구되었습니다",
|
||||
"server.failed.title": "서버 연결 실패",
|
||||
"server.failed.text": "서버 연결에 실패했습니다. 잠시 후 다시 시도해주세요",
|
||||
"server.retries": "회 재시도",
|
||||
"network.error.title": "네트워크 연결 불안",
|
||||
"network.error.text": "인터넷 연결이 불안정합니다. 자동으로 재시도합니다...",
|
||||
"commons.west": "서",
|
||||
"commons.east": "동",
|
||||
"commons.south": "남",
|
||||
|
||||
@ -255,6 +255,62 @@ function createPaddingPolygon(polygon, offset, arcSegments = 0) {
|
||||
* @param {Array} vertices - [{x, y}, ...] 형태의 점 배열
|
||||
* @returns {Array} 정리된 점 배열
|
||||
*/
|
||||
/**
|
||||
* offset 폴리곤의 꼭짓점 중 원본 폴리곤 바깥에 나간 점을
|
||||
* 원본 폴리곤 경계 위의 가장 가까운 점으로 투영한다.
|
||||
* @param {Array} offsetVertices - offset된 점 배열 [{x, y}, ...]
|
||||
* @param {Array} originalVertices - 원본 폴리곤 점 배열 [{x, y}, ...]
|
||||
* @returns {Array} 클리핑된 점 배열
|
||||
*/
|
||||
export function clipOffsetToOriginal(offsetVertices, originalVertices) {
|
||||
if (!offsetVertices || offsetVertices.length < 3) return offsetVertices
|
||||
if (!originalVertices || originalVertices.length < 3) return offsetVertices
|
||||
|
||||
// 점이 폴리곤 내부에 있는지 판단 (ray casting)
|
||||
function pointInPolygon(point, polygon) {
|
||||
let inside = false
|
||||
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
|
||||
const xi = polygon[i].x, yi = polygon[i].y
|
||||
const xj = polygon[j].x, yj = polygon[j].y
|
||||
if (((yi > point.y) !== (yj > point.y)) &&
|
||||
(point.x < (xj - xi) * (point.y - yi) / (yj - yi) + xi)) {
|
||||
inside = !inside
|
||||
}
|
||||
}
|
||||
return inside
|
||||
}
|
||||
|
||||
// 점을 선분 위의 가장 가까운 점으로 투영
|
||||
function projectOnSegment(p, a, b) {
|
||||
const dx = b.x - a.x
|
||||
const dy = b.y - a.y
|
||||
const lenSq = dx * dx + dy * dy
|
||||
if (lenSq === 0) return { x: a.x, y: a.y }
|
||||
let t = ((p.x - a.x) * dx + (p.y - a.y) * dy) / lenSq
|
||||
t = Math.max(0, Math.min(1, t))
|
||||
return { x: a.x + t * dx, y: a.y + t * dy }
|
||||
}
|
||||
|
||||
return offsetVertices.map((p) => {
|
||||
if (pointInPolygon(p, originalVertices)) return p
|
||||
|
||||
// 바깥에 있으면 원본 폴리곤의 가장 가까운 변 위의 점으로 이동
|
||||
let minDist = Infinity
|
||||
let nearest = p
|
||||
for (let i = 0; i < originalVertices.length; i++) {
|
||||
const a = originalVertices[i]
|
||||
const b = originalVertices[(i + 1) % originalVertices.length]
|
||||
const proj = projectOnSegment(p, a, b)
|
||||
const dist = (proj.x - p.x) * (proj.x - p.x) + (proj.y - p.y) * (proj.y - p.y)
|
||||
if (dist < minDist) {
|
||||
minDist = dist
|
||||
nearest = proj
|
||||
}
|
||||
}
|
||||
return nearest
|
||||
})
|
||||
}
|
||||
|
||||
export function cleanSelfIntersectingPolygon(vertices) {
|
||||
if (!vertices || vertices.length < 3) return vertices
|
||||
|
||||
|
||||
@ -463,8 +463,39 @@ export const skeletonBuilder = (roofId, canvas, textMode) => {
|
||||
})
|
||||
|
||||
// 6. 마루이동(용마루 위치 수동 조정)이 설정된 경우 movingLineFromSkeleton 결과로 대체
|
||||
|
||||
if (moveFlowLine !== 0 || moveUpDown !== 0) {
|
||||
const movedPoints = movingLineFromSkeleton(roofId, canvas)
|
||||
console.log("movedPoints:::", movedPoints);
|
||||
|
||||
// movedPoints를 roof 경계로 확장
|
||||
// 이번 이동에서 실제로 변경된 포인트만 구분하여, 변경되지 않은 축만 roof 경계로 확장
|
||||
const oldPoints = canvas?.skeleton?.lastPoints ?? roofLinePoints
|
||||
const tolerance = 0.1
|
||||
const correctedPoints = movedPoints.map((mp, i) => {
|
||||
const rp = roofLinePoints[i]
|
||||
const op = oldPoints[i]
|
||||
if (!rp || !op) return mp
|
||||
const xMatch = Math.abs(mp.x - rp.x) < tolerance
|
||||
const yMatch = Math.abs(mp.y - rp.y) < tolerance
|
||||
if (xMatch && yMatch) return mp
|
||||
// 이번 이동에서 실제로 변경된 축 판별 (oldPoints vs movedPoints)
|
||||
const xMoved = Math.abs(mp.x - op.x) >= tolerance
|
||||
const yMoved = Math.abs(mp.y - op.y) >= tolerance
|
||||
let newX = mp.x
|
||||
let newY = mp.y
|
||||
// 이번에 이동되지 않았는데 roof 경계와 다른 축만 교정
|
||||
if (!xMoved && !xMatch) newX = rp.x
|
||||
if (!yMoved && !yMatch) newY = rp.y
|
||||
if (newX !== mp.x || newY !== mp.y) {
|
||||
console.log(`point[${i}] 교정: mp=(${mp.x}, ${mp.y}) → (${newX}, ${newY}), op=(${op.x}, ${op.y}), xMoved=${xMoved}, yMoved=${yMoved}`)
|
||||
}
|
||||
return { x: newX, y: newY }
|
||||
})
|
||||
|
||||
// roofLineContactPoints = correctedPoints
|
||||
// changRoofLinePoints = correctedPoints
|
||||
|
||||
roofLineContactPoints = movedPoints
|
||||
changRoofLinePoints = movedPoints
|
||||
}
|
||||
@ -828,11 +859,11 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
||||
|
||||
//roofline 외곽선 설정
|
||||
|
||||
console.log('index::::', index)
|
||||
console.log('roofLine:', roofLine.x1, roofLine.y1, roofLine.x2, roofLine.y2)
|
||||
console.log('wallLine:', wallLine.x1, wallLine.y1, wallLine.x2, wallLine.y2)
|
||||
console.log('wallBaseLine:', wallBaseLine.x1, wallBaseLine.y1, wallBaseLine.x2, wallBaseLine.y2)
|
||||
console.log('isSamePoint result:', isSameLine2(wallBaseLine, wallLine))
|
||||
// console.log('index::::', index)
|
||||
// console.log('roofLine:', roofLine.x1, roofLine.y1, roofLine.x2, roofLine.y2)
|
||||
// console.log('wallLine:', wallLine.x1, wallLine.y1, wallLine.x2, wallLine.y2)
|
||||
// console.log('wallBaseLine:', wallBaseLine.x1, wallBaseLine.y1, wallBaseLine.x2, wallBaseLine.y2)
|
||||
// console.log('isSamePoint result:', isSameLine2(wallBaseLine, wallLine))
|
||||
|
||||
const isCollinear = (l1, l2, tolerance = 0.1) => {
|
||||
const slope1 = Math.abs(l1.x2 - l1.x1) < tolerance ? Infinity : (l1.y2 - l1.y1) / (l1.x2 - l1.x1)
|
||||
@ -871,7 +902,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
||||
|
||||
//현재 roof는 무조건 시계방향
|
||||
|
||||
const getAddLine = (p1, p2, stroke = '') => {
|
||||
const getAddLine = (p1, p2, stroke = '', lineType = 'eaveHelpLine') => {
|
||||
movedLines.push({ index, p1, p2 })
|
||||
|
||||
const dx = Math.abs(p2.x - p1.x);
|
||||
@ -884,14 +915,14 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
||||
fontSize: roof.fontSize,
|
||||
stroke: 'black',
|
||||
strokeWidth: 4,
|
||||
name: 'eaveHelpLine',
|
||||
lineName: 'eaveHelpLine',
|
||||
name: lineType,
|
||||
lineName: lineType,
|
||||
visible: true,
|
||||
roofId: roofId,
|
||||
selectable: true,
|
||||
hoverCursor: 'pointer',
|
||||
attributes: {
|
||||
type: 'eaveHelpLine',
|
||||
type: lineType,
|
||||
isStart: true,
|
||||
pitch: wallLine.attributes.pitch,
|
||||
planeSize: calcLinePlaneSize({ x1: p1.x, y1: p1.y, x2: p2.x, y2: p2.y }),
|
||||
@ -951,6 +982,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
||||
isIn = true
|
||||
|
||||
if (isStartEnd.start) {
|
||||
console.log('left_in::::isStartEnd:::::', isStartEnd)
|
||||
newPEnd.y = roofLine.y2
|
||||
newPEnd.x = roofLine.x2
|
||||
|
||||
@ -970,7 +1002,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
||||
const pLineX = sortRoofLines[prevIndex].x1
|
||||
|
||||
getAddLine({ x: newPStart.x, y: newPStart.y }, { x: ePoint.x, y: ePoint.y }, 'blue')
|
||||
getAddLine({ x: roofLine.x2, y: roofLine.y2 }, { x: newPointX, y: roofLine.y2 }, 'orange')
|
||||
// getAddLine({ x: roofLine.x2, y: roofLine.y2 }, { x: newPointX, y: roofLine.y2 }, 'orange')
|
||||
|
||||
if (Math.abs(wallBaseLine.y1 - wallLine.y1) < 0.1) {
|
||||
getAddLine({ x: pLineX, y: pLineY }, { x: newPointX, y: pLineY }, 'green')
|
||||
@ -981,6 +1013,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
||||
}
|
||||
|
||||
if (isStartEnd.end) {
|
||||
console.log('left_in::::isStartEnd:::::', isStartEnd)
|
||||
newPStart.y = roofLine.y1
|
||||
newPStart.x = roofLine.x1
|
||||
|
||||
@ -1000,7 +1033,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
||||
const pLineX = sortRoofLines[nextIndex].x2
|
||||
|
||||
getAddLine({ x: newPEnd.x, y: newPEnd.y }, { x: ePoint.x, y: ePoint.y }, 'blue')
|
||||
getAddLine({ x: roofLine.x1, y: roofLine.y1 }, { x: newPointX, y: roofLine.y1 }, 'orange')
|
||||
// getAddLine({ x: roofLine.x1, y: roofLine.y1 }, { x: newPointX, y: roofLine.y1 }, 'orange')
|
||||
|
||||
if (Math.abs(wallBaseLine.y2 - wallLine.y2) < 0.1) {
|
||||
getAddLine({ x: pLineX, y: pLineY }, { x: newPointX, y: pLineY }, 'green')
|
||||
@ -1070,6 +1103,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
||||
}
|
||||
|
||||
if (isStartEnd.end) {
|
||||
console.log('left_in::::isStartEnd:::::', isStartEnd)
|
||||
const moveDist = Big(wallLine.x1).minus(wallBaseLine.x1).abs().toNumber()
|
||||
const aStartY = Big(roofLine.y2).plus(moveDist).toNumber()
|
||||
const bStartY = Big(wallLine.y2).plus(moveDist).toNumber()
|
||||
@ -1131,6 +1165,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
||||
}
|
||||
} else if (condition === 'right_in') {
|
||||
if (isStartEnd.start) {
|
||||
console.log('right_in::::isStartEnd:::::', isStartEnd)
|
||||
newPEnd.y = roofLine.y2
|
||||
newPEnd.x = roofLine.x2
|
||||
|
||||
@ -1160,6 +1195,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
||||
}
|
||||
|
||||
if (isStartEnd.end) {
|
||||
console.log('left_in::::isStartEnd:::::', isStartEnd)
|
||||
newPStart.y = roofLine.y1
|
||||
newPStart.x = roofLine.x1
|
||||
|
||||
@ -1179,7 +1215,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
||||
const pLineX = sortRoofLines[nextIndex].x2
|
||||
|
||||
getAddLine({ x: newPEnd.x, y: newPEnd.y }, { x: ePoint.x, y: ePoint.y }, 'blue')
|
||||
getAddLine({ x: roofLine.x1, y: roofLine.y1 }, { x: newPointX, y: roofLine.y1 }, 'orange')
|
||||
//getAddLine({ x: roofLine.x1, y: Big(roofLine.y1).minus(moveDist).toNumber() }, { x: newPointX, y: Big(roofLine.y1).minus(moveDist).toNumber()}, 'orange')
|
||||
|
||||
if (Math.abs(wallBaseLine.y2 - wallLine.y2) < 0.1) {
|
||||
getAddLine({ x: pLineX, y: pLineY }, { x: newPointX, y: pLineY }, 'green')
|
||||
@ -1329,6 +1365,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
||||
|
||||
if (condition === 'top_in') {
|
||||
if (isStartEnd.start) {
|
||||
console.log('top_in start')
|
||||
const moveDist = Big(wallLine.y1).minus(wallBaseLine.y1).abs().toNumber()
|
||||
sPoint = { x: wallBaseLine.x1, y: wallBaseLine.y1 }
|
||||
newPStart.x = wallBaseLine.x1
|
||||
@ -1357,6 +1394,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
||||
}
|
||||
|
||||
if (isStartEnd.end) {
|
||||
console.log('top_in end', isStartEnd)
|
||||
const moveDist = Big(wallLine.y2).minus(wallBaseLine.y2).abs().toNumber()
|
||||
sPoint = { x: wallBaseLine.x2, y: wallBaseLine.y2 }
|
||||
newPEnd.x = wallBaseLine.x2
|
||||
@ -1384,6 +1422,68 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
||||
//getAddLine({ x: roofLine.x1, y: roofLine.y1 }, { x: roofLine.x1, y: newPointY }, 'orange')
|
||||
getAddLine(newPStart, newPEnd, 'red')
|
||||
}
|
||||
if(!isStartEnd.start && !isStartEnd.end){
|
||||
console.log('top_in start and end false')
|
||||
|
||||
const moveDistY1 = Big(wallLine.y1).minus(wallBaseLine.y1).abs().toNumber()
|
||||
const moveDistX1 = Big(wallLine.x1).minus(wallBaseLine.x1).abs().toNumber()
|
||||
const moveDistX2 = Big(wallLine.x2).minus(wallBaseLine.x2).abs().toNumber()
|
||||
const moveDistY2 = Big(wallLine.y2).minus(wallBaseLine.y2).abs().toNumber()
|
||||
|
||||
const sPoint = { x: wallBaseLine.x1, y: wallBaseLine.y1 }
|
||||
const ePoint = { x: wallBaseLine.x2, y: wallBaseLine.y2 }
|
||||
|
||||
|
||||
if(moveDistX1 > 0) {
|
||||
findPoints.push({ x: sPoint.x, y: sPoint.y, position: 'top_in_start' })
|
||||
|
||||
if(moveDistY1 > moveDistX1){
|
||||
const dist = moveDistY1 - moveDistX1
|
||||
// console.log('Creating extensionLine (Y1 > X1):', { sPoint, roofLine, dist, roofId })
|
||||
const createdLine = getAddLine({ x: sPoint.x, y: sPoint.y }, { x: roofLine.x1, y: roofLine.y1 + dist }, 'orange','extensionLine')
|
||||
console.log('extensionLine created - length:', createdLine.length, 'coords:', { x1: createdLine.x1, y1: createdLine.y1, x2: createdLine.x2, y2: createdLine.y2 })
|
||||
createdLine.set({ name: LINE_TYPE.SUBLINE.HIP, lineName: LINE_TYPE.SUBLINE.HIP, stroke: '#1083E3', strokeWidth: 2 })
|
||||
createdLine.attributes = { ...createdLine.attributes, type: LINE_TYPE.SUBLINE.HIP }
|
||||
innerLines.push(createdLine)
|
||||
|
||||
}else{
|
||||
const dist = moveDistX1 - moveDistY1
|
||||
// console.log('Creating extensionLine (X1 > Y1):', { sPoint, roofLine, dist, roofId })
|
||||
const createdLine = getAddLine({ x: sPoint.x, y: sPoint.y }, { x: roofLine.x1 - dist, y: roofLine.y1 }, 'orange','extensionLine')
|
||||
console.log('extensionLine created - length:', createdLine.length, 'coords:', { x1: createdLine.x1, y1: createdLine.y1, x2: createdLine.x2, y2: createdLine.y2 })
|
||||
createdLine.set({ name: LINE_TYPE.SUBLINE.HIP, lineName: LINE_TYPE.SUBLINE.HIP, stroke: '#1083E3', strokeWidth: 2 })
|
||||
createdLine.attributes = { ...createdLine.attributes, type: LINE_TYPE.SUBLINE.HIP }
|
||||
innerLines.push(createdLine)
|
||||
}
|
||||
|
||||
}
|
||||
if(moveDistX2 > 0) {
|
||||
findPoints.push({ x: ePoint.x, y: ePoint.y, position: 'top_in_end' })
|
||||
|
||||
if(moveDistY2 > moveDistX2){
|
||||
const dist = moveDistY2 - moveDistX2
|
||||
// getAddLine({ x: roofLine.x2, y: roofLine.y2 }, { x: roofLine.x2, y: roofLine.y2 - dist }, 'red')
|
||||
// console.log('Creating extensionLine (Y2 > X2):', { ePoint, roofLine, dist, roofId })
|
||||
const createdLine = getAddLine({ x: ePoint.x, y: ePoint.y }, { x: roofLine.x2, y: roofLine.y2 + dist }, 'orange', 'extensionLine')
|
||||
console.log('extensionLine created - length:', createdLine.length, 'coords:', { x1: createdLine.x1, y1: createdLine.y1, x2: createdLine.x2, y2: createdLine.y2 })
|
||||
createdLine.set({ name: LINE_TYPE.SUBLINE.HIP, lineName: LINE_TYPE.SUBLINE.HIP, stroke: '#1083E3', strokeWidth: 2 })
|
||||
createdLine.attributes = { ...createdLine.attributes, type: LINE_TYPE.SUBLINE.HIP }
|
||||
innerLines.push(createdLine)
|
||||
|
||||
}else{
|
||||
const dist = moveDistX2 - moveDistY2
|
||||
// getAddLine({ x: roofLine.x2, y: roofLine.y2 }, { x: roofLine.x2 - dist, y: roofLine.y2 }, 'red')
|
||||
// console.log('Creating extensionLine (X2 > Y2):', { ePoint, roofLine, dist, roofId })
|
||||
const createdLine = getAddLine({ x: ePoint.x, y: ePoint.y }, { x: roofLine.x2 + dist, y: roofLine.y2 }, 'orange', 'extensionLine')
|
||||
console.log('extensionLine created - length:', createdLine.length, 'coords:', { x1: createdLine.x1, y1: createdLine.y1, x2: createdLine.x2, y2: createdLine.y2 })
|
||||
createdLine.set({ name: LINE_TYPE.SUBLINE.HIP, lineName: LINE_TYPE.SUBLINE.HIP, stroke: '#1083E3', strokeWidth: 2 })
|
||||
createdLine.attributes = { ...createdLine.attributes, type: LINE_TYPE.SUBLINE.HIP }
|
||||
innerLines.push(createdLine)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} else if (condition === 'top_out') {
|
||||
console.log('top_out isStartEnd:::::::', isStartEnd)
|
||||
|
||||
@ -1444,6 +1544,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
||||
getAddLine(newPStart, newPEnd, 'red')
|
||||
}
|
||||
if (isStartEnd.end) {
|
||||
console.log('isStartEnd:::::', isStartEnd)
|
||||
const moveDist = Big(wallLine.y1).minus(wallBaseLine.y1).abs().toNumber()
|
||||
const aStartX = Big(roofLine.x2).minus(moveDist).toNumber()
|
||||
const bStartX = Big(wallLine.x2).minus(moveDist).toNumber()
|
||||
@ -1501,7 +1602,9 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
||||
getAddLine(newPStart, newPEnd, 'red')
|
||||
}
|
||||
} else if (condition === 'bottom_in') {
|
||||
console.log('bottom_in::::isStartEnd:::::', isStartEnd)
|
||||
if (isStartEnd.start) {
|
||||
console.log('isStartEnd:::::', isStartEnd)
|
||||
const moveDist = Big(wallLine.y1).minus(wallBaseLine.y1).abs().toNumber()
|
||||
sPoint = { x: wallBaseLine.x1, y: wallBaseLine.y1 }
|
||||
newPStart.x = wallBaseLine.x1
|
||||
@ -1530,6 +1633,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
||||
}
|
||||
|
||||
if (isStartEnd.end) {
|
||||
console.log('isStartEnd:::::', isStartEnd)
|
||||
const moveDist = Big(wallLine.y2).minus(wallBaseLine.y2).abs().toNumber()
|
||||
sPoint = { x: wallBaseLine.x2, y: wallBaseLine.y2 }
|
||||
newPEnd.x = wallBaseLine.x2
|
||||
@ -1557,9 +1661,73 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
||||
//getAddLine({ x: roofLine.x1, y: roofLine.y1 }, { x: roofLine.x1, y: newPointY }, 'orange')
|
||||
getAddLine(newPStart, newPEnd, 'red')
|
||||
}
|
||||
|
||||
if(!isStartEnd.start && !isStartEnd.end){
|
||||
|
||||
console.log('isStartEnd:::::', isStartEnd)
|
||||
|
||||
const moveDistY1 = Big(wallLine.y1).minus(wallBaseLine.y1).abs().toNumber()
|
||||
const moveDistX1 = Big(wallLine.x1).minus(wallBaseLine.x1).abs().toNumber()
|
||||
const moveDistX2 = Big(wallLine.x2).minus(wallBaseLine.x2).abs().toNumber()
|
||||
const moveDistY2 = Big(wallLine.y2).minus(wallBaseLine.y2).abs().toNumber()
|
||||
|
||||
const sPoint = { x: wallBaseLine.x1, y: wallBaseLine.y1 }
|
||||
const ePoint = { x: wallBaseLine.x2, y: wallBaseLine.y2 }
|
||||
|
||||
|
||||
if(moveDistX1 > 0) {
|
||||
findPoints.push({ x: sPoint.x, y: sPoint.y, position: 'bottom_in_start' })
|
||||
|
||||
if(moveDistY1 > moveDistX1){
|
||||
const dist = moveDistY1 - moveDistX1
|
||||
// console.log('Creating extensionLine (Y1 > X1):', { sPoint, roofLine, dist, roofId })
|
||||
const createdLine = getAddLine({ x: sPoint.x, y: sPoint.y }, { x: roofLine.x1, y: roofLine.y1 - dist }, 'orange','extensionLine')
|
||||
console.log('extensionLine created - length:', createdLine.length, 'coords:', { x1: createdLine.x1, y1: createdLine.y1, x2: createdLine.x2, y2: createdLine.y2 })
|
||||
createdLine.set({ name: LINE_TYPE.SUBLINE.HIP, lineName: LINE_TYPE.SUBLINE.HIP, stroke: '#1083E3', strokeWidth: 2 })
|
||||
createdLine.attributes = { ...createdLine.attributes, type: LINE_TYPE.SUBLINE.HIP }
|
||||
innerLines.push(createdLine)
|
||||
|
||||
}else{
|
||||
const dist = moveDistX1 - moveDistY1
|
||||
// console.log('Creating extensionLine (X1 > Y1):', { sPoint, roofLine, dist, roofId })
|
||||
const createdLine = getAddLine({ x: sPoint.x, y: sPoint.y }, { x: roofLine.x1 + dist, y: roofLine.y1 }, 'orange','extensionLine')
|
||||
console.log('extensionLine created - length:', createdLine.length, 'coords:', { x1: createdLine.x1, y1: createdLine.y1, x2: createdLine.x2, y2: createdLine.y2 })
|
||||
createdLine.set({ name: LINE_TYPE.SUBLINE.HIP, lineName: LINE_TYPE.SUBLINE.HIP, stroke: '#1083E3', strokeWidth: 2 })
|
||||
createdLine.attributes = { ...createdLine.attributes, type: LINE_TYPE.SUBLINE.HIP }
|
||||
innerLines.push(createdLine)
|
||||
}
|
||||
|
||||
}
|
||||
if(moveDistX2 > 0) {
|
||||
findPoints.push({ x: ePoint.x, y: ePoint.y, position: 'bottom_in_end' })
|
||||
|
||||
if(moveDistY2 > moveDistX2){
|
||||
const dist = moveDistY2 - moveDistX2
|
||||
// getAddLine({ x: roofLine.x2, y: roofLine.y2 }, { x: roofLine.x2, y: roofLine.y2 - dist }, 'red')
|
||||
// console.log('Creating extensionLine (Y2 > X2):', { ePoint, roofLine, dist, roofId })
|
||||
const createdLine = getAddLine({ x: ePoint.x, y: ePoint.y }, { x: roofLine.x2, y: roofLine.y2 - dist }, 'orange', 'extensionLine')
|
||||
console.log('extensionLine created - length:', createdLine.length, 'coords:', { x1: createdLine.x1, y1: createdLine.y1, x2: createdLine.x2, y2: createdLine.y2 })
|
||||
createdLine.set({ name: LINE_TYPE.SUBLINE.HIP, lineName: LINE_TYPE.SUBLINE.HIP, stroke: '#1083E3', strokeWidth: 2 })
|
||||
createdLine.attributes = { ...createdLine.attributes, type: LINE_TYPE.SUBLINE.HIP }
|
||||
innerLines.push(createdLine)
|
||||
|
||||
}else{
|
||||
const dist = moveDistX2 - moveDistY2
|
||||
// getAddLine({ x: roofLine.x2, y: roofLine.y2 }, { x: roofLine.x2 - dist, y: roofLine.y2 }, 'red')
|
||||
// console.log('Creating extensionLine (X2 > Y2):', { ePoint, roofLine, dist, roofId })
|
||||
const createdLine = getAddLine({ x: ePoint.x, y: ePoint.y }, { x: roofLine.x2 - dist, y: roofLine.y2 }, 'orange', 'extensionLine')
|
||||
console.log('extensionLine created - length:', createdLine.length, 'coords:', { x1: createdLine.x1, y1: createdLine.y1, x2: createdLine.x2, y2: createdLine.y2 })
|
||||
createdLine.set({ name: LINE_TYPE.SUBLINE.HIP, lineName: LINE_TYPE.SUBLINE.HIP, stroke: '#1083E3', strokeWidth: 2 })
|
||||
createdLine.attributes = { ...createdLine.attributes, type: LINE_TYPE.SUBLINE.HIP }
|
||||
innerLines.push(createdLine)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (condition === 'bottom_out') {
|
||||
console.log('bottom_out isStartEnd:::::::', isStartEnd)
|
||||
if (isStartEnd.start) {
|
||||
console.log('isStartEnd:::::::', isStartEnd)
|
||||
const moveDist = Big(wallLine.y1).minus(wallBaseLine.y1).abs().toNumber()
|
||||
const aStartX = Big(roofLine.x1).minus(moveDist).toNumber()
|
||||
const bStartX = Big(wallLine.x1).minus(moveDist).toNumber()
|
||||
@ -1618,6 +1786,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
||||
}
|
||||
|
||||
if (isStartEnd.end) {
|
||||
console.log('isStartEnd:::::', isStartEnd)
|
||||
const moveDist = Big(wallLine.y1).minus(wallBaseLine.y1).abs().toNumber()
|
||||
const aStartX = Big(roofLine.x2).plus(moveDist).toNumber()
|
||||
const bStartX = Big(wallLine.x2).plus(moveDist).toNumber()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user