Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/skeleton-dev
# Conflicts: # src/util/skeleton-utils.js
This commit is contained in:
commit
56371807ab
@ -11,6 +11,7 @@ import { moduleSelectionDataState } from '@/store/selectedModuleOptions'
|
|||||||
import { useCanvasPopupStatusController } from '@/hooks/common/useCanvasPopupStatusController'
|
import { useCanvasPopupStatusController } from '@/hooks/common/useCanvasPopupStatusController'
|
||||||
import { isObjectNotEmpty } from '@/util/common-utils'
|
import { isObjectNotEmpty } from '@/util/common-utils'
|
||||||
import { normalizeDecimal} from '@/util/input-utils'
|
import { normalizeDecimal} from '@/util/input-utils'
|
||||||
|
import { CalculatorInput } from '@/components/common/input/CalcInput'
|
||||||
|
|
||||||
export default function Module({ setTabNum }) {
|
export default function Module({ setTabNum }) {
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
@ -185,11 +186,23 @@ export default function Module({ setTabNum }) {
|
|||||||
<div className="eaves-keraba-td">
|
<div className="eaves-keraba-td">
|
||||||
<div className="outline-form">
|
<div className="outline-form">
|
||||||
<div className="grid-select mr10">
|
<div className="grid-select mr10">
|
||||||
<input
|
{/*<input*/}
|
||||||
type="text"
|
{/* type="text"*/}
|
||||||
|
{/* className="input-origin block"*/}
|
||||||
|
{/* value={inputInstallHeight}*/}
|
||||||
|
{/* onChange={(e) => setInputInstallHeight(normalizeDecimal(e.target.value))}*/}
|
||||||
|
{/*/>*/}
|
||||||
|
<CalculatorInput
|
||||||
|
id=""
|
||||||
|
name=""
|
||||||
|
label=""
|
||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
value={inputInstallHeight}
|
value={inputInstallHeight}
|
||||||
onChange={(e) => setInputInstallHeight(normalizeDecimal(e.target.value))}
|
onChange={(value) => setInputInstallHeight(value)}
|
||||||
|
options={{
|
||||||
|
allowNegative: false,
|
||||||
|
allowDecimal: false
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">m</span>
|
<span className="thin">m</span>
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting'
|
|||||||
import { useCommonCode } from '@/hooks/common/useCommonCode'
|
import { useCommonCode } from '@/hooks/common/useCommonCode'
|
||||||
import Swal from 'sweetalert2'
|
import Swal from 'sweetalert2'
|
||||||
import { normalizeDecimal} from '@/util/input-utils'
|
import { normalizeDecimal} from '@/util/input-utils'
|
||||||
|
import { CalculatorInput } from '@/components/common/input/CalcInput'
|
||||||
|
|
||||||
export const Orientation = forwardRef((props, ref) => {
|
export const Orientation = forwardRef((props, ref) => {
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
@ -436,13 +437,26 @@ export const Orientation = forwardRef((props, ref) => {
|
|||||||
<label htmlFor="ch99">{getMessage('modal.module.basic.setting.orientation.setting.angle.passivity')}</label>
|
<label htmlFor="ch99">{getMessage('modal.module.basic.setting.orientation.setting.angle.passivity')}</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="input-grid mr10" style={{ width: '60px' }}>
|
<div className="input-grid mr10" style={{ width: '60px' }}>
|
||||||
<input
|
{/*<input*/}
|
||||||
type="text"
|
{/* type="text"*/}
|
||||||
|
{/* className="input-origin block"*/}
|
||||||
|
{/* value={inputCompasDeg}*/}
|
||||||
|
{/* readOnly={!hasAnglePassivity}*/}
|
||||||
|
{/* placeholder={0}*/}
|
||||||
|
{/* onChange={(e) => checkDegree(e.target.value)}*/}
|
||||||
|
{/*/>*/}
|
||||||
|
<CalculatorInput
|
||||||
|
id=""
|
||||||
|
name=""
|
||||||
|
label=""
|
||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
value={inputCompasDeg}
|
value={inputCompasDeg}
|
||||||
readOnly={!hasAnglePassivity}
|
readOnly={!hasAnglePassivity}
|
||||||
placeholder={0}
|
onChange={(value) => setInputCompasDeg(value)}
|
||||||
onChange={(e) => checkDegree(e.target.value)}
|
options={{
|
||||||
|
allowNegative: true,
|
||||||
|
allowDecimal: false
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">°</span>
|
<span className="thin">°</span>
|
||||||
@ -533,7 +547,19 @@ export const Orientation = forwardRef((props, ref) => {
|
|||||||
<div className="outline-form mt15">
|
<div className="outline-form mt15">
|
||||||
<span>{getMessage('modal.module.basic.setting.module.placement.area')}</span>
|
<span>{getMessage('modal.module.basic.setting.module.placement.area')}</span>
|
||||||
<div className="input-grid mr10" style={{ width: '60px' }}>
|
<div className="input-grid mr10" style={{ width: '60px' }}>
|
||||||
<input type="text" className="input-origin block" value={inputMargin} onChange={(e) => setInputMargin(normalizeDecimal(e.target.value))} />
|
{/*<input type="text" className="input-origin block" value={inputMargin} onChange={(e) => setInputMargin(normalizeDecimal(e.target.value))} />*/}
|
||||||
|
<CalculatorInput
|
||||||
|
id=""
|
||||||
|
name=""
|
||||||
|
label=""
|
||||||
|
className="input-origin block"
|
||||||
|
value={inputMargin}
|
||||||
|
onChange={(value) => setInputMargin(value)}
|
||||||
|
options={{
|
||||||
|
allowNegative: false,
|
||||||
|
allowDecimal: false
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">m</span>
|
<span className="thin">m</span>
|
||||||
</div>
|
</div>
|
||||||
@ -561,11 +587,23 @@ export const Orientation = forwardRef((props, ref) => {
|
|||||||
<div className="outline-form">
|
<div className="outline-form">
|
||||||
<span>{getMessage('modal.module.basic.setting.module.fitting.height')}</span>
|
<span>{getMessage('modal.module.basic.setting.module.fitting.height')}</span>
|
||||||
<div className="input-grid mr10">
|
<div className="input-grid mr10">
|
||||||
<input
|
{/*<input*/}
|
||||||
type="text"
|
{/* type="text"*/}
|
||||||
|
{/* className="input-origin block"*/}
|
||||||
|
{/* value={inputInstallHeight}*/}
|
||||||
|
{/* onChange={(e) => handleChangeInstallHeight(normalizeDecimal(e.target.value))}*/}
|
||||||
|
{/*/>*/}
|
||||||
|
<CalculatorInput
|
||||||
|
id=""
|
||||||
|
name=""
|
||||||
|
label=""
|
||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
value={inputInstallHeight}
|
value={inputInstallHeight}
|
||||||
onChange={(e) => handleChangeInstallHeight(normalizeDecimal(e.target.value))}
|
onChange={(value) => handleChangeInstallHeight(value)}
|
||||||
|
options={{
|
||||||
|
allowNegative: false,
|
||||||
|
allowDecimal: false
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">m</span>
|
<span className="thin">m</span>
|
||||||
@ -589,11 +627,23 @@ export const Orientation = forwardRef((props, ref) => {
|
|||||||
<div className="outline-form">
|
<div className="outline-form">
|
||||||
<span>{getMessage('modal.module.basic.setting.module.standard.snowfall.amount')}</span>
|
<span>{getMessage('modal.module.basic.setting.module.standard.snowfall.amount')}</span>
|
||||||
<div className="input-grid mr10">
|
<div className="input-grid mr10">
|
||||||
<input
|
{/*<input*/}
|
||||||
type="text"
|
{/* type="text"*/}
|
||||||
|
{/* className="input-origin block"*/}
|
||||||
|
{/* value={inputVerticalSnowCover}*/}
|
||||||
|
{/* onChange={(e) => handleChangeVerticalSnowCover(normalizeDecimal(e.target.value))}*/}
|
||||||
|
{/*/>*/}
|
||||||
|
<CalculatorInput
|
||||||
|
id=""
|
||||||
|
name=""
|
||||||
|
label=""
|
||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
value={inputVerticalSnowCover}
|
value={inputInstallHeight}
|
||||||
onChange={(e) => handleChangeVerticalSnowCover(normalizeDecimal(e.target.value))}
|
onChange={(value) => handleChangeVerticalSnowCover(value)}
|
||||||
|
options={{
|
||||||
|
allowNegative: false,
|
||||||
|
allowDecimal: false
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">cm</span>
|
<span className="thin">cm</span>
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import { forwardRef, useContext, useEffect, useImperativeHandle, useRef, useStat
|
|||||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||||
import Swal from 'sweetalert2'
|
import Swal from 'sweetalert2'
|
||||||
import { normalizeDigits } from '@/util/input-utils'
|
import { normalizeDigits } from '@/util/input-utils'
|
||||||
|
import { CalculatorInput } from '@/components/common/input/CalcInput'
|
||||||
|
|
||||||
const Trestle = forwardRef((props, ref) => {
|
const Trestle = forwardRef((props, ref) => {
|
||||||
const { tabNum, setTabNum, trestleTrigger, roofs, setRoofs, moduleSelectionData, setModuleSelectionData, setRoofsStore } = props
|
const { tabNum, setTabNum, trestleTrigger, roofs, setRoofs, moduleSelectionData, setModuleSelectionData, setRoofsStore } = props
|
||||||
@ -885,12 +886,24 @@ const Trestle = forwardRef((props, ref) => {
|
|||||||
<div className="outline-form mr15">
|
<div className="outline-form mr15">
|
||||||
<span>{getMessage('modal.module.basic.setting.module.placement.area.eaves')}</span>
|
<span>{getMessage('modal.module.basic.setting.module.placement.area.eaves')}</span>
|
||||||
<div className="input-grid mr10">
|
<div className="input-grid mr10">
|
||||||
<input
|
{/*<input*/}
|
||||||
type="number"
|
{/* type="number"*/}
|
||||||
|
{/* className="input-origin block"*/}
|
||||||
|
{/* value={eavesMargin ?? 0}*/}
|
||||||
|
{/* // onChange={(e) => dispatch({ type: 'SET_TRESTLE_DETAIL', roof: { ...trestleState, eavesMargin: e.target.value } })}*/}
|
||||||
|
{/* onChange={(e) => setEavesMargin(+e.target.value)}*/}
|
||||||
|
{/*/>*/}
|
||||||
|
<CalculatorInput
|
||||||
|
id=""
|
||||||
|
name=""
|
||||||
|
label=""
|
||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
value={eavesMargin ?? 0}
|
value={eavesMargin ?? 0}
|
||||||
// onChange={(e) => dispatch({ type: 'SET_TRESTLE_DETAIL', roof: { ...trestleState, eavesMargin: e.target.value } })}
|
onChange={(value) => setEavesMargin(value)}
|
||||||
onChange={(e) => setEavesMargin(+e.target.value)}
|
options={{
|
||||||
|
allowNegative: false,
|
||||||
|
allowDecimal: false
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">mm</span>
|
<span className="thin">mm</span>
|
||||||
@ -898,12 +911,24 @@ const Trestle = forwardRef((props, ref) => {
|
|||||||
<div className="outline-form mr15">
|
<div className="outline-form mr15">
|
||||||
<span>{getMessage('modal.module.basic.setting.module.placement.area.ridge')}</span>
|
<span>{getMessage('modal.module.basic.setting.module.placement.area.ridge')}</span>
|
||||||
<div className="input-grid mr10">
|
<div className="input-grid mr10">
|
||||||
<input
|
{/*<input*/}
|
||||||
type="number"
|
{/* type="number"*/}
|
||||||
|
{/* className="input-origin block"*/}
|
||||||
|
{/* value={ridgeMargin ?? 0}*/}
|
||||||
|
{/* // onChange={(e) => dispatch({ type: 'SET_TRESTLE_DETAIL', roof: { ...trestleState, ridgeMargin: e.target.value } })}*/}
|
||||||
|
{/* onChange={(e) => setRidgeMargin(+e.target.value)}*/}
|
||||||
|
{/*/>*/}
|
||||||
|
<CalculatorInput
|
||||||
|
id=""
|
||||||
|
name=""
|
||||||
|
label=""
|
||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
value={ridgeMargin ?? 0}
|
value={ridgeMargin ?? 0}
|
||||||
// onChange={(e) => dispatch({ type: 'SET_TRESTLE_DETAIL', roof: { ...trestleState, ridgeMargin: e.target.value } })}
|
onChange={(value) => setRidgeMargin(value)}
|
||||||
onChange={(e) => setRidgeMargin(+e.target.value)}
|
options={{
|
||||||
|
allowNegative: false,
|
||||||
|
allowDecimal: false
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">mm</span>
|
<span className="thin">mm</span>
|
||||||
@ -911,12 +936,24 @@ const Trestle = forwardRef((props, ref) => {
|
|||||||
<div className="outline-form ">
|
<div className="outline-form ">
|
||||||
<span>{getMessage('modal.module.basic.setting.module.placement.area.keraba')}</span>
|
<span>{getMessage('modal.module.basic.setting.module.placement.area.keraba')}</span>
|
||||||
<div className="input-grid mr10">
|
<div className="input-grid mr10">
|
||||||
<input
|
{/*<input*/}
|
||||||
type="number"
|
{/* type="number"*/}
|
||||||
|
{/* className="input-origin block"*/}
|
||||||
|
{/* value={kerabaMargin ?? 0}*/}
|
||||||
|
{/* // onChange={(e) => dispatch({ type: 'SET_TRESTLE_DETAIL', roof: { ...trestleState, kerabaMargin: e.target.value } })}*/}
|
||||||
|
{/* onChange={(e) => setKerabaMargin(+e.target.value)}*/}
|
||||||
|
{/*/>*/}
|
||||||
|
<CalculatorInput
|
||||||
|
id=""
|
||||||
|
name=""
|
||||||
|
label=""
|
||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
value={kerabaMargin ?? 0}
|
value={kerabaMargin ?? 0}
|
||||||
// onChange={(e) => dispatch({ type: 'SET_TRESTLE_DETAIL', roof: { ...trestleState, kerabaMargin: e.target.value } })}
|
onChange={(value) => setKerabaMargin(value)}
|
||||||
onChange={(e) => setKerabaMargin(+e.target.value)}
|
options={{
|
||||||
|
allowNegative: false,
|
||||||
|
allowDecimal: false
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">mm</span>
|
<span className="thin">mm</span>
|
||||||
|
|||||||
@ -631,7 +631,7 @@ export function useMovementSetting(id) {
|
|||||||
}
|
}
|
||||||
return Big(0); // 기본값으로 0 반환 또는 다른 적절한 기본값
|
return Big(0); // 기본값으로 0 반환 또는 다른 적절한 기본값
|
||||||
})();
|
})();
|
||||||
if (target.y1 === target.y2) {
|
if (Math.abs(target.y1 - target.y2) < 0.5) {
|
||||||
value = value.neg()
|
value = value.neg()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -851,12 +851,15 @@ export const usePolygon = () => {
|
|||||||
// innerLines와 polygonLines의 겹침을 확인하고 type 변경
|
// innerLines와 polygonLines의 겹침을 확인하고 type 변경
|
||||||
innerLines.forEach((innerLine) => {
|
innerLines.forEach((innerLine) => {
|
||||||
polygonLines.forEach((polygonLine) => {
|
polygonLines.forEach((polygonLine) => {
|
||||||
|
if (polygonLine.attributes.type === LINE_TYPE.WALLLINE.EAVES) {
|
||||||
|
return
|
||||||
|
}
|
||||||
if (checkLineOverlap(innerLine, polygonLine)) {
|
if (checkLineOverlap(innerLine, polygonLine)) {
|
||||||
// innerLine의 type을 polygonLine의 type으로 변경
|
// innerLine의 type을 polygonLine의 type으로 변경
|
||||||
if (innerLine.attributes && polygonLine.attributes.type) {
|
if (innerLine.attributes && polygonLine.attributes.type) {
|
||||||
// innerLine이 polygonLine보다 긴 경우 polygonLine.need를 false로 변경
|
// innerLine이 polygonLine보다 긴 경우 polygonLine.need를 false로 변경
|
||||||
if (polygonLine.length < innerLine.length) {
|
if (polygonLine.length < innerLine.length) {
|
||||||
if (polygonLine.lineName !== 'eaveHelpLine') {
|
if (polygonLine.lineName !== 'eaveHelpLine' || polygonLine.lineName !== 'eaveHelpLine') {
|
||||||
polygonLine.need = false
|
polygonLine.need = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1014,6 +1017,7 @@ export const usePolygon = () => {
|
|||||||
canvas.add(line)
|
canvas.add(line)
|
||||||
})
|
})
|
||||||
canvas.renderAll()*/
|
canvas.renderAll()*/
|
||||||
|
|
||||||
polygonLines = polygonLines.filter((line) => line.need)
|
polygonLines = polygonLines.filter((line) => line.need)
|
||||||
|
|
||||||
polygonLines.forEach((line) => {
|
polygonLines.forEach((line) => {
|
||||||
@ -1377,7 +1381,6 @@ export const usePolygon = () => {
|
|||||||
let newRoofs = getSplitRoofsPoints(allLines)
|
let newRoofs = getSplitRoofsPoints(allLines)
|
||||||
|
|
||||||
newRoofs = newRoofs.filter((roof) => roof.length !== 0)
|
newRoofs = newRoofs.filter((roof) => roof.length !== 0)
|
||||||
|
|
||||||
newRoofs.forEach((roofPoint, index) => {
|
newRoofs.forEach((roofPoint, index) => {
|
||||||
let defense, pitch
|
let defense, pitch
|
||||||
|
|
||||||
@ -1411,6 +1414,124 @@ export const usePolygon = () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// representLines가 없다면 A,B타입중 하나임
|
||||||
|
if (representLines.length === 0) {
|
||||||
|
// 1. roofPoint로 폴리곤의 라인들을 생성
|
||||||
|
const roofPolygonLines = []
|
||||||
|
for (let i = 0; i < roofPoint.length; i++) {
|
||||||
|
const nextIndex = (i + 1) % roofPoint.length
|
||||||
|
const startPt = roofPoint[i]
|
||||||
|
const endPt = roofPoint[nextIndex]
|
||||||
|
roofPolygonLines.push({
|
||||||
|
x1: startPt.x,
|
||||||
|
y1: startPt.y,
|
||||||
|
x2: endPt.x,
|
||||||
|
y2: endPt.y,
|
||||||
|
startPoint: startPt,
|
||||||
|
endPoint: endPt,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 평행 여부 확인 함수
|
||||||
|
const checkParallel = (line1, line2) => {
|
||||||
|
const v1x = line1.x2 - line1.x1
|
||||||
|
const v1y = line1.y2 - line1.y1
|
||||||
|
const v2x = line2.x2 - line2.x1
|
||||||
|
const v2y = line2.y2 - line2.y1
|
||||||
|
|
||||||
|
const length1 = Math.sqrt(v1x ** 2 + v1y ** 2)
|
||||||
|
const length2 = Math.sqrt(v2x ** 2 + v2y ** 2)
|
||||||
|
|
||||||
|
if (length1 === 0 || length2 === 0) return false
|
||||||
|
|
||||||
|
const norm1x = v1x / length1
|
||||||
|
const norm1y = v1y / length1
|
||||||
|
const norm2x = v2x / length2
|
||||||
|
const norm2y = v2y / length2
|
||||||
|
|
||||||
|
const EPSILON = 0.01
|
||||||
|
const crossProduct = Math.abs(norm1x * norm2y - norm1y * norm2x)
|
||||||
|
const dotProduct = norm1x * norm2x + norm1y * norm2y
|
||||||
|
|
||||||
|
return crossProduct < EPSILON || Math.abs(Math.abs(dotProduct) - 1) < EPSILON
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 점에서 라인까지의 거리 계산 함수
|
||||||
|
const getDistanceFromPointToLine = (point, lineP1, lineP2) => {
|
||||||
|
const A = point.x - lineP1.x
|
||||||
|
const B = point.y - lineP1.y
|
||||||
|
const C = lineP2.x - lineP1.x
|
||||||
|
const D = lineP2.y - lineP1.y
|
||||||
|
|
||||||
|
const dot = A * C + B * D
|
||||||
|
const lenSq = C * C + D * D
|
||||||
|
let param = -1
|
||||||
|
|
||||||
|
if (lenSq !== 0) {
|
||||||
|
param = dot / lenSq
|
||||||
|
}
|
||||||
|
|
||||||
|
let xx, yy
|
||||||
|
|
||||||
|
if (param < 0) {
|
||||||
|
xx = lineP1.x
|
||||||
|
yy = lineP1.y
|
||||||
|
} else if (param > 1) {
|
||||||
|
xx = lineP2.x
|
||||||
|
yy = lineP2.y
|
||||||
|
} else {
|
||||||
|
xx = lineP1.x + param * C
|
||||||
|
yy = lineP1.y + param * D
|
||||||
|
}
|
||||||
|
|
||||||
|
const dx = point.x - xx
|
||||||
|
const dy = point.y - yy
|
||||||
|
|
||||||
|
return Math.sqrt(dx * dx + dy * dy)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 두 평행한 라인 사이의 거리 계산 (한 라인의 중점에서 다른 라인까지의 거리)
|
||||||
|
const getDistanceBetweenParallelLines = (line1, line2) => {
|
||||||
|
const midPoint = {
|
||||||
|
x: (line1.x1 + line1.x2) / 2,
|
||||||
|
y: (line1.y1 + line1.y2) / 2,
|
||||||
|
}
|
||||||
|
return getDistanceFromPointToLine(midPoint, { x: line2.x1, y: line2.y1 }, { x: line2.x2, y: line2.y2 })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. roofPolygonLines의 모든 라인에서 평행하면서 가장 가까운 EAVES 라인 찾기
|
||||||
|
let closestLine = null
|
||||||
|
let minDistance = Infinity
|
||||||
|
|
||||||
|
roofPolygonLines.forEach((roofLine) => {
|
||||||
|
;[...polygonLines, ...innerLines].forEach((line) => {
|
||||||
|
// EAVES 타입만 필터링
|
||||||
|
if (line.attributes?.type !== LINE_TYPE.WALLLINE.EAVES && line.attributes?.type !== LINE_TYPE.WALLLINE.EAVE_HELP_LINE) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const lineObj = {
|
||||||
|
x1: line.startPoint.x,
|
||||||
|
y1: line.startPoint.y,
|
||||||
|
x2: line.endPoint.x,
|
||||||
|
y2: line.endPoint.y,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkParallel(roofLine, lineObj)) {
|
||||||
|
const distance = getDistanceBetweenParallelLines(roofLine, lineObj)
|
||||||
|
if (distance < minDistance && distance > 0) {
|
||||||
|
minDistance = distance
|
||||||
|
closestLine = line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
if (closestLine) {
|
||||||
|
representLines.push(closestLine)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// representLines중 가장 긴 line을 찾는다.
|
// representLines중 가장 긴 line을 찾는다.
|
||||||
representLines.forEach((line) => {
|
representLines.forEach((line) => {
|
||||||
if (!representLine) {
|
if (!representLine) {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -494,29 +494,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
/*
|
|
||||||
//2. 연결이 끊어진 스켈레톤 선을 찾아 연장합니다.
|
|
||||||
const { disconnectedLines } = findDisconnectedSkeletonLines(skeletonLines, roof.lines);
|
|
||||||
|
|
||||||
if(disconnectedLines.length > 0) {
|
|
||||||
|
|
||||||
disconnectedLines.forEach(dLine => {
|
|
||||||
const { index, extendedLine, p1Connected, p2Connected } = dLine;
|
|
||||||
const newPoint = extendedLine?.point;
|
|
||||||
if (!newPoint) return;
|
|
||||||
// p1이 끊어졌으면 p1을, p2가 끊어졌으면 p2를 연장된 지점으로 업데이트
|
|
||||||
if (p1Connected) { //p2 연장
|
|
||||||
skeletonLines[index].p2 = { ...skeletonLines[index].p2, x: newPoint.x, y: newPoint.y };
|
|
||||||
} else if (p2Connected) {//p1 연장
|
|
||||||
skeletonLines[index].p1 = { ...skeletonLines[index].p1, x: newPoint.x, y: newPoint.y };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//2-1 확장된 스켈레톤 선이 연장되다가 서로 만나면 만난점(접점)에서 멈추어야 된다.
|
|
||||||
trimIntersectingExtendedLines(skeletonLines, disconnectedLines);
|
|
||||||
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
//2. 연결이 끊어진 라인이 있을경우 찾아서 추가한다(동 이동일때)
|
//2. 연결이 끊어진 라인이 있을경우 찾아서 추가한다(동 이동일때)
|
||||||
|
|
||||||
@ -549,17 +527,6 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
|||||||
{ x: sktLine.p2.x, y: sktLine.p2.y }
|
{ x: sktLine.p2.x, y: sktLine.p2.y }
|
||||||
);
|
);
|
||||||
|
|
||||||
//그림을 그릴때 idx 가 필요함 roof는 왼쪽부터 시작됨 - 그림그리는 순서가 필요함
|
|
||||||
|
|
||||||
|
|
||||||
// roofLines.forEach((roofLine) => {
|
|
||||||
//
|
|
||||||
// if (isSameLine(p1.x, p1.y, p2.x, p2.y, roofLine) || isSameLine(p2.x, p2.y, p1.x, p1.y, roofLine)) {
|
|
||||||
// roofIdx = roofLine.idx;
|
|
||||||
// console.log("roofIdx::::::", roofIdx)
|
|
||||||
// return false; // forEach 중단
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
|
|
||||||
const skeletonLine = new QLine([p1.x, p1.y, p2.x, p2.y], {
|
const skeletonLine = new QLine([p1.x, p1.y, p2.x, p2.y], {
|
||||||
@ -579,7 +546,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
|||||||
//visible: (!sktLine.attributes.isOuterEdge),
|
//visible: (!sktLine.attributes.isOuterEdge),
|
||||||
});
|
});
|
||||||
|
|
||||||
coordinateText(skeletonLine)
|
//coordinateText(skeletonLine)
|
||||||
canvas.add(skeletonLine);
|
canvas.add(skeletonLine);
|
||||||
skeletonLine.bringToFront();
|
skeletonLine.bringToFront();
|
||||||
existingLines.add(lineKey); // 추가된 라인을 추적
|
existingLines.add(lineKey); // 추가된 라인을 추적
|
||||||
@ -613,7 +580,6 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
|||||||
const currentRoofLines = canvas.getObjects().filter((obj) => obj.lineName === 'roofLine' && obj.attributes.roofId === roofId)
|
const currentRoofLines = canvas.getObjects().filter((obj) => obj.lineName === 'roofLine' && obj.attributes.roofId === roofId)
|
||||||
let roofLineRects = canvas.getObjects().filter((obj) => obj.name === 'roofLineRect' && obj.roofId === roofId)
|
let roofLineRects = canvas.getObjects().filter((obj) => obj.name === 'roofLineRect' && obj.roofId === roofId)
|
||||||
|
|
||||||
|
|
||||||
roofLineRects.forEach((roofLineRect) => {
|
roofLineRects.forEach((roofLineRect) => {
|
||||||
canvas.remove(roofLineRect)
|
canvas.remove(roofLineRect)
|
||||||
canvas.renderAll()
|
canvas.renderAll()
|
||||||
@ -629,69 +595,64 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
|||||||
return [...lines].sort((a, b) => {
|
return [...lines].sort((a, b) => {
|
||||||
// Get all coordinates in a consistent order
|
// Get all coordinates in a consistent order
|
||||||
const getCoords = (line) => {
|
const getCoords = (line) => {
|
||||||
const x1 = line.x1 ?? line.get('x1');
|
const x1 = line.x1 ?? line.get('x1')
|
||||||
const y1 = line.y1 ?? line.get('y1');
|
const y1 = line.y1 ?? line.get('y1')
|
||||||
const x2 = line.x2 ?? line.get('x2');
|
const x2 = line.x2 ?? line.get('x2')
|
||||||
const y2 = line.y2 ?? line.get('y2');
|
const y2 = line.y2 ?? line.get('y2')
|
||||||
|
|
||||||
// Sort points left-to-right, then top-to-bottom
|
// Sort points left-to-right, then top-to-bottom
|
||||||
return x1 < x2 || (x1 === x2 && y1 < y2)
|
return x1 < x2 || (x1 === x2 && y1 < y2) ? [x1, y1, x2, y2] : [x2, y2, x1, y1]
|
||||||
? [x1, y1, x2, y2]
|
}
|
||||||
: [x2, y2, x1, y1];
|
|
||||||
};
|
|
||||||
|
|
||||||
const aCoords = getCoords(a);
|
const aCoords = getCoords(a)
|
||||||
const bCoords = getCoords(b);
|
const bCoords = getCoords(b)
|
||||||
|
|
||||||
// Compare each coordinate in order
|
// Compare each coordinate in order
|
||||||
for (let i = 0; i < 4; i++) {
|
for (let i = 0; i < 4; i++) {
|
||||||
if (Math.abs(aCoords[i] - bCoords[i]) > 0.1) {
|
if (Math.abs(aCoords[i] - bCoords[i]) > 0.1) {
|
||||||
return aCoords[i] - bCoords[i];
|
return aCoords[i] - bCoords[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 각 라인 집합 정렬
|
// 각 라인 집합 정렬
|
||||||
|
const sortWallLines = ensureCounterClockwiseLines(wallLines)
|
||||||
|
const sortWallBaseLines = ensureCounterClockwiseLines(wall.baseLines)
|
||||||
|
const sortRoofLines = ensureCounterClockwiseLines(roofLines)
|
||||||
|
|
||||||
// roofLines의 방향에 맞춰 currentRoofLines의 방향을 조정
|
// roofLines의 방향에 맞춰 currentRoofLines의 방향을 조정
|
||||||
const alignLineDirection = (sourceLines, targetLines) => {
|
const alignLineDirection = (sourceLines, targetLines) => {
|
||||||
return sourceLines.map(sourceLine => {
|
return sourceLines.map((sourceLine) => {
|
||||||
// 가장 가까운 targetLine 찾기
|
// 가장 가까운 targetLine 찾기
|
||||||
const nearestTarget = targetLines.reduce((nearest, targetLine) => {
|
const nearestTarget = targetLines.reduce((nearest, targetLine) => {
|
||||||
const sourceCenter = {
|
const sourceCenter = {
|
||||||
x: (sourceLine.x1 + sourceLine.x2) / 2,
|
x: (sourceLine.x1 + sourceLine.x2) / 2,
|
||||||
y: (sourceLine.y1 + sourceLine.y2) / 2
|
y: (sourceLine.y1 + sourceLine.y2) / 2,
|
||||||
};
|
}
|
||||||
const targetCenter = {
|
const targetCenter = {
|
||||||
x: (targetLine.x1 + targetLine.x2) / 2,
|
x: (targetLine.x1 + targetLine.x2) / 2,
|
||||||
y: (targetLine.y1 + targetLine.y2) / 2
|
y: (targetLine.y1 + targetLine.y2) / 2,
|
||||||
};
|
}
|
||||||
const distance = Math.hypot(
|
const distance = Math.hypot(sourceCenter.x - targetCenter.x, sourceCenter.y - targetCenter.y)
|
||||||
sourceCenter.x - targetCenter.x,
|
|
||||||
sourceCenter.y - targetCenter.y
|
|
||||||
);
|
|
||||||
|
|
||||||
return !nearest || distance < nearest.distance
|
return !nearest || distance < nearest.distance ? { line: targetLine, distance } : nearest
|
||||||
? { line: targetLine, distance }
|
}, null)?.line
|
||||||
: nearest;
|
|
||||||
}, null)?.line;
|
|
||||||
|
|
||||||
if (!nearestTarget) return sourceLine;
|
if (!nearestTarget) return sourceLine
|
||||||
|
|
||||||
// 방향이 반대인지 확인 (벡터 내적을 사용)
|
// 방향이 반대인지 확인 (벡터 내적을 사용)
|
||||||
const sourceVec = {
|
const sourceVec = {
|
||||||
x: sourceLine.x2 - sourceLine.x1,
|
x: sourceLine.x2 - sourceLine.x1,
|
||||||
y: sourceLine.y2 - sourceLine.y1
|
y: sourceLine.y2 - sourceLine.y1,
|
||||||
};
|
}
|
||||||
const targetVec = {
|
const targetVec = {
|
||||||
x: nearestTarget.x2 - nearestTarget.x1,
|
x: nearestTarget.x2 - nearestTarget.x1,
|
||||||
y: nearestTarget.y2 - nearestTarget.y1
|
y: nearestTarget.y2 - nearestTarget.y1,
|
||||||
};
|
}
|
||||||
|
|
||||||
const dotProduct = sourceVec.x * targetVec.x + sourceVec.y * targetVec.y;
|
const dotProduct = sourceVec.x * targetVec.x + sourceVec.y * targetVec.y
|
||||||
|
|
||||||
// 내적이 음수이면 방향이 반대이므로 뒤집기
|
// 내적이 음수이면 방향이 반대이므로 뒤집기
|
||||||
if (dotProduct < 0) {
|
if (dotProduct < 0) {
|
||||||
@ -700,81 +661,43 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
|||||||
x1: sourceLine.x2,
|
x1: sourceLine.x2,
|
||||||
y1: sourceLine.y2,
|
y1: sourceLine.y2,
|
||||||
x2: sourceLine.x1,
|
x2: sourceLine.x1,
|
||||||
y2: sourceLine.y1
|
y2: sourceLine.y1,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return sourceLine;
|
return sourceLine
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
console.log("wallBaseLines", wall.baseLines)
|
console.log('wallBaseLines', wall.baseLines)
|
||||||
// const sortedWallLines = sortCurrentRoofLines(wall.lines);
|
|
||||||
// roofLines의 방향에 맞춰 currentRoofLines 조정 후 정렬
|
|
||||||
const alignedCurrentRoofLines = alignLineDirection(currentRoofLines, roofLines);
|
|
||||||
const sortedCurrentRoofLines = sortCurrentRoofLines(alignedCurrentRoofLines)
|
|
||||||
// const sortedRoofLines = sortCurrentRoofLines(roofLines);
|
|
||||||
const sortedWallBaseLines = sortCurrentRoofLines(wall.baseLines)
|
|
||||||
// const sortedBaseLines = sortBaseLinesByWallLines(wall.baseLines, wallLines);
|
|
||||||
const sortRoofLines = sortBaseLinesByWallLines(roofLines, wallLines);
|
|
||||||
|
|
||||||
// 원본 wallLines를 복사하여 사용
|
|
||||||
const sortedWallLines = [...wallLines];
|
|
||||||
const sortedBaseLines = sortBaseLinesByWallLines(wall.baseLines, sortedWallLines);
|
|
||||||
const sortedRoofLines = sortBaseLinesByWallLines(roofLines, sortedWallLines);
|
|
||||||
|
|
||||||
//wall.lines 는 기본 벽 라인
|
|
||||||
//wall.baseLine은 움직인라인
|
//wall.baseLine은 움직인라인
|
||||||
const movedLines = []
|
let movedLines = []
|
||||||
|
|
||||||
// 조건에 맞는 라인들만 필터링
|
// 조건에 맞는 라인들만 필터링
|
||||||
const validWallLines = [...wallLines]
|
const validWallLines = [...wallLines].sort((a, b) => a.idx - b.idx).filter((wallLine, index) => wallLine.idx - 1 === index)
|
||||||
.sort((a, b) => a.idx - b.idx)
|
|
||||||
.filter((wallLine, index) => wallLine.idx - 1 === index);
|
|
||||||
|
|
||||||
wallLines.length > 3 && wallLines.forEach((wallLine, index) => {
|
console.log('', sortRoofLines, sortWallLines, sortWallBaseLines)
|
||||||
const originalIndex = wallLines.indexOf(wallLine)
|
sortWallLines.length > 3 &&
|
||||||
const roofLine = sortRoofLines[originalIndex]
|
sortWallLines.forEach((wallLine, index) => {
|
||||||
const currentRoofLine = currentRoofLines[originalIndex]
|
|
||||||
const moveLine = wall.baseLines[originalIndex]
|
|
||||||
const wallBaseLine = wall.baseLines[originalIndex]
|
|
||||||
|
|
||||||
// const roofLine = sortRoofLines[index];
|
const roofLine = sortRoofLines[index]
|
||||||
|
const wallBaseLine = sortWallBaseLines[index]
|
||||||
// if (roofLine.attributes.wallLine !== wallLine.id || (roofLine.idx - 1) !== index) {
|
|
||||||
// console.log("wallLine2::::", wallLine.id)
|
|
||||||
// console.log('roofLine:::', roofLine.attributes.wallLine)
|
|
||||||
// console.log("w:::", wallLine.startPoint, wallLine.endPoint)
|
|
||||||
// console.log("R:::", roofLine.startPoint, roofLine.endPoint)
|
|
||||||
// console.log("not matching roofLine", roofLine);
|
|
||||||
// return false
|
|
||||||
// }//roofLines.find(line => line.attributes.wallLineId === wallLine.attributes.wallId);
|
|
||||||
|
|
||||||
// const currentRoofLine = currentRoofLines[index];
|
|
||||||
// const moveLine = wall.baseLines[index]
|
|
||||||
// const wallBaseLine = wall.baseLines[index]
|
|
||||||
//console.log("wallBaseLine", wallBaseLine);
|
|
||||||
|
|
||||||
//roofline 외곽선 설정
|
//roofline 외곽선 설정
|
||||||
|
|
||||||
const origin = moveLine.attributes?.originPoint
|
|
||||||
if (!origin) return
|
|
||||||
console.log('index::::', index)
|
console.log('index::::', index)
|
||||||
console.log("", roofLines, wallLines, wall.baseLines)
|
|
||||||
console.log('roofLine:', roofLine.x1, roofLine.y1, roofLine.x2, roofLine.y2)
|
console.log('roofLine:', roofLine.x1, roofLine.y1, roofLine.x2, roofLine.y2)
|
||||||
// console.log('moveLine:', moveLine.x1, moveLine.y1, moveLine.x2, moveLine.y2)
|
|
||||||
console.log('wallLine:', wallLine.x1, wallLine.y1, wallLine.x2, wallLine.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('wallBaseLine:', wallBaseLine.x1, wallBaseLine.y1, wallBaseLine.x2, wallBaseLine.y2)
|
||||||
|
console.log('isSamePoint result:', isSameLine2(wallBaseLine, wallLine))
|
||||||
|
|
||||||
|
if (isSameLine2(wallBaseLine, wallLine)) {
|
||||||
console.log('isSamePoint result:', isSameLine2(moveLine, wallLine))
|
|
||||||
|
|
||||||
if (isSameLine2(moveLine, wallLine)) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const movedStart = Math.abs(moveLine.x1 - wallLine.x1) > EPSILON || Math.abs(moveLine.y1 - origin.y1) > EPSILON
|
const movedStart = Math.abs(wallBaseLine.x1 - wallLine.x1) > EPSILON || Math.abs(wallBaseLine.y1 - wallLine.y1) > EPSILON
|
||||||
const movedEnd = Math.abs(moveLine.x2 - wallLine.x2) > EPSILON || Math.abs(moveLine.y2 - origin.y2) > EPSILON
|
const movedEnd = Math.abs(wallBaseLine.x2 - wallLine.x2) > EPSILON || Math.abs(wallBaseLine.y2 - wallLine.y2) > EPSILON
|
||||||
|
|
||||||
const fullyMoved = movedStart && movedEnd
|
const fullyMoved = movedStart && movedEnd
|
||||||
|
|
||||||
@ -787,13 +710,11 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
|||||||
const getAddLine = (p1, p2, stroke = '') => {
|
const getAddLine = (p1, p2, stroke = '') => {
|
||||||
movedLines.push({ index, p1, p2 })
|
movedLines.push({ index, p1, p2 })
|
||||||
|
|
||||||
// Usage:
|
|
||||||
// let mergeLines = mergeMovedLines(movedLines);
|
|
||||||
//console.log("mergeLines:::::::", mergeLines);
|
//console.log("mergeLines:::::::", mergeLines);
|
||||||
const line = new QLine([p1.x, p1.y, p2.x, p2.y], {
|
const line = new QLine([p1.x, p1.y, p2.x, p2.y], {
|
||||||
parentId: roof.id,
|
parentId: roof.id,
|
||||||
fontSize: roof.fontSize,
|
fontSize: roof.fontSize,
|
||||||
stroke: stroke,
|
stroke: 'black',
|
||||||
strokeWidth: 4,
|
strokeWidth: 4,
|
||||||
name: 'eaveHelpLine',
|
name: 'eaveHelpLine',
|
||||||
lineName: 'eaveHelpLine',
|
lineName: 'eaveHelpLine',
|
||||||
@ -846,7 +767,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
|||||||
const positionType = isInPosition ? 'in' : 'out'
|
const positionType = isInPosition ? 'in' : 'out'
|
||||||
|
|
||||||
const condition = `${mLine.position}_${positionType}`
|
const condition = `${mLine.position}_${positionType}`
|
||||||
let isStartEnd = findInteriorPoint(wallBaseLine, sortedBaseLines)
|
let isStartEnd = findInteriorPoint(wallBaseLine, sortWallBaseLines)
|
||||||
let sPoint, ePoint
|
let sPoint, ePoint
|
||||||
if (condition === 'left_in') {
|
if (condition === 'left_in') {
|
||||||
isIn = true
|
isIn = true
|
||||||
@ -863,8 +784,12 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
|||||||
const newPointX = Big(roofLine.x1).plus(moveDist).abs().toNumber()
|
const newPointX = Big(roofLine.x1).plus(moveDist).abs().toNumber()
|
||||||
const pDist = Big(wallLine.x1).minus(roofLine.x1).toNumber()
|
const pDist = Big(wallLine.x1).minus(roofLine.x1).toNumber()
|
||||||
const pLineY = Big(roofLine.y1).minus(0).abs().toNumber()
|
const pLineY = Big(roofLine.y1).minus(0).abs().toNumber()
|
||||||
let idx = 0 > index - 1 ? roofLines.length : index
|
// let idx = 0 > index - 1 ? sortRoofLines.length : index
|
||||||
const pLineX = roofLines[idx - 1].x1
|
// const pLineX = sortRoofLines[idx - 1].x1
|
||||||
|
|
||||||
|
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length
|
||||||
|
const nextIndex = (index + 1) % sortRoofLines.length
|
||||||
|
const pLineX = sortRoofLines[prevIndex].x1
|
||||||
|
|
||||||
getAddLine({ x: newPStart.x, y: newPStart.y }, { x: ePoint.x, y: ePoint.y }, 'blue')
|
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')
|
||||||
@ -887,8 +812,12 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
|||||||
const newPointX = Big(roofLine.x1).plus(moveDist).toNumber()
|
const newPointX = Big(roofLine.x1).plus(moveDist).toNumber()
|
||||||
const pDist = Big(wallLine.x1).minus(roofLine.x1).abs().toNumber()
|
const pDist = Big(wallLine.x1).minus(roofLine.x1).abs().toNumber()
|
||||||
const pLineY = Big(roofLine.y2).minus(0).toNumber()
|
const pLineY = Big(roofLine.y2).minus(0).toNumber()
|
||||||
let idx = roofLines.length < index + 1 ? 0 : index
|
// let idx = sortRoofLines.length < index + 1 ? 0 : index
|
||||||
const pLineX = roofLines[idx + 1].x2
|
// const pLineX = sortRoofLines[idx + 1].x2
|
||||||
|
|
||||||
|
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length
|
||||||
|
const nextIndex = (index + 1) % sortRoofLines.length
|
||||||
|
const pLineX = sortRoofLines[nextIndex].x2
|
||||||
|
|
||||||
getAddLine({ x: newPEnd.x, y: newPEnd.y }, { x: ePoint.x, y: ePoint.y }, 'blue')
|
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')
|
||||||
@ -910,8 +839,12 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
|||||||
const eLineY = Big(bStartY).minus(wallLine.y1).abs().toNumber()
|
const eLineY = Big(bStartY).minus(wallLine.y1).abs().toNumber()
|
||||||
newPStart.y = aStartY
|
newPStart.y = aStartY
|
||||||
newPEnd.y = roofLine.y2 //Big(roofLine.y2).minus(eLineY).toNumber()
|
newPEnd.y = roofLine.y2 //Big(roofLine.y2).minus(eLineY).toNumber()
|
||||||
let idx = 0 >= index - 1 ? roofLines.length : index
|
// let idx = 0 >= index - 1 ? sortRoofLines.length : index
|
||||||
const newLine = roofLines[idx - 1]
|
// const newLine = sortRoofLines[idx - 1]
|
||||||
|
|
||||||
|
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length
|
||||||
|
const nextIndex = (index + 1) % sortRoofLines.length
|
||||||
|
const newLine = sortRoofLines[prevIndex]
|
||||||
|
|
||||||
if (Math.abs(wallBaseLine.y1 - wallLine.y1) < 0.1) {
|
if (Math.abs(wallBaseLine.y1 - wallLine.y1) < 0.1) {
|
||||||
if (inLine) {
|
if (inLine) {
|
||||||
@ -960,8 +893,13 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
|||||||
const eLineY = Big(bStartY).minus(wallLine.y2).abs().toNumber()
|
const eLineY = Big(bStartY).minus(wallLine.y2).abs().toNumber()
|
||||||
newPEnd.y = aStartY
|
newPEnd.y = aStartY
|
||||||
newPStart.y = roofLine.y1 //Big(roofLine.y1).plus(eLineY).toNumber()
|
newPStart.y = roofLine.y1 //Big(roofLine.y1).plus(eLineY).toNumber()
|
||||||
let idx = roofLines.length < index + 1 ? 0 : index
|
// let idx = sortRoofLines.length < index + 1 ? 0 : index
|
||||||
const newLine = roofLines[idx + 1]
|
// const newLine = sortRoofLines[idx + 1]
|
||||||
|
|
||||||
|
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length;
|
||||||
|
const nextIndex = (index + 1) % sortRoofLines.length;
|
||||||
|
const newLine = sortRoofLines[nextIndex]
|
||||||
|
|
||||||
|
|
||||||
if (Math.abs(wallBaseLine.y2 - wallLine.y2) < 0.1) {
|
if (Math.abs(wallBaseLine.y2 - wallLine.y2) < 0.1) {
|
||||||
if (inLine) {
|
if (inLine) {
|
||||||
@ -1015,8 +953,12 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
|||||||
const newPointX = Big(roofLine.x1).minus(moveDist).abs().toNumber()
|
const newPointX = Big(roofLine.x1).minus(moveDist).abs().toNumber()
|
||||||
const pDist = Big(wallLine.x1).minus(roofLine.x1).abs().toNumber()
|
const pDist = Big(wallLine.x1).minus(roofLine.x1).abs().toNumber()
|
||||||
const pLineY = Big(roofLine.y1).minus(0).abs().toNumber()
|
const pLineY = Big(roofLine.y1).minus(0).abs().toNumber()
|
||||||
let idx = 0 >= index - 1 ? roofLines.length : index
|
// let idx = 0 >= index - 1 ? sortRoofLines.length : index
|
||||||
const pLineX = roofLines[idx - 1].x1
|
// const pLineX = sortRoofLines[idx - 1].x1
|
||||||
|
|
||||||
|
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length
|
||||||
|
const nextIndex = (index + 1) % sortRoofLines.length
|
||||||
|
const pLineX = sortRoofLines[prevIndex].x1
|
||||||
|
|
||||||
getAddLine({ x: newPStart.x, y: newPStart.y }, { x: ePoint.x, y: ePoint.y }, 'blue')
|
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')
|
||||||
@ -1039,8 +981,12 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
|||||||
const newPointX = Big(roofLine.x1).minus(moveDist).toNumber()
|
const newPointX = Big(roofLine.x1).minus(moveDist).toNumber()
|
||||||
const pDist = Big(wallLine.x1).minus(roofLine.x1).abs().toNumber()
|
const pDist = Big(wallLine.x1).minus(roofLine.x1).abs().toNumber()
|
||||||
const pLineY = Big(roofLine.y2).minus(0).abs().toNumber()
|
const pLineY = Big(roofLine.y2).minus(0).abs().toNumber()
|
||||||
let idx = roofLines.length < index + 1 ? 0 : index
|
// let idx = sortRoofLines.length < index + 1 ? 0 : index
|
||||||
const pLineX = roofLines[idx + 1].x2
|
// const pLineX = sortRoofLines[idx + 1].x2
|
||||||
|
|
||||||
|
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length
|
||||||
|
const nextIndex = (index + 1) % sortRoofLines.length
|
||||||
|
const pLineX = sortRoofLines[nextIndex].x2
|
||||||
|
|
||||||
getAddLine({ x: newPEnd.x, y: newPEnd.y }, { x: ePoint.x, y: ePoint.y }, 'blue')
|
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')
|
||||||
@ -1063,8 +1009,12 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
|||||||
const eLineY = Big(bStartY).minus(wallLine.y1).abs().toNumber()
|
const eLineY = Big(bStartY).minus(wallLine.y1).abs().toNumber()
|
||||||
newPStart.y = aStartY
|
newPStart.y = aStartY
|
||||||
newPEnd.y = roofLine.y2 //Big(roofLine.y2).plus(eLineY).toNumber()
|
newPEnd.y = roofLine.y2 //Big(roofLine.y2).plus(eLineY).toNumber()
|
||||||
let idx = 0 >= index - 1 ? roofLines.length : index
|
// let idx = 0 >= index - 1 ? sortRoofLines.length : index
|
||||||
const newLine = roofLines[idx - 1]
|
// const newLine = sortRoofLines[idx - 1]
|
||||||
|
|
||||||
|
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length
|
||||||
|
const nextIndex = (index + 1) % sortRoofLines.length
|
||||||
|
const newLine = sortRoofLines[prevIndex]
|
||||||
|
|
||||||
if (Math.abs(wallBaseLine.y1 - wallLine.y1) < 0.1) {
|
if (Math.abs(wallBaseLine.y1 - wallLine.y1) < 0.1) {
|
||||||
if (inLine) {
|
if (inLine) {
|
||||||
@ -1113,8 +1063,13 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
|||||||
const eLineY = Big(bStartY).minus(wallLine.y2).abs().toNumber()
|
const eLineY = Big(bStartY).minus(wallLine.y2).abs().toNumber()
|
||||||
newPEnd.y = aStartY
|
newPEnd.y = aStartY
|
||||||
newPStart.y = roofLine.y1 //Big(roofLine.y1).minus(eLineY).toNumber()
|
newPStart.y = roofLine.y1 //Big(roofLine.y1).minus(eLineY).toNumber()
|
||||||
let idx = roofLines.length < index + 1 ? 0 : index
|
// let idx = sortRoofLines.length < index + 1 ? 0 : index
|
||||||
const newLine = roofLines[idx + 1]
|
// const newLine = sortRoofLines[idx + 1]
|
||||||
|
|
||||||
|
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length;
|
||||||
|
const nextIndex = (index + 1) % sortRoofLines.length;
|
||||||
|
const newLine = sortRoofLines[nextIndex]
|
||||||
|
|
||||||
if (inLine) {
|
if (inLine) {
|
||||||
if (inLine.x2 < inLine.x1) {
|
if (inLine.x2 < inLine.x1) {
|
||||||
getAddLine({ y: bStartY, x: wallLine.x1 }, { y: inLine.y2, x: inLine.x2 }, 'pink')
|
getAddLine({ y: bStartY, x: wallLine.x1 }, { y: inLine.y2, x: inLine.x2 }, 'pink')
|
||||||
@ -1172,7 +1127,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
|||||||
|
|
||||||
const positionType = isInPosition ? 'in' : 'out'
|
const positionType = isInPosition ? 'in' : 'out'
|
||||||
const condition = `${mLine.position}_${positionType}`
|
const condition = `${mLine.position}_${positionType}`
|
||||||
let isStartEnd = findInteriorPoint(wallBaseLine, sortedBaseLines)
|
let isStartEnd = findInteriorPoint(wallBaseLine, sortWallBaseLines)
|
||||||
|
|
||||||
let sPoint, ePoint
|
let sPoint, ePoint
|
||||||
|
|
||||||
@ -1186,8 +1141,14 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
|||||||
|
|
||||||
const pDist = Big(wallLine.y2).minus(roofLine.y2).abs().toNumber()
|
const pDist = Big(wallLine.y2).minus(roofLine.y2).abs().toNumber()
|
||||||
const pLineX = Big(roofLine.x1).minus(0).toNumber()
|
const pLineX = Big(roofLine.x1).minus(0).toNumber()
|
||||||
let idx = 0 >= index - 1 ? roofLines.length : index
|
// let idx = 0 >= index - 1 ? sortRoofLines.length : index
|
||||||
const pLineY = roofLines[idx - 1].y1
|
// const pLineY = sortRoofLines[idx - 1].y1
|
||||||
|
|
||||||
|
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length
|
||||||
|
const nextIndex = (index + 1) % sortRoofLines.length
|
||||||
|
const pLineY = sortRoofLines[prevIndex].y1
|
||||||
|
|
||||||
|
|
||||||
getAddLine({ x: newPStart.x, y: newPStart.y }, { x: sPoint.x, y: sPoint.y }, 'blue')
|
getAddLine({ x: newPStart.x, y: newPStart.y }, { x: sPoint.x, y: sPoint.y }, 'blue')
|
||||||
findPoints.push({ x: sPoint.x, y: sPoint.y, position: 'top_in_start' })
|
findPoints.push({ x: sPoint.x, y: sPoint.y, position: 'top_in_start' })
|
||||||
|
|
||||||
@ -1207,8 +1168,14 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
|||||||
|
|
||||||
const pDist = Big(wallLine.y1).minus(roofLine.y1).abs().toNumber()
|
const pDist = Big(wallLine.y1).minus(roofLine.y1).abs().toNumber()
|
||||||
const pLineX = Big(roofLine.x2).minus(0).abs().toNumber()
|
const pLineX = Big(roofLine.x2).minus(0).abs().toNumber()
|
||||||
let idx = roofLines.length < index + 1 ? 0 : index
|
|
||||||
const pLineY = roofLines[idx + 1].y2
|
// let idx = sortRoofLines.length < index + 1 ? 0 : index
|
||||||
|
// const pLineY = sortRoofLines[idx + 1].y2
|
||||||
|
|
||||||
|
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length
|
||||||
|
const nextIndex = (index + 1) % sortRoofLines.length
|
||||||
|
const pLineY = sortRoofLines[nextIndex].y2
|
||||||
|
|
||||||
getAddLine({ x: newPEnd.x, y: newPEnd.y }, { x: sPoint.x, y: sPoint.y }, 'blue')
|
getAddLine({ x: newPEnd.x, y: newPEnd.y }, { x: sPoint.x, y: sPoint.y }, 'blue')
|
||||||
findPoints.push({ x: sPoint.x, y: sPoint.y, position: 'top_in_end' })
|
findPoints.push({ x: sPoint.x, y: sPoint.y, position: 'top_in_end' })
|
||||||
|
|
||||||
@ -1231,8 +1198,12 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
|||||||
const eLineX = Big(bStartX).minus(wallLine.x1).abs().toNumber()
|
const eLineX = Big(bStartX).minus(wallLine.x1).abs().toNumber()
|
||||||
newPEnd.x = roofLine.x2 //Big(newPEnd.x).plus(eLineX).toNumber()
|
newPEnd.x = roofLine.x2 //Big(newPEnd.x).plus(eLineX).toNumber()
|
||||||
newPStart.x = aStartX
|
newPStart.x = aStartX
|
||||||
let idx = 0 > index - 1 ? roofLines.length : index
|
// let idx = 0 > index - 1 ? sortRoofLines.length : index
|
||||||
const newLine = roofLines[idx - 1]
|
// const newLine = sortRoofLines[idx - 1]
|
||||||
|
|
||||||
|
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length;
|
||||||
|
const nextIndex = (index + 1) % sortRoofLines.length;
|
||||||
|
const newLine = sortRoofLines[prevIndex]
|
||||||
|
|
||||||
if (Math.abs(wallBaseLine.x1 - wallLine.x1) < 0.1) {
|
if (Math.abs(wallBaseLine.x1 - wallLine.x1) < 0.1) {
|
||||||
if (inLine) {
|
if (inLine) {
|
||||||
@ -1279,8 +1250,12 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
|||||||
const eLineX = Big(bStartX).minus(wallLine.x2).abs().toNumber()
|
const eLineX = Big(bStartX).minus(wallLine.x2).abs().toNumber()
|
||||||
newPStart.x = roofLine.x1 //Big(newPStart.x).minus(eLineX).abs().toNumber()
|
newPStart.x = roofLine.x1 //Big(newPStart.x).minus(eLineX).abs().toNumber()
|
||||||
newPEnd.x = aStartX
|
newPEnd.x = aStartX
|
||||||
let idx = roofLines.length < index + 1 ? 0 : index
|
// let idx = sortRoofLines.length < index + 1 ? 0 : index
|
||||||
const newLine = roofLines[idx + 1]
|
// const newLine = sortRoofLines[idx + 1]
|
||||||
|
|
||||||
|
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length;
|
||||||
|
const nextIndex = (index + 1) % sortRoofLines.length;
|
||||||
|
const newLine = sortRoofLines[nextIndex]
|
||||||
|
|
||||||
if (Math.abs(wallBaseLine.x2 - wallLine.x2) < 0.1) {
|
if (Math.abs(wallBaseLine.x2 - wallLine.x2) < 0.1) {
|
||||||
if (inLine) {
|
if (inLine) {
|
||||||
@ -1329,8 +1304,14 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
|||||||
|
|
||||||
const pDist = Big(wallLine.y2).minus(roofLine.y2).abs().toNumber()
|
const pDist = Big(wallLine.y2).minus(roofLine.y2).abs().toNumber()
|
||||||
const pLineX = Big(roofLine.x1).minus(0).abs().toNumber()
|
const pLineX = Big(roofLine.x1).minus(0).abs().toNumber()
|
||||||
let idx = 0 > index - 1 ? roofLines.length : index
|
|
||||||
const pLineY = roofLines[idx - 1].y1
|
// let idx = 0 > index - 1 ? sortRoofLines.length : index
|
||||||
|
// const pLineY = sortRoofLines[idx - 1].y1
|
||||||
|
|
||||||
|
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length
|
||||||
|
const nextIndex = (index + 1) % sortRoofLines.length
|
||||||
|
const pLineY = sortRoofLines[prevIndex].y1
|
||||||
|
|
||||||
getAddLine({ x: newPStart.x, y: newPStart.y }, { x: sPoint.x, y: sPoint.y }, 'blue')
|
getAddLine({ x: newPStart.x, y: newPStart.y }, { x: sPoint.x, y: sPoint.y }, 'blue')
|
||||||
findPoints.push({ x: sPoint.x, y: sPoint.y, position: 'bottom_in_start' })
|
findPoints.push({ x: sPoint.x, y: sPoint.y, position: 'bottom_in_start' })
|
||||||
|
|
||||||
@ -1350,8 +1331,15 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
|||||||
|
|
||||||
const pDist = Big(wallLine.y1).minus(roofLine.y1).abs().toNumber()
|
const pDist = Big(wallLine.y1).minus(roofLine.y1).abs().toNumber()
|
||||||
const pLineX = Big(roofLine.x2).minus(0).abs().toNumber()
|
const pLineX = Big(roofLine.x2).minus(0).abs().toNumber()
|
||||||
let idx = roofLines.length < index + 1 ? 0 : index
|
|
||||||
const pLineY = roofLines[idx + 1].y2
|
// let idx = sortRoofLines.length < index + 1 ? 0 : index
|
||||||
|
// const pLineY = sortRoofLines[idx + 1].y2
|
||||||
|
|
||||||
|
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length
|
||||||
|
const nextIndex = (index + 1) % sortRoofLines.length
|
||||||
|
const pLineY = sortRoofLines[nextIndex].y2
|
||||||
|
|
||||||
|
|
||||||
getAddLine({ x: newPEnd.x, y: newPEnd.y }, { x: sPoint.x, y: sPoint.y }, 'blue')
|
getAddLine({ x: newPEnd.x, y: newPEnd.y }, { x: sPoint.x, y: sPoint.y }, 'blue')
|
||||||
findPoints.push({ x: sPoint.x, y: sPoint.y, position: 'bottom_in_end' })
|
findPoints.push({ x: sPoint.x, y: sPoint.y, position: 'bottom_in_end' })
|
||||||
|
|
||||||
@ -1372,8 +1360,12 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
|||||||
const eLineX = Big(bStartX).minus(wallLine.x1).abs().toNumber()
|
const eLineX = Big(bStartX).minus(wallLine.x1).abs().toNumber()
|
||||||
newPEnd.x = roofLine.x2 //Big(roofLine.x2).minus(eLineX).toNumber()
|
newPEnd.x = roofLine.x2 //Big(roofLine.x2).minus(eLineX).toNumber()
|
||||||
newPStart.x = aStartX
|
newPStart.x = aStartX
|
||||||
let idx = 0 > index - 1 ? roofLines.length : index
|
// let idx = 0 > index - 1 ? sortRoofLines.length : index
|
||||||
const newLine = roofLines[idx - 1]
|
// const newLine = sortRoofLines[idx - 1]
|
||||||
|
|
||||||
|
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length;
|
||||||
|
const nextIndex = (index + 1) % sortRoofLines.length;
|
||||||
|
const newLine = sortRoofLines[prevIndex]
|
||||||
|
|
||||||
if (Math.abs(wallBaseLine.x1 - wallLine.x1) < 0.1) {
|
if (Math.abs(wallBaseLine.x1 - wallLine.x1) < 0.1) {
|
||||||
if (inLine) {
|
if (inLine) {
|
||||||
@ -1422,8 +1414,12 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
|||||||
const eLineX = Big(bStartX).minus(wallLine.x2).abs().toNumber()
|
const eLineX = Big(bStartX).minus(wallLine.x2).abs().toNumber()
|
||||||
newPEnd.x = aStartX
|
newPEnd.x = aStartX
|
||||||
newPStart.x = roofLine.x1 //Big(roofLine.x1).plus(eLineX).toNumber()
|
newPStart.x = roofLine.x1 //Big(roofLine.x1).plus(eLineX).toNumber()
|
||||||
let idx = roofLines.length < index + 1 ? 0 : index
|
// let idx = sortRoofLines.length < index + 1 ? 0 : index
|
||||||
const newLine = roofLines[idx + 1]
|
// const newLine = sortRoofLines[idx + 1]
|
||||||
|
|
||||||
|
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length;
|
||||||
|
const nextIndex = (index + 1) % sortRoofLines.length;
|
||||||
|
const newLine = sortRoofLines[nextIndex]
|
||||||
|
|
||||||
if (Math.abs(wallBaseLine.x2 - wallLine.x2) < 0.1) {
|
if (Math.abs(wallBaseLine.x2 - wallLine.x2) < 0.1) {
|
||||||
if (inLine) {
|
if (inLine) {
|
||||||
@ -1473,7 +1469,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
canvas.renderAll()
|
canvas.renderAll()
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
getMoveUpDownLine()
|
getMoveUpDownLine()
|
||||||
|
|
||||||
@ -1766,6 +1762,115 @@ const isSameLine = (edgeStartX, edgeStartY, edgeEndX, edgeEndY, baseLine) => {
|
|||||||
|
|
||||||
// --- Disconnected Line Processing ---
|
// --- Disconnected Line Processing ---
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 라인들이 반시계 방향이 되도록 정렬하고, 왼쪽 상단에서 시작하는 새 배열 반환
|
||||||
|
* @param {Array} lines - x1, y1, x2, y2 속성을 가진 라인 객체 배열
|
||||||
|
* @returns {Array} 반시계 방향으로 정렬된 새 라인 배열
|
||||||
|
*/
|
||||||
|
export function ensureCounterClockwiseLines(lines) {
|
||||||
|
if (!lines || lines.length < 3) return [...(lines || [])];
|
||||||
|
|
||||||
|
// 1. 모든 점을 연결 그래프로 구성
|
||||||
|
const graph = new Map();
|
||||||
|
|
||||||
|
// 각 점에서 연결된 점들을 저장
|
||||||
|
lines.forEach(line => {
|
||||||
|
const p1 = `${line.x1},${line.y1}`;
|
||||||
|
const p2 = `${line.x2},${line.y2}`;
|
||||||
|
|
||||||
|
if (!graph.has(p1)) graph.set(p1, []);
|
||||||
|
if (!graph.has(p2)) graph.set(p2, []);
|
||||||
|
|
||||||
|
// 양방향 연결
|
||||||
|
graph.get(p1).push({ x: line.x2, y: line.y2, line });
|
||||||
|
graph.get(p2).push({ x: line.x1, y: line.y1, line });
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. 왼쪽 상단 점 찾기
|
||||||
|
let startPoint = null;
|
||||||
|
let minY = Infinity;
|
||||||
|
let minX = Infinity;
|
||||||
|
|
||||||
|
for (const [pointStr] of graph) {
|
||||||
|
const [x, y] = pointStr.split(',').map(Number);
|
||||||
|
if (y < minY || (y === minY && x < minX)) {
|
||||||
|
minY = y;
|
||||||
|
minX = x;
|
||||||
|
startPoint = { x, y };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!startPoint) return [...lines];
|
||||||
|
|
||||||
|
// 3. 점들을 순회하며 라인 구성
|
||||||
|
const visited = new Set();
|
||||||
|
const result = [];
|
||||||
|
let current = `${startPoint.x},${startPoint.y}`;
|
||||||
|
let prev = null;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (visited.has(current)) break;
|
||||||
|
visited.add(current);
|
||||||
|
|
||||||
|
const neighbors = graph.get(current) || [];
|
||||||
|
if (neighbors.length === 0) break;
|
||||||
|
|
||||||
|
// 이전 점 제외
|
||||||
|
const nextPoints = neighbors.filter(n =>
|
||||||
|
!prev || `${n.x},${n.y}` !== `${prev.x},${prev.y}`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (nextPoints.length === 0) break;
|
||||||
|
|
||||||
|
// 각도가 가장 작은(반시계 방향) 이웃 선택
|
||||||
|
const [cx, cy] = current.split(',').map(Number);
|
||||||
|
const next = nextPoints.reduce((best, curr) => {
|
||||||
|
const angleBest = Math.atan2(best.y - cy, best.x - cx);
|
||||||
|
const angleCurr = Math.atan2(curr.y - cy, curr.x - cx);
|
||||||
|
return angleCurr > angleBest ? curr : best;
|
||||||
|
}, nextPoints[0]);
|
||||||
|
|
||||||
|
// 라인 추가 (방향 유지)
|
||||||
|
const line = next.line;
|
||||||
|
const isReversed = (line.x1 !== next.x || line.y1 !== next.y);
|
||||||
|
|
||||||
|
result.push({
|
||||||
|
...line,
|
||||||
|
x1: isReversed ? line.x2 : line.x1,
|
||||||
|
y1: isReversed ? line.y2 : line.y1,
|
||||||
|
x2: isReversed ? line.x1 : line.x2,
|
||||||
|
y2: isReversed ? line.y1 : line.y2,
|
||||||
|
idx: result.length
|
||||||
|
});
|
||||||
|
|
||||||
|
prev = { x: cx, y: cy };
|
||||||
|
current = `${next.x},${next.y}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 시계 방향이면 뒤집기
|
||||||
|
let area = 0;
|
||||||
|
for (let i = 0; i < result.length; i++) {
|
||||||
|
const current = result[i];
|
||||||
|
const next = result[(i + 1) % result.length];
|
||||||
|
area += (next.x1 - current.x1) * (next.y1 + current.y1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (area > 0) {
|
||||||
|
return result.reverse().map((line, idx) => ({
|
||||||
|
...line,
|
||||||
|
x1: line.x2,
|
||||||
|
y1: line.y2,
|
||||||
|
x2: line.x1,
|
||||||
|
y2: line.y1,
|
||||||
|
idx
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 점을 선분에 투영한 점의 좌표를 반환합니다.
|
* 점을 선분에 투영한 점의 좌표를 반환합니다.
|
||||||
* @param {object} point - 투영할 점 {x, y}
|
* @param {object} point - 투영할 점 {x, y}
|
||||||
@ -2992,17 +3097,32 @@ function pointToLineDistance(point, lineP1, lineP2) {
|
|||||||
|
|
||||||
|
|
||||||
const getOrientation = (line, eps = 0.1) => {
|
const getOrientation = (line, eps = 0.1) => {
|
||||||
const x1 = line.get('x1')
|
if (!line) {
|
||||||
const y1 = line.get('y1')
|
console.error('line 객체가 유효하지 않습니다:', line);
|
||||||
const x2 = line.get('x2')
|
return null; // 또는 적절한 기본값 반환
|
||||||
const y2 = line.get('y2')
|
}
|
||||||
const dx = Math.abs(x2 - x1)
|
|
||||||
const dy = Math.abs(y2 - y1)
|
|
||||||
|
|
||||||
if (dx < eps && dy >= eps) return 'vertical'
|
// get 메서드가 있으면 사용하고, 없으면 직접 프로퍼티에 접근
|
||||||
if (dy < eps && dx >= eps) return 'horizontal'
|
const getValue = (obj, key) =>
|
||||||
if (dx < eps && dy < eps) return 'point'
|
obj && typeof obj.get === 'function' ? obj.get(key) : obj[key];
|
||||||
return 'diagonal'
|
|
||||||
|
try {
|
||||||
|
const x1 = getValue(line, 'x1');
|
||||||
|
const y1 = getValue(line, 'y1');
|
||||||
|
const x2 = getValue(line, 'x2');
|
||||||
|
const y2 = getValue(line, 'y2');
|
||||||
|
|
||||||
|
const dx = Math.abs(x2 - x1);
|
||||||
|
const dy = Math.abs(y2 - y1);
|
||||||
|
|
||||||
|
if (dx < eps && dy >= eps) return 'vertical';
|
||||||
|
if (dy < eps && dx >= eps) return 'horizontal';
|
||||||
|
if (dx < eps && dy < eps) return 'point';
|
||||||
|
return 'diagonal';
|
||||||
|
} catch (e) {
|
||||||
|
console.error('방향 계산 중 오류 발생:', e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -3371,102 +3491,3 @@ function findInteriorPoint(line, polygonLines) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* baseLines의 순서를 wallLines의 순서와 일치시킵니다.
|
|
||||||
* 1순위: 공통 ID(id, matchingId, parentId 등)를 이용한 직접 매칭
|
|
||||||
* 2순위: 기하학적 유사성(기울기, 길이, 위치)을 점수화하여 매칭
|
|
||||||
*
|
|
||||||
* @param {Array} baseLines - 정렬할 원본 baseLine 배열
|
|
||||||
* @param {Array} wallLines - 기준이 되는 wallLine 배열
|
|
||||||
* @returns {Array} wallLines 순서에 맞춰 정렬된 baseLines
|
|
||||||
*/
|
|
||||||
export const sortBaseLinesByWallLines = (baseLines, wallLines) => {
|
|
||||||
if (!baseLines || !wallLines || baseLines.length === 0 || wallLines.length === 0) {
|
|
||||||
return baseLines;
|
|
||||||
}
|
|
||||||
|
|
||||||
const sortedBaseLines = new Array(wallLines.length).fill(null);
|
|
||||||
const usedBaseIndices = new Set();
|
|
||||||
|
|
||||||
// [1단계] ID 매칭 (기존 로직 유지 - 혹시 ID가 있는 경우를 대비)
|
|
||||||
// ... (ID 매칭 코드는 생략하거나 유지) ...
|
|
||||||
|
|
||||||
// [2단계] 'originPoint' 또는 좌표 일치성을 이용한 강력한 기하학적 매칭
|
|
||||||
wallLines.forEach((wLine, wIndex) => {
|
|
||||||
if (sortedBaseLines[wIndex]) return;
|
|
||||||
|
|
||||||
// 비교할 기준 좌표 설정 (originPoint가 있으면 그것을, 없으면 현재 좌표 사용)
|
|
||||||
const wStart = wLine.attributes?.originPoint
|
|
||||||
? { x: wLine.attributes.originPoint.x1, y: wLine.attributes.originPoint.y1 }
|
|
||||||
: { x: wLine.x1, y: wLine.y1 };
|
|
||||||
|
|
||||||
const wEnd = wLine.attributes?.originPoint
|
|
||||||
? { x: wLine.attributes.originPoint.x2, y: wLine.attributes.originPoint.y2 }
|
|
||||||
: { x: wLine.x2, y: wLine.y2 };
|
|
||||||
|
|
||||||
// 수직/수평 여부 판단
|
|
||||||
const isVertical = Math.abs(wStart.x - wEnd.x) < 0.1;
|
|
||||||
const isHorizontal = Math.abs(wStart.y - wEnd.y) < 0.1;
|
|
||||||
|
|
||||||
let bestMatchIndex = -1;
|
|
||||||
let minDiff = Infinity;
|
|
||||||
|
|
||||||
baseLines.forEach((bLine, bIndex) => {
|
|
||||||
if (usedBaseIndices.has(bIndex)) return;
|
|
||||||
|
|
||||||
let diff = Infinity;
|
|
||||||
|
|
||||||
// 1. 수직선인 경우: X좌표가 일치해야 함 (예: 230.8 == 230.8)
|
|
||||||
if (isVertical) {
|
|
||||||
// bLine도 수직선인지 확인 (x1, x2 차이가 거의 없어야 함)
|
|
||||||
if (Math.abs(bLine.x1 - bLine.x2) < 1.0) {
|
|
||||||
// X좌표 차이를 오차(diff)로 계산
|
|
||||||
diff = Math.abs(wStart.x - bLine.x1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 2. 수평선인 경우: Y좌표가 일치해야 함
|
|
||||||
else if (isHorizontal) {
|
|
||||||
// bLine도 수평선인지 확인
|
|
||||||
if (Math.abs(bLine.y1 - bLine.y2) < 1.0) {
|
|
||||||
diff = Math.abs(wStart.y - bLine.y1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 3. 대각선인 경우: 기울기와 절편 비교 (복잡하므로 거리로 대체)
|
|
||||||
else {
|
|
||||||
// 중점 간 거리 + 기울기 차이
|
|
||||||
// (이전 답변의 로직 사용 가능)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 오차가 매우 작으면(예: 1px 미만) 같은 라인으로 간주
|
|
||||||
if (diff < 1.0 && diff < minDiff) {
|
|
||||||
minDiff = diff;
|
|
||||||
bestMatchIndex = bIndex;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (bestMatchIndex !== -1) {
|
|
||||||
sortedBaseLines[wIndex] = baseLines[bestMatchIndex];
|
|
||||||
usedBaseIndices.add(bestMatchIndex);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// [3단계] 남은 라인 처리 (Fallback)
|
|
||||||
// 매칭되지 않은 wallLine들에 대해 남은 baseLines를 순서대로 배정하거나
|
|
||||||
// 거리 기반 근사 매칭을 수행
|
|
||||||
// ... (기존 fallback 로직) ...
|
|
||||||
|
|
||||||
// 빈 구멍 채우기 (null 방지)
|
|
||||||
for(let i=0; i<sortedBaseLines.length; i++) {
|
|
||||||
if(!sortedBaseLines[i]) {
|
|
||||||
const unused = baseLines.findIndex((_, idx) => !usedBaseIndices.has(idx));
|
|
||||||
if(unused !== -1) {
|
|
||||||
sortedBaseLines[i] = baseLines[unused];
|
|
||||||
usedBaseIndices.add(unused);
|
|
||||||
} else {
|
|
||||||
sortedBaseLines[i] = baseLines[0]; // 최후의 수단
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return sortedBaseLines;
|
|
||||||
};
|
|
||||||
Loading…
x
Reference in New Issue
Block a user