diff --git a/src/components/common/input/CalcInput.jsx b/src/components/common/input/CalcInput.jsx index 17af010e..d386bd7d 100644 --- a/src/components/common/input/CalcInput.jsx +++ b/src/components/common/input/CalcInput.jsx @@ -3,7 +3,7 @@ import { createCalculator } from '@/util/calc-utils' import '@/styles/calc.scss' export const CalculatorInput = forwardRef( - ({ value, onChange, label, options = {}, id, className = 'calculator-input', readOnly = false, placeholder, name='', disabled = false }, ref) => { + ({ value, onChange, label, options = {}, id, className = 'calculator-input', readOnly = false, placeholder, name='', disabled = false, maxLength = 6 }, ref) => { const [showKeypad, setShowKeypad] = useState(false) const [displayValue, setDisplayValue] = useState(value || '0') const [hasOperation, setHasOperation] = useState(false) @@ -48,28 +48,56 @@ export const CalculatorInput = forwardRef( const calculator = calculatorRef.current let newDisplayValue = '' + // maxLength 체크 + if (maxLength > 0) { + const currentLength = (calculator.currentOperand || '').length + (calculator.previousOperand || '').length + (calculator.operation || '').length + if (currentLength >= maxLength) { + return + } + } + // 소수점 이하 2자리 제한 로직 추가 const shouldPreventInput = (value) => { - const decimalParts = (value || '').split('.') + if (!value) return false + const decimalParts = value.toString().split('.') return decimalParts.length > 1 && decimalParts[1].length >= 2 } + // 숫자 추가 함수 + const appendNumber = (current, num) => { + // maxLength 체크 + if (maxLength > 0 && (current + num).length > maxLength) { + return current + } + // 현재 값이 0이고 소수점이 없을 때 0이 아닌 숫자를 입력하면 대체 + if (current === '0' && num !== '.' && !current.includes('.')) { + return num.toString() + } + // 0. 다음에 0을 입력하는 경우 허용 + if (current === '0' && num === '0') { + return '0.' + } + return current + num + } + if (hasOperation) { // 연산자 이후 숫자 입력 시 - if (calculator.currentOperand === '0' || calculator.shouldResetDisplay) { + if (calculator.shouldResetDisplay) { calculator.currentOperand = num.toString() calculator.shouldResetDisplay = false - }else if (!shouldPreventInput(calculator.currentOperand)) { //소수점 이하2자리 - calculator.currentOperand = (calculator.currentOperand || '') + num + } else if (num === '.') { + if (!calculator.currentOperand.includes('.')) { + calculator.currentOperand = calculator.currentOperand || '0' + '.' + } + } else if (!shouldPreventInput(calculator.currentOperand)) { + calculator.currentOperand = appendNumber(calculator.currentOperand || '0', num) } - // else { - // calculator.currentOperand = (calculator.currentOperand || '') + num - // } + newDisplayValue = calculator.previousOperand + calculator.operation + calculator.currentOperand setDisplayValue(newDisplayValue) } else { // 첫 번째 숫자 입력 시 - if (displayValue === '0' || calculator.shouldResetDisplay) { + if (calculator.shouldResetDisplay) { calculator.currentOperand = num.toString() calculator.shouldResetDisplay = false newDisplayValue = calculator.currentOperand @@ -77,8 +105,17 @@ export const CalculatorInput = forwardRef( if (!hasOperation) { onChange(calculator.currentOperand) } - } else if (!shouldPreventInput(calculator.currentOperand)) { //소수점 이하2자리 - calculator.currentOperand = (calculator.currentOperand || '') + num + } else if (num === '.') { + if (!calculator.currentOperand.includes('.')) { + calculator.currentOperand = (calculator.currentOperand || '0') + '.' + newDisplayValue = calculator.currentOperand + setDisplayValue(newDisplayValue) + if (!hasOperation) { + onChange(newDisplayValue) + } + } + } else if (!shouldPreventInput(calculator.currentOperand)) { + calculator.currentOperand = appendNumber(calculator.currentOperand || '0', num) newDisplayValue = calculator.currentOperand setDisplayValue(newDisplayValue) if (!hasOperation) { @@ -382,6 +419,7 @@ export const CalculatorInput = forwardRef( placeholder={placeholder} autoComplete={'off'} disabled={disabled} + maxLength={maxLength} /> {showKeypad && !readOnly && ( diff --git a/src/components/community/Qna.jsx b/src/components/community/Qna.jsx index 033f7424..f13a8639 100644 --- a/src/components/community/Qna.jsx +++ b/src/components/community/Qna.jsx @@ -57,6 +57,7 @@ export default function Qna() { endRow : endRow, schMainYn : 'N', siteTpCd : 'QC', + langCd : 'JA', }) const apiUrl = `${url}?${params.toString()}` diff --git a/src/components/estimate/Estimate.jsx b/src/components/estimate/Estimate.jsx index d8b127fc..a8f5f148 100644 --- a/src/components/estimate/Estimate.jsx +++ b/src/components/estimate/Estimate.jsx @@ -24,6 +24,7 @@ import { useSwal } from '@/hooks/useSwal' import { QcastContext } from '@/app/QcastProvider' import { useCanvasMenu } from '@/hooks/common/useCanvasMenu' import {normalizeDigits, normalizeDecimal} from '@/util/input-utils' +import { CalculatorInput } from '@/components/common/input/CalcInput' export default function Estimate({}) { const [uniqueData, setUniqueData] = useState([]) const [handlePricingFlag, setHandlePricingFlag] = useState(false) @@ -1693,13 +1694,13 @@ export default function Estimate({}) { {/* 파일첨부 끝 */} {/* 견적특이사항 시작 */}
-
-

{getMessage('estimate.detail.header.specialEstimate')}

-
- - -
-
+ {/*
*/} + {/*

{getMessage('estimate.detail.header.specialEstimate')}

*/} + {/*
*/} + {/* */} + {/* */} + {/*
*/} + {/*
*/}
{/* 견적 특이사항 코드영역시작 */}
@@ -2106,25 +2107,60 @@ export default function Estimate({}) {
- {*/} + {/* onChangeAmount(e.target.value, item.dispOrder, index)*/} + {/* }}*/} + {/* maxLength={6}*/} + {/*/>*/} + { - onChangeAmount(e.target.value, item.dispOrder, index) + onChange={(value) =>{ + onChangeAmount(value, item.dispOrder, index) }} - maxLength={6} - /> + options={{ + allowNegative: false, + allowDecimal: false + }} + />
{item.unit}
- {*/} + {/* onChangeSalePrice(e.target.value, item.dispOrder, index)*/} + {/* }}*/} + {/* maxLength={12}*/} + {/*/>*/} + { - onChangeSalePrice(e.target.value, item.dispOrder, index) + onChange={(value) =>{ + onChangeSalePrice(value, item.dispOrder, index) }} maxLength={12} + options={{ + allowNegative: false, + allowDecimal: false + }} />
{item.openFlg === '1' && ( diff --git a/src/components/floor-plan/modal/basic/step/Module.jsx b/src/components/floor-plan/modal/basic/step/Module.jsx index 426724eb..a708f749 100644 --- a/src/components/floor-plan/modal/basic/step/Module.jsx +++ b/src/components/floor-plan/modal/basic/step/Module.jsx @@ -235,11 +235,23 @@ export default function Module({ setTabNum }) {
- setInputVerticalSnowCover(normalizeDecimal(e.target.value))}*/} + {/*/>*/} + setInputVerticalSnowCover(normalizeDecimal(e.target.value))} + onChange={(value) => setInputVerticalSnowCover(value)} + options={{ + allowNegative: false, + allowDecimal: false + }} />
cm diff --git a/src/components/floor-plan/modal/basic/step/Orientation.jsx b/src/components/floor-plan/modal/basic/step/Orientation.jsx index fdf599be..4feeb41b 100644 --- a/src/components/floor-plan/modal/basic/step/Orientation.jsx +++ b/src/components/floor-plan/modal/basic/step/Orientation.jsx @@ -638,7 +638,7 @@ export const Orientation = forwardRef((props, ref) => { name="" label="" className="input-origin block" - value={inputInstallHeight} + value={inputVerticalSnowCover} onChange={(value) => handleChangeVerticalSnowCover(value)} options={{ allowNegative: false, diff --git a/src/components/management/StuffDetail.jsx b/src/components/management/StuffDetail.jsx index dfcc547c..cc9d71ea 100644 --- a/src/components/management/StuffDetail.jsx +++ b/src/components/management/StuffDetail.jsx @@ -2125,12 +2125,12 @@ export default function StuffDetail() { {/* {...register('verticalSnowCover')}*/} {/*/>*/} {}} + onChange={(value) => form.setValue('verticalSnowCover', value)} options={{ allowNegative: false, allowDecimal: false @@ -2197,12 +2197,12 @@ export default function StuffDetail() { {/* {...register('installHeight')}*/} {/*/>*/} {}} + onChange={(value) => form.setValue('installHeight', value)} options={{ allowNegative: false, allowDecimal: false @@ -2721,12 +2721,12 @@ export default function StuffDetail() { {/* {...register('verticalSnowCover')}*/} {/*/>*/} {}} + onChange={(value) => form.setValue('verticalSnowCover', value)} options={{ allowNegative: false, allowDecimal: false @@ -2797,12 +2797,12 @@ export default function StuffDetail() { {/*/>*/} {}} + onChange={(value) => form.setValue('installHeight', value)} options={{ allowNegative: false, allowDecimal: false diff --git a/src/hooks/roofcover/useMovementSetting.js b/src/hooks/roofcover/useMovementSetting.js index 795086fb..e74a59e3 100644 --- a/src/hooks/roofcover/useMovementSetting.js +++ b/src/hooks/roofcover/useMovementSetting.js @@ -631,7 +631,7 @@ export function useMovementSetting(id) { } return Big(0); // 기본값으로 0 반환 또는 다른 적절한 기본값 })(); - if (target.y1 === target.y2) { + if (Math.abs(target.y1 - target.y2) < 0.5) { value = value.neg() } } else { diff --git a/src/hooks/roofcover/useRoofAllocationSetting.js b/src/hooks/roofcover/useRoofAllocationSetting.js index f93e230d..3ebaa56c 100644 --- a/src/hooks/roofcover/useRoofAllocationSetting.js +++ b/src/hooks/roofcover/useRoofAllocationSetting.js @@ -473,7 +473,20 @@ export function useRoofAllocationSetting(id) { // 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)) - roofBase.lines = [...newEaveLines] + // Filter out lines from roofBase.lines that share any points with newEaveLines + const linesToKeep = roofBase.lines.filter(roofLine => { + return !newEaveLines.some(eaveLine => { + // Check if any endpoint of roofLine matches any endpoint of eaveLine + return ( + // Check if any endpoint of roofLine matches any endpoint of eaveLine + (Math.abs(roofLine.x1 - eaveLine.x1) < 0.1 && Math.abs(roofLine.y1 - eaveLine.y1) < 0.1) || // p1 matches p1 + (Math.abs(roofLine.x2 - eaveLine.x2) < 0.1 && Math.abs(roofLine.y2 - eaveLine.y2) < 0.1) // p2 matches p2 + ); + }); + }); + +// Combine remaining lines with newEaveLines + roofBase.lines = [...linesToKeep, ...newEaveLines]; } else { roofBase.lines = [...roofEaveHelpLines] } diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index f8c011f0..3811e438 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -2591,6 +2591,9 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { case LINE_TYPE.WALLLINE.JERKINHEAD: offset = oppLine.attributes.width / 2 break + case LINE_TYPE.WALLLINE.WALL: + offset = 0 + break default: break } @@ -2936,6 +2939,9 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { case LINE_TYPE.WALLLINE.JERKINHEAD: offset = oppLine.attributes.width / 2 break + case LINE_TYPE.WALLLINE.WALL: + offset = 0 + break default: break } @@ -3396,6 +3402,9 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { case LINE_TYPE.WALLLINE.JERKINHEAD: offset = oppLine.attributes.width / 2 break + case LINE_TYPE.WALLLINE.WALL: + offset = 0 + break default: break } @@ -3515,7 +3524,7 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { const oMidX = (oppLine.x1 + oppLine.x2) / 2 const oMidY = (oppLine.y1 + oppLine.y2) / 2 const cOffset = currentLine.attributes.offset - const oOffset = oppLine.attributes.offset + const oOffset = oppLine.attributes.type === LINE_TYPE.WALLLINE.WALL ? 0 : oppLine.attributes.offset const ridgeVector = { x: Math.sign(clamp01(cMidX - oMidX)), y: Math.sign(clamp01(cMidY - oMidY)) } const ridgePoint = [ cMidX + ridgeVector.x * cOffset, @@ -3531,6 +3540,9 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { case LINE_TYPE.WALLLINE.JERKINHEAD: offset = oppLine.attributes.width / 2 break + case LINE_TYPE.WALLLINE.WALL: + offset = 0 + break default: break } @@ -4664,8 +4676,8 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { } } } - const checkNewLine = new fabric.Line(linePoint, { stroke: 'cyan', strokeWidth: 4, parentId: roofId, name: 'check' }) - canvas.add(checkNewLine).renderAll() + // const checkNewLine = new fabric.Line(linePoint, { stroke: 'cyan', strokeWidth: 4, parentId: roofId, name: 'check' }) + // canvas.add(checkNewLine).renderAll() newAnalysis.push({ start: { x: linePoint[0], y: linePoint[1] }, @@ -4827,8 +4839,8 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { }) } - //하단 지붕 라인처리 - const downRoofLines = [] + //케라바에서 파생된 하단 지붕 라인처리 + const downRoofGable = [] baseLines.forEach((baseLine, index) => { const nextLine = baseLines[(index + 1) % baseLines.length] const prevLine = baseLines[(index - 1 + baseLines.length) % baseLines.length] @@ -4836,16 +4848,422 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { const prevLineVector = { x: Math.sign(prevLine.x1 - prevLine.x2), y: Math.sign(prevLine.y1 - prevLine.y2) } const nextLineVector = { x: Math.sign(nextLine.x1 - nextLine.x2), y: Math.sign(nextLine.y1 - nextLine.y2) } + const midX = (baseLine.x1 + baseLine.x2) / 2 + const midY = (baseLine.y1 + baseLine.y2) / 2 + const checkPoint = { x: midX + nextLineVector.x, y: midY + nextLineVector.y } //반절마루 생성불가이므로 지붕선 분기를 해야하는지 확인 후 처리. if ( prevLineVector.x === nextLineVector.x && prevLineVector.y === nextLineVector.y && + baseLine.attributes.type === LINE_TYPE.WALLLINE.EAVES && (prevLine.attributes.type === LINE_TYPE.WALLLINE.GABLE || nextLine.attributes.type === LINE_TYPE.WALLLINE.GABLE) ) { - downRoofLines.push(index) + downRoofGable.push({ currLine: baseLine, currIndex: index, type: 'A' }) + } + + if ( + (prevLineVector.x !== nextLineVector.x || prevLineVector.y !== nextLineVector.y) && + checkWallPolygon.inPolygon(checkPoint) && + prevLine.attributes.type === LINE_TYPE.WALLLINE.GABLE && + nextLine.attributes.type === LINE_TYPE.WALLLINE.GABLE + ) { + downRoofGable.push({ currLine: baseLine, currIndex: index, type: 'B' }) } }) + const downRoofLines = [] // 하단지붕 파생 라인 처리후 innerLines에 추가. + //A타입 하단 지붕 prevLine과 nextLine이 같은 방향으로 가는 경우 + downRoofGable + .filter((l) => l.type === 'A') + .forEach(({ currLine, currIndex }) => { + // 라인의 방향 + const currVector = { x: Math.sign(clamp01(currLine.x1 - currLine.x2)), y: Math.sign(clamp01(currLine.y1 - currLine.y2)) } + + //어느쪽이 기준인지 확인. + //대각선 제외 + if (currVector.x !== 0 && currVector.y !== 0) return + + const prevLine = baseLines[(currIndex - 1 + baseLines.length) % baseLines.length] + const nextLine = baseLines[(currIndex + 1) % baseLines.length] + + const prevVector = { x: Math.sign(clamp01(prevLine.x1 - prevLine.x2)), y: Math.sign(clamp01(prevLine.y1 - prevLine.y2)) } + const nextVector = { x: Math.sign(clamp01(nextLine.x1 - nextLine.x2)), y: Math.sign(clamp01(nextLine.y1 - nextLine.y2)) } + + let gableLine + //가로선 + if (currVector.y === 0) { + if (currVector.x === 1) { + if (prevVector.y === 1 && nextVector.y === 1) { + gableLine = nextLine + } + if (prevVector.y === -1 && nextVector.y === -1) { + gableLine = prevLine + } + } + if (currVector.x === -1) { + if (prevVector.y === 1 && nextVector.y === 1) { + gableLine = prevLine + } + if (prevVector.y === -1 && nextVector.y === -1) { + gableLine = nextLine + } + } + } + + //세로선 + if (currVector.x === 0) { + if (currVector.y === 1) { + if (prevVector.x === 1 && nextVector.x === 1) { + gableLine = prevLine + } + if (prevVector.x === -1 && nextVector.x === -1) { + gableLine = nextLine + } + } + if (currVector.y === -1) { + if (prevVector.x === 1 && nextVector.x === 1) { + gableLine = nextLine + } + if (prevVector.x === -1 && nextVector.x === -1) { + gableLine = prevLine + } + } + } + + //기준점 + let stdPoint = [] + //반대쪽 라인을 찾기위한 vector + let oppFindVector + if (gableLine === prevLine) { + stdPoint.push(currLine.x1, currLine.y1) + stdPoint.push(currLine.x2 + -currVector.x * nextLine.attributes.offset, currLine.y2 + -currVector.y * nextLine.attributes.offset) + oppFindVector = { x: Math.sign(clamp01(nextLine.x2 - nextLine.x1)), y: Math.sign(clamp01(nextLine.y2 - nextLine.y1)) } + } else { + stdPoint.push(currLine.x2, currLine.y2) + stdPoint.push(currLine.x1 + currVector.x * prevLine.attributes.offset, currLine.y1 + currVector.y * prevLine.attributes.offset) + oppFindVector = { x: Math.sign(clamp01(prevLine.x1 - prevLine.x2)), y: Math.sign(clamp01(prevLine.y1 - prevLine.y2)) } + } + + //반대쪽 라인들 (마루선을 위함) + const oppLines = baseLines.filter((line) => { + const lineVector = { x: Math.sign(clamp01(line.x1 - line.x2)), y: Math.sign(clamp01(line.y1 - line.y2)) } + const oppVector = + lineVector.x === 0 ? { x: Math.sign(clamp01(line.x1 - stdPoint[0])), y: 0 } : { x: 0, y: Math.sign(clamp01(line.y1 - stdPoint[1])) } + + const rightDirection = + (currVector.x === lineVector.x && currVector.y !== lineVector.y) || (currVector.x !== lineVector.x && currVector.y === lineVector.y) + const rightOpp = oppFindVector.x === oppVector.x && oppFindVector.y === oppVector.y + return rightDirection && rightOpp + }) + + const innerRidge = innerLines + .filter((line) => line.name === LINE_TYPE.SUBLINE.RIDGE) + .filter((line) => { + const lineVector = { x: Math.sign(clamp01(line.x1 - line.x2)), y: Math.sign(clamp01(line.y1 - line.y2)) } + //마루선을 찾는다. + if ((currVector.x === 0 && lineVector.x === 0) || (currVector.y === 0 && lineVector.y === 0)) { + //세로선 + if (lineVector.x === 0) { + const minY = Math.min(line.y1, line.y2) + const maxY = Math.max(line.y1, line.y2) + // 기준 라인 안에 들어있는 경우에만 처리. + if ( + Math.min(stdPoint[1], stdPoint[3]) <= minY && + maxY <= Math.max(stdPoint[1], stdPoint[3]) && + Math.min(stdPoint[0], ...oppLines.map((line) => line.x1)) <= line.x1 && + line.x1 <= Math.max(stdPoint[0], ...oppLines.map((line) => line.x1)) + ) { + return true + } + } + //가로선 + if (lineVector.y === 0) { + const minX = Math.min(line.x1, line.x2) + const maxX = Math.max(line.x1, line.x2) + // 기준 라인 안에 들어있는 경우에만 처리 + if ( + Math.min(stdPoint[0], stdPoint[2]) <= minX && + maxX <= Math.max(stdPoint[0], stdPoint[2]) && + Math.min(stdPoint[1], ...oppLines.map((line) => line.y1)) <= line.y1 && + line.y1 <= Math.max(stdPoint[1], ...oppLines.map((line) => line.y1)) + ) { + return true + } + } + } + }) + + //1. 현재 라인을 기준으로 지붕선 추가. + //1-1 stdPoint을 현재라인의 지붕 출폭 만큼 조정 + const currOffset = currLine.attributes.offset + const noGableLine = gableLine === prevLine ? nextLine : prevLine + + let roofLinePoint = stdPoint + if (currVector.x === 0) { + //세로일때 + //x축 조정 + roofLinePoint[0] = roofLinePoint[0] + currVector.y * currOffset + roofLinePoint[2] = roofLinePoint[2] + currVector.y * currOffset + } else if (currVector.y === 0) { + //가로일때 + //y축 조정 + roofLinePoint[1] = roofLinePoint[1] - currVector.x * currOffset + roofLinePoint[3] = roofLinePoint[3] - currVector.x * currOffset + } + + //지붕선추가. + downRoofLines.push(drawRoofLine(roofLinePoint, canvas, roof, textMode)) + + //1-2 지붕선에서 oppLine으로 향하는 중 가장 가까운 마루선까지의 연결선 생성 + const findRidgeEdge = { + vertex1: { x: roofLinePoint[0], y: roofLinePoint[1] }, + vertex2: { x: roofLinePoint[0] + oppFindVector.x, y: roofLinePoint[1] + oppFindVector.y }, + } + let minDistance = Infinity + let minPoint + let isLine + innerRidge.forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersect = edgesIntersection(lineEdge, findRidgeEdge) + if (intersect) { + let distance = Infinity + if (currVector.x === 0) { + const lineDistance1 = Math.abs(line.y1 - roofLinePoint[1]) + const lineDistance2 = Math.abs(line.y2 - roofLinePoint[1]) + distance = Math.min(lineDistance1, lineDistance2) + } else if (currVector.y === 0) { + const lineDistance1 = Math.abs(line.x1 - roofLinePoint[0]) + const lineDistance2 = Math.abs(line.x2 - roofLinePoint[0]) + distance = Math.min(lineDistance1, lineDistance2) + } + if (distance < minDistance) { + minDistance = distance + minPoint = intersect + isLine = line + } + } + }) + if (minPoint) { + const hipPoint = [roofLinePoint[0], roofLinePoint[1], minPoint.x, minPoint.y] + downRoofLines.push( + drawHipLine(hipPoint, canvas, roof, textMode, null, getDegreeByChon(currLine.attributes.pitch), getDegreeByChon(currLine.attributes.pitch)), + ) + + if (isLine) { + const newRidgePoint = [minPoint.x, minPoint.y] + const distance1 = Math.sqrt(Math.pow(minPoint.x - isLine.x1, 2) + Math.pow(minPoint.y - isLine.y1, 2)) + const distance2 = Math.sqrt(Math.pow(minPoint.x - isLine.x2, 2) + Math.pow(minPoint.y - isLine.y2, 2)) + if (distance2 < distance1) { + newRidgePoint.push(isLine.x1, isLine.y1) + } else { + newRidgePoint.push(isLine.x2, isLine.y2) + } + downRoofLines.push(drawRoofLine(newRidgePoint, canvas, roof, textMode)) + } + } + }) + + // B타입 하단지붕 prevLine과 nextLine의 방향이 반대인데 현재 라인이 지붕 안쪽으로 들어가는 모양. + downRoofGable + .filter((l) => l.type === 'B') + .forEach(({ currLine, currIndex }) => { + const checkLine = new fabric.Line([currLine.x1, currLine.y1, currLine.x2, currLine.y2], { + stroke: 'red', + strokeWidth: 4, + parentId: roofId, + name: 'check', + }) + canvas.add(checkLine).renderAll() + + // 라인의 방향 + const currVector = { x: Math.sign(clamp01(currLine.x1 - currLine.x2)), y: Math.sign(clamp01(currLine.y1 - currLine.y2)) } + + //어느쪽이 기준인지 확인. + //대각선 제외 + if (currVector.x !== 0 && currVector.y !== 0) return + + const prevLine = baseLines[(currIndex - 1 + baseLines.length) % baseLines.length] + const nextLine = baseLines[(currIndex + 1) % baseLines.length] + + const prevVector = { x: Math.sign(clamp01(prevLine.x1 - prevLine.x2)), y: Math.sign(clamp01(prevLine.y1 - prevLine.y2)) } + const nextVector = { x: Math.sign(clamp01(nextLine.x1 - nextLine.x2)), y: Math.sign(clamp01(nextLine.y1 - nextLine.y2)) } + + const minX = Math.min(currLine.x1, currLine.x2) + const maxX = Math.max(currLine.x1, currLine.x2) + const minY = Math.min(currLine.y1, currLine.y2) + const maxY = Math.max(currLine.y1, currLine.y2) + const midX = (currLine.x1 + currLine.x2) / 2 + const midY = (currLine.y1 + currLine.y2) / 2 + let ridgeFindVector = { x: nextVector.x, y: nextVector.y } + if (!checkWallPolygon.inPolygon({ x: midX, y: midY })) { + ridgeFindVector = { x: prevVector.x, y: prevVector.y } + } + + // 마루를 따라 생성되어야 하는 지붕선 추가. + const oppLines = innerLines + .filter((l) => l.name === LINE_TYPE.SUBLINE.RIDGE) + .filter((line) => { + const lineVector = { x: Math.sign(clamp01(line.x1 - line.x2)), y: Math.sign(clamp01(line.y1 - line.y2)) } + let oppVector + if (currVector.x === 0) { + if (currVector.y === 1) { + oppVector = { x: Math.sign(clamp01(currLine.x1 - line.x1)), y: 0 } + } else if (currVector.y === -1) { + oppVector = { x: Math.sign(clamp01(line.x1 - currLine.x1)), y: 0 } + } + } else if (currVector.y === 0) { + if (currVector.x === 1) { + oppVector = { x: 0, y: Math.sign(clamp01(line.y1 - currLine.y1)) } + } else if (currVector.x === -1) { + oppVector = { x: 0, y: Math.sign(clamp01(currLine.y1 - line.y1)) } + } + } + + const lineMinX = Math.min(line.x1, line.x2) + const lineMaxX = Math.max(line.x1, line.x2) + const lineMinY = Math.min(line.y1, line.y2) + const lineMaxY = Math.max(line.y1, line.y2) + + const isInside = lineVector.y === 0 ? minX <= lineMinX && lineMaxX <= maxX : minY <= lineMinY && lineMaxY <= maxY + + const rightOpp = ridgeFindVector.x === oppVector.x && ridgeFindVector.y === oppVector.y + return rightOpp && isInside + }) + + // 현재 라인의 지붕선 추가. + const currOffset = currLine.attributes.offset + const roofPoint = [ + currLine.x1 + -nextVector.x * currOffset, + currLine.y1 + -nextVector.y * currOffset, + currLine.x2 + -nextVector.x * currOffset, + currLine.y2 + -nextVector.y * currOffset, + ] + downRoofLines.push(drawRoofLine(roofPoint, canvas, roof, textMode)) + + // 현재 라인 좌표의 시작과 끝 포인트에서 직교하는 포인트와 라인을 찾는다 + let orthogonalStartPoint, + orthogonalStartDistance = Infinity, + orthogonalStartLine + let orthogonalEndPoint, + orthogonalEndDistance = Infinity, + orthogonalEndLine + oppLines.forEach((line) => { + const checkLine = new fabric.Line([line.x1, line.y1, line.x2, line.y2], { stroke: 'red', strokeWidth: 4, parentId: roofId, name: 'check' }) + canvas.add(checkLine).renderAll() + + if (currVector.x === 0) { + //세로선 + // 시작포인트와 가까운 포인트의 길이 + const lineStartDist = + Math.abs(currLine.y1 - line.y1) < Math.abs(currLine.y1 - line.y2) ? Math.abs(currLine.y1 - line.y1) : Math.abs(currLine.y1 - line.y2) + const lineStartPoint = + Math.abs(currLine.y1 - line.y1) < Math.abs(currLine.y1 - line.y2) ? { x: line.x1, y: currLine.y1 } : { x: line.x2, y: currLine.y1 } + if (lineStartDist < orthogonalStartDistance) { + orthogonalStartDistance = lineStartDist + orthogonalStartPoint = lineStartPoint + orthogonalStartLine = line + } + // 종료포인트와 가까운 포인트의 길이 + const lineEndDist = + Math.abs(currLine.y2 - line.y1) < Math.abs(currLine.y2 - line.y2) ? Math.abs(currLine.y2 - line.y2) : Math.abs(currLine.y2 - line.y1) + const lineEndPoint = + Math.abs(currLine.y2 - line.y1) < Math.abs(currLine.y2 - line.y2) ? { x: line.x2, y: currLine.y2 } : { x: line.x1, y: currLine.y2 } + if (lineEndDist < orthogonalEndDistance) { + orthogonalEndDistance = lineEndDist + orthogonalEndPoint = lineEndPoint + orthogonalEndLine = line + } + } else if (currVector.y === 0) { + //가로선 + // 시작포인트와 가까운 포인트의 길이 + const lineStartDist = + Math.abs(currLine.x1 - line.x1) < Math.abs(currLine.x1 - line.x2) ? Math.abs(currLine.x1 - line.x1) : Math.abs(currLine.x1 - line.x2) + const lineStartPoint = + Math.abs(currLine.x1 - line.x1) < Math.abs(currLine.x1 - line.x2) ? { x: currLine.x1, y: line.y1 } : { x: currLine.x1, y: line.y2 } + if (lineStartDist < orthogonalStartDistance) { + orthogonalStartDistance = lineStartDist + orthogonalStartPoint = lineStartPoint + orthogonalStartLine = line + } + //종료포인트와 가까운 포인트의 길이 + const lineEndDist = + Math.abs(currLine.x2 - line.x1) < Math.abs(currLine.x2 - line.x2) ? Math.abs(currLine.x2 - line.x1) : Math.abs(currLine.x2 - line.x2) + const lineEndPoint = + Math.abs(currLine.x2 - line.x1) < Math.abs(currLine.x2 - line.x2) ? { x: currLine.x2, y: line.y1 } : { x: currLine.x2, y: line.y2 } + if (lineEndDist < orthogonalEndDistance) { + orthogonalEndDistance = lineEndDist + orthogonalEndPoint = lineEndPoint + orthogonalEndLine = line + } + } + }) + + //직교 라인이 있는 경우 + if (orthogonalStartLine !== undefined && orthogonalEndLine !== undefined) { + if (orthogonalStartLine === orthogonalEndLine) { + //직교 라인이 1개일때 처리 + downRoofLines.push( + drawRoofLine([orthogonalStartPoint.x, orthogonalStartPoint.y, orthogonalEndPoint.x, orthogonalEndPoint.y], canvas, roof, textMode), + ) + } else { + //직교 라인이 2개일때 처리 + // 시작 라인 처리 + const startDist1 = Math.sqrt( + Math.pow(orthogonalStartPoint.x - orthogonalStartLine.x1, 2) + Math.pow(orthogonalStartPoint.y - orthogonalStartLine.y1, 2), + ) + const startDist2 = Math.sqrt( + Math.pow(orthogonalStartPoint.x - orthogonalStartLine.x2, 2) + Math.pow(orthogonalStartPoint.y - orthogonalStartLine.y2, 2), + ) + const otherStartPoint = + startDist1 > startDist2 + ? { x: orthogonalStartLine.x1, y: orthogonalStartLine.y1 } + : { x: orthogonalStartLine.x2, y: orthogonalStartLine.y2 } + downRoofLines.push( + drawRoofLine([orthogonalStartPoint.x, orthogonalStartPoint.y, otherStartPoint.x, otherStartPoint.y], canvas, roof, textMode), + ) + + const endDist1 = Math.sqrt( + Math.pow(orthogonalEndPoint.x - orthogonalEndLine.x1, 2) + Math.pow(orthogonalEndPoint.y - orthogonalEndLine.y1, 2), + ) + const endDist2 = Math.sqrt( + Math.pow(orthogonalEndPoint.x - orthogonalEndLine.x2, 2) + Math.pow(orthogonalEndPoint.y - orthogonalEndLine.y2, 2), + ) + const otherEndPoint = + endDist1 > endDist2 ? { x: orthogonalEndLine.x1, y: orthogonalEndLine.y1 } : { x: orthogonalEndLine.x2, y: orthogonalEndLine.y2 } + downRoofLines.push(drawRoofLine([orthogonalEndPoint.x, orthogonalEndPoint.y, otherEndPoint.x, otherEndPoint.y], canvas, roof, textMode)) + } + + //지붕선(roofPoint)에서 직교포인트까지 연결하는 라인을 추가한다. + const orthogonalPoint1 = [roofPoint[0], roofPoint[1], orthogonalStartPoint.x, orthogonalStartPoint.y] + const orthogonalPoint2 = [roofPoint[2], roofPoint[3], orthogonalEndPoint.x, orthogonalEndPoint.y] + downRoofLines.push( + drawHipLine( + orthogonalPoint1, + canvas, + roof, + textMode, + null, + getDegreeByChon(currLine.attributes.pitch), + getDegreeByChon(currLine.attributes.pitch), + ), + ) + downRoofLines.push( + drawHipLine( + orthogonalPoint2, + canvas, + roof, + textMode, + null, + getDegreeByChon(currLine.attributes.pitch), + getDegreeByChon(currLine.attributes.pitch), + ), + ) + } + }) + + //추가된 하단 지붕 라인 innerLines에 추가. + innerLines.push(...downRoofLines) + //지붕선에 따라 라인추가 작업 처리. const innerLinesPoints = [] innerLines.forEach((line) => { @@ -4857,9 +5275,8 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { roof.lines.forEach((currentLine, index) => { const prevLine = roof.lines[(index - 1 + roof.lines.length) % roof.lines.length] const nextLine = roof.lines[(index + 1) % roof.lines.length] - const prevDegree = getDegreeByChon(prevLine.attributes.pitch) - const nextDegree = getDegreeByChon(nextLine.attributes.pitch) - console.log('prevDegree, nextDegree: ', prevDegree, nextDegree) + const prevDegree = prevLine.attributes.pitch ? getDegreeByChon(prevLine.attributes.pitch) : 0 + const nextDegree = nextLine.attributes.pitch ? getDegreeByChon(nextLine.attributes.pitch) : 0 const splitPoint = [] let hasOverlapLine = false @@ -4903,7 +5320,13 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { } startPoint = point } - innerLines.push(drawHipLine([startPoint.x, startPoint.y, currentLine.x2, currentLine.y2], canvas, roof, textMode, null, nextDegree, nextDegree)) + if (splitPoint.length === 1) { + innerLines.push(drawRoofLine([startPoint.x, startPoint.y, currentLine.x2, currentLine.y2], canvas, roof, textMode)) + } else { + innerLines.push( + drawHipLine([startPoint.x, startPoint.y, currentLine.x2, currentLine.y2], canvas, roof, textMode, null, nextDegree, nextDegree), + ) + } } else { innerLines.push(drawRoofLine([currentLine.x1, currentLine.y1, currentLine.x2, currentLine.y2], canvas, roof, textMode)) } @@ -4917,730 +5340,6 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { .filter((object) => object.name === 'check') .forEach((object) => canvas.remove(object)) canvas.renderAll() - //return - /*while (linesAnalysis.length > 0 && iterations < MAX_ITERATIONS) { - iterations++ - - linesAnalysis.forEach((line) => { - const point = [line.start.x, line.start.y, line.end.x, line.end.y] - const checkLine = new fabric.Line(point, { - stroke: 'red', - strokeWidth: 2, - parentId: roofId, - name: 'check', - }) - canvas.add(checkLine) - canvas.renderAll() - }) - // 각 가선분의 최단 교점 찾기 - const intersections = [] - for (let i = 0; i < linesAnalysis.length; i++) { - let minDistance = Infinity //최단거리 - let intersectPoint = null //교점 좌표 - let partners = new Set() //교점 선분의 index - - const lineI = linesAnalysis[i] - const lengthI = Math.sqrt(Math.pow(lineI.end.x - lineI.start.x, 2) + Math.pow(lineI.end.y - lineI.start.y, 2)) - console.log('lengthI', lengthI) - const checkLineI = new fabric.Line([lineI.start.x, lineI.start.y, lineI.end.x, lineI.end.y], { - stroke: 'blue', - strokeWidth: 2, - parentId: roofId, - name: 'check', - }) - canvas.add(checkLineI).renderAll() - - if (lineI.type === TYPES.GABLE_LINE && lineI.connectCnt > 1) continue - - if (lengthI > EPSILON) { - const otherLines = linesAnalysis.filter((j) => j !== lineI) - const zeroLines = linesAnalysis.filter((j) => Math.sqrt(Math.pow(j.end.x - j.start.x, 2) + Math.pow(j.end.y - j.start.y, 2)) < EPSILON) - if (otherLines.length === zeroLines.length) { - zeroLines.forEach((j) => { - const jIndex = linesAnalysis.indexOf(j) - if (isPointOnLineNew({ x1: lineI.start.x, y1: lineI.start.y, x2: lineI.end.x, y2: lineI.end.y }, { x: j.start.x, y: j.start.y })) { - const distance = Math.sqrt(Math.pow(j.start.x - lineI.start.x, 2) + Math.pow(j.start.y - lineI.start.y, 2)) - if (distance < minDistance) { - minDistance = distance - intersectPoint = { x: j.start.x, y: j.start.y } - partners = new Set([jIndex]) - } else if (almostEqual(distance, minDistance)) { - partners.add(jIndex) - } - } - }) - if (intersectPoint) { - intersections.push({ index: i, intersectPoint, partners, distance: minDistance }) - partners.forEach((j) => { - const p = new Set([i]) - intersections.push({ index: j, intersectPoint, partners: p, distance: 0 }) - }) - continue - } - } - } - - for (let j = 0; j < linesAnalysis.length; j++) { - const lineJ = linesAnalysis[j] - if (lineI === lineJ) continue - if (lineI.type === TYPES.GABLE_LINE && lineJ.type === TYPES.GABLE_LINE && i.gableId === lineJ.gableId) continue - if (lineJ.type === TYPES.GABLE_LINE && lineJ.connectCnt > 1) continue - - const intersection = lineIntersection(lineI.start, lineI.end, lineJ.start, lineJ.end, canvas) - if (intersection) { - const distance = Math.sqrt((intersection.x - lineI.start.x) ** 2 + (intersection.y - lineI.start.y) ** 2) - const distance2 = Math.sqrt((intersection.x - lineJ.start.x) ** 2 + (intersection.y - lineJ.start.y) ** 2) - if (lineI.type === TYPES.GABLE_LINE && distance < EPSILON) continue - if (distance < minDistance && !almostEqual(distance, minDistance) && !(distance < EPSILON && distance2 < EPSILON)) { - minDistance = distance - intersectPoint = intersection - partners = new Set([j]) - } else if (almostEqual(distance, minDistance)) { - partners.add(j) - } - } - } - if (intersectPoint) { - intersections.push({ index: i, intersectPoint, partners, distance: minDistance }) - } - canvas.remove(checkLineI).renderAll() - } - - // 제일 가까운 교점부터 처리 - intersections.sort((a, b) => a.distance - b.distance) - - console.log('intersections', intersections) - // 교점에 대한 적합 여부 판단 및 처리. - let newAnalysis = [] //신규 발생 선 - const processed = new Set() //처리된 선 - for (const { index, intersectPoint, partners } of intersections) { - canvas - .getObjects() - .filter((object) => object.name === 'check') - .forEach((object) => canvas.remove(object)) - canvas.renderAll() - let isProceed = false - for (const pIndex of partners) { - console.log('pIndex : ', pIndex) - const check1 = linesAnalysis[index] - const checkLine1 = new fabric.Line([check1.start.x, check1.start.y, check1.end.x, check1.end.y], { - stroke: 'red', - strokeWidth: 2, - parentId: roofId, - name: 'check', - }) - const checkCircle1 = new fabric.Circle({ left: check1.start.x, top: check1.start.y, radius: 5, fill: 'red', parentId: roofId, name: 'check' }) - const checkCircle2 = new fabric.Circle({ left: check1.end.x, top: check1.end.y, radius: 5, fill: 'green', parentId: roofId, name: 'check' }) - canvas.add(checkLine1, checkCircle1, checkCircle2) - canvas.renderAll() - - const check2 = linesAnalysis[pIndex] - const checkLine2 = new fabric.Line([check2.start.x, check2.start.y, check2.end.x, check2.end.y], { - stroke: 'green', - strokeWidth: 2, - parentId: roofId, - name: 'check', - }) - - const checkCircle3 = new fabric.Circle({ left: check2.start.x, top: check2.start.y, radius: 5, fill: 'red', parentId: roofId, name: 'check' }) - const checkCircle4 = new fabric.Circle({ left: check2.end.x, top: check2.end.y, radius: 5, fill: 'green', parentId: roofId, name: 'check' }) - canvas.add(checkLine2, checkCircle3, checkCircle4) - canvas.renderAll() - - console.log('!intersectPoint || processed.has(index)', !intersectPoint, processed.has(index)) - //교점이 없거나, 이미 처리된 선분이면 처리하지 않는다. - if (!intersectPoint || processed.has(index)) continue - - const partner = intersections.find((p) => p.index === pIndex) - //교점이 없거나, 교점 선분이 없으면 처리하지 않는다. - if (!partner || !partner.intersectPoint) continue - - //상호 최단 교점 여부 확인. - if (partner.partners.has(index)) { - const line1 = linesAnalysis[index] - const line2 = linesAnalysis[pIndex] - - //좌,우 선 중 공통 선 존재 확인. - const isSameLine = line1.left === line2.left || line1.left === line2.right || line1.right === line2.left || line1.right === line2.right - if (isSameLine) { - // 현재 선이 처리 되었음을 표기 - let point1 = [line1.start.x, line1.start.y, intersectPoint.x, intersectPoint.y] - let point2 = [line2.start.x, line2.start.y, intersectPoint.x, intersectPoint.y] - let length1 = Math.sqrt((point1[2] - point1[0]) ** 2 + (point1[3] - point1[1]) ** 2) - let length2 = Math.sqrt((point2[2] - point2[0]) ** 2 + (point2[3] - point2[1]) ** 2) - if (length1 < EPSILON && length2 < EPSILON) continue - isProceed = true - - //gable라인과 붙는경우 length가 0에 가까우면 포인트를 뒤집는다. - if (line1.type === TYPES.GABLE_LINE && length2 < EPSILON) { - point2 = [line2.end.x, line2.end.y, intersectPoint.x, intersectPoint.y] - length2 = Math.sqrt((point2[2] - point2[0]) ** 2 + (point2[3] - point2[1]) ** 2) - } - if (line2.type === TYPES.GABLE_LINE && length1 < EPSILON) { - point1 = [line1.end.x, line1.end.y, intersectPoint.x, intersectPoint.y] - length1 = Math.sqrt((point1[2] - point1[0]) ** 2 + (point1[3] - point1[1]) ** 2) - } - - if (length1 > 0 && !alreadyPoints(innerLines, point1)) { - if (line1.type === TYPES.HIP) { - innerLines.push(drawHipLine(point1, canvas, roof, textMode, null, line1.degree, line1.degree)) - } else if (line1.type === TYPES.RIDGE) { - innerLines.push(drawRidgeLine(point1, canvas, roof, textMode)) - } else if (line1.type === TYPES.NEW) { - const isDiagonal = Math.abs(point1[0] - point1[2]) >= 1 && Math.abs(point1[1] - point1[3]) >= 1 - if (isDiagonal) { - innerLines.push(drawHipLine(point1, canvas, roof, textMode, null, line1.degree, line1.degree)) - } else { - innerLines.push(drawRidgeLine(point1, canvas, roof, textMode)) - } - } else if (line1.type === TYPES.GABLE_LINE) { - if (line1.degree > 0) { - innerLines.push(drawHipLine(point1, canvas, roof, textMode, null, line1.degree, line1.degree)) - } else { - innerLines.push(drawRidgeLine(point1, canvas, roof, textMode)) - } - } - } - - if (length2 > 0 && !alreadyPoints(innerLines, point2)) { - if (line2.type === TYPES.HIP) { - innerLines.push(drawHipLine(point2, canvas, roof, textMode, null, line2.degree, line2.degree)) - } else if (line2.type === TYPES.RIDGE) { - innerLines.push(drawRidgeLine(point2, canvas, roof, textMode)) - } else if (line2.type === TYPES.NEW) { - const isDiagonal = Math.abs(point2[0] - point2[2]) >= 1 && Math.abs(point2[1] - point2[3]) >= 1 - if (isDiagonal) { - innerLines.push(drawHipLine(point2, canvas, roof, textMode, null, line2.degree, line2.degree)) - } else { - innerLines.push(drawRidgeLine(point2, canvas, roof, textMode)) - } - } else if (line2.type === TYPES.GABLE_LINE) { - if (line2.degree > 0) { - innerLines.push(drawHipLine(point2, canvas, roof, textMode, null, line2.degree, line2.degree)) - } else { - innerLines.push(drawRidgeLine(point2, canvas, roof, textMode)) - } - } - } - - if (line1.type === TYPES.GABLE_LINE || line2.type === TYPES.GABLE_LINE) { - console.log('gableLine newAnalyze start') - const gableLine = line1.type === TYPES.GABLE_LINE ? line1 : line2 - gableLine.connectCnt++ - - const checkLine = new fabric.Line([gableLine.start.x, gableLine.start.y, gableLine.end.x, gableLine.end.y], { - stroke: 'red', - strokeWidth: 4, - parentId: roofId, - name: 'check', - }) - const checkCircle = new fabric.Circle({ - left: intersectPoint.x, - top: intersectPoint.y, - radius: 5, - parentId: roofId, - name: 'check', - }) - canvas.add(checkLine, checkCircle) - canvas.renderAll() - - const relationBaseLines = [line1.left, line1.right, line2.left, line2.right] - const uniqueBaseLines = [...new Set(relationBaseLines)] - if (uniqueBaseLines.length === 3) { - const linesCounts = new Map() - relationBaseLines.forEach((e) => { - linesCounts.set(e, (linesCounts.get(e) || 0) + 1) - }) - const uniqueLines = Array.from(linesCounts.entries()) - .filter(([_, count]) => count === 1) - .map(([line, _]) => line) - - if (uniqueLines.length === 2) { - if (gableLine.connectCnt < 2) { - newAnalysis.push({ - start: { x: intersectPoint.x, y: intersectPoint.y }, - end: { x: gableLine.end.x, y: gableLine.end.y }, - left: gableLine.left, - right: gableLine.right, - type: TYPES.GABLE_LINE, - degree: getDegreeByChon(baseLines[gableLine.right].attributes.pitch), - gableId: gableLine.gableId, - connectCnt: gableLine.connectCnt, - }) - } - if (gableLine.connectCnt >= 2) { - //가선분 발생만 시키는 더미 생성. - newAnalysis.push({ - start: { x: intersectPoint.x, y: intersectPoint.y }, - end: { x: intersectPoint.x, y: intersectPoint.y }, - left: gableLine.gableId, - right: gableLine.gableId, - type: TYPES.HIP, - degree: 0, - }) - } - } - } - console.log('gableLine newAnalyze end') - } else { - // 연결점에서 새로운 가선분을 생성 - const relationBaseLines = [line1.left, line1.right, line2.left, line2.right] - const uniqueBaseLines = [...new Set(relationBaseLines)] - if (uniqueBaseLines.length === 3) { - const linesCounts = new Map() - relationBaseLines.forEach((e) => { - linesCounts.set(e, (linesCounts.get(e) || 0) + 1) - }) - - const uniqueLines = Array.from(linesCounts.entries()) - .filter(([_, count]) => count === 1) - .map(([line, _]) => line) - - if (uniqueLines.length === 2) { - // 두 변의 이등분선 방향 계산 - // uniqueLines.sort((a, b) => a - b) - console.log('uniqueLines : ', uniqueLines) - const baseLine1 = baseLines[uniqueLines[0]] - const baseLine2 = baseLines[uniqueLines[1]] - - const checkLine1 = new fabric.Line([baseLine1.x1, baseLine1.y1, baseLine1.x2, baseLine1.y2], { - stroke: 'yellow', - strokeWidth: 4, - parentId: roofId, - name: 'check', - }) - const checkLine2 = new fabric.Line([baseLine2.x1, baseLine2.y1, baseLine2.x2, baseLine2.y2], { - stroke: 'blue', - strokeWidth: 4, - parentId: roofId, - name: 'check', - }) - canvas.add(checkLine1, checkLine2) - canvas.renderAll() - let bisector - console.log('isParallel(baseLine1, baseLine2)', isParallel(baseLine1, baseLine2)) - if (isParallel(baseLine1, baseLine2)) { - bisector = getBisectLines( - { x1: line1.start.x, x2: line1.end.x, y1: line1.start.y, y2: line1.end.y }, - { x1: line2.start.x, y1: line2.start.y, x2: line2.end.x, y2: line2.end.y }, - ) - } else { - //이등분선 - bisector = getBisectBaseLines(baseLine1, baseLine2, intersectPoint, canvas) - } - - //마주하는 지붕선을 찾는다. - const intersectionsByRoof = [] - const checkEdge = { - vertex1: { x: intersectPoint.x, y: intersectPoint.y }, - vertex2: { x: intersectPoint.x + bisector.x, y: intersectPoint.y + bisector.y }, - } - const checkVector = { - x: Math.sign(checkEdge.vertex1.x - checkEdge.vertex2.x), - y: Math.sign(checkEdge.vertex1.y - checkEdge.vertex2.y), - } - roof.lines.forEach((line) => { - const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } - const is = edgesIntersection(lineEdge, checkEdge) - if (is && isPointOnLineNew(line, is)) { - const distance = Math.sqrt((is.x - intersectPoint.x) ** 2 + (is.y - intersectPoint.y) ** 2) - const isVector = { x: Math.sign(intersectPoint.x - is.x), y: Math.sign(intersectPoint.y - is.y) } - if (isVector.x === checkVector.x && isVector.y === checkVector.y) { - intersectionsByRoof.push({ is, distance }) - } - } - }) - intersectionsByRoof.sort((a, b) => a.distance - b.distance) - //지붕 선과의 교점이 존재 할때 - if (intersectionsByRoof.length > 0) { - let is = intersectionsByRoof[0].is - let linePoint = [intersectPoint.x, intersectPoint.y, is.x, is.y] - const isDiagonal = Math.abs(is.x - intersectPoint.x) >= 1 && Math.abs(is.y - intersectPoint.y) >= 1 - const length = Math.sqrt((linePoint[2] - linePoint[0]) ** 2 + (linePoint[3] - linePoint[1]) ** 2) - if (!isDiagonal) { - const line1 = baseLines[uniqueLines[0]] - const line2 = baseLines[uniqueLines[1]] - const vector1 = { x: Math.sign(line1.x1 - line1.x2), y: Math.sign(line1.y1 - line1.y2) } - - const prevLine = checkVector.x === vector1.x && checkVector.y === vector1.y ? line2 : line1 - const nextLine = checkVector.x === vector1.x && checkVector.y === vector1.y ? line1 : line2 - const drivePoint = getRidgeDrivePoint(linePoint, prevLine, nextLine, baseLines) - if (drivePoint !== null) { - const driveLength = Math.sqrt((drivePoint.x - intersectPoint.x) ** 2 + (drivePoint.y - intersectPoint.y) ** 2) - - if (driveLength < length) { - linePoint = [intersectPoint.x, intersectPoint.y, drivePoint.x, drivePoint.y] - } - } - } - const checkNewLine = new fabric.Line(linePoint, { stroke: 'cyan', strokeWidth: 4, parentId: roofId, name: 'check' }) - canvas.add(checkNewLine).renderAll() - - newAnalysis.push({ - start: { x: linePoint[0], y: linePoint[1] }, - end: { x: linePoint[2], y: linePoint[3] }, - left: uniqueLines[0], - right: uniqueLines[1], - type: TYPES.NEW, - degree: isDiagonal ? line1.degree : 0, - }) - } - } - canvas - .getObjects() - .filter((object) => object.name === 'check') - .forEach((object) => canvas.remove(object)) - canvas.renderAll() - } - } - processed.add(pIndex) - } - } - canvas - .getObjects() - .filter((object) => object.name === 'check') - .forEach((object) => canvas.remove(object)) - canvas.renderAll() - } - if (isProceed) { - processed.add(index) - break - } - } - // 처리된 가선분 제외 - linesAnalysis = newAnalysis.concat(linesAnalysis.filter((_, index) => !processed.has(index))) - console.log('lineAnalysis: ', linesAnalysis) - - canvas - .getObjects() - .filter((object) => object.name === 'check' || object.name === 'checkAnalysis') - .forEach((object) => canvas.remove(object)) - canvas.renderAll() - // 새로운 가선분이 없을때 종료 - console.log('newAnalysis.length : ', newAnalysis.length) - if (newAnalysis.length === 0) break - } - console.log('lineAnalysis: end ', linesAnalysis)*/ - - // 가선분 중 처리가 안되어있는 붙어있는 라인에 대한 예외처리. - /* const proceedAnalysis = [] - linesAnalysis - .filter((line) => { - const dx = line.end.x - line.start.x - const dy = line.end.y - line.start.y - const length = Math.sqrt(dx ** 2 + dy ** 2) - return length > 0 - }) - .forEach((currentLine) => { - if (proceedAnalysis.find((p) => p === currentLine)) return - //현재와 같으면 제외, 이미 처리된 라인은 제외, 현재와 공통선분이 존재하지 않으면 제외 - linesAnalysis - .filter((line) => { - const dx = line.end.x - line.start.x - const dy = line.end.y - line.start.y - const length = Math.sqrt(dx ** 2 + dy ** 2) - return length > 0 - }) - .filter( - (partnerLine) => - partnerLine !== currentLine && - !proceedAnalysis.find((p) => p === partnerLine) && - (currentLine.left === partnerLine.left || - currentLine.left === partnerLine.right || - partnerLine.left === currentLine.left || - partnerLine.left === currentLine.right), - ) - .forEach((partnerLine) => { - const dx1 = currentLine.end.x - currentLine.start.x - const dy1 = currentLine.end.y - currentLine.start.y - const dx2 = partnerLine.end.x - partnerLine.start.x - const dy2 = partnerLine.end.y - partnerLine.start.y - const denominator = dy2 * dx1 - dx2 * dy1 - const isOpposite = dx1 * dx2 + dy1 * dy2 < 0 - //평행하지 않으면 제외 - if (Math.abs(denominator) > EPSILON) return - - const currentDegree = getDegreeByChon(baseLines[currentLine.left].attributes.pitch) - if (isOpposite) { - const points = [currentLine.start.x, currentLine.start.y, partnerLine.start.x, partnerLine.start.y] - const length = Math.sqrt((points[0] - points[2]) ** 2 + (points[1] - points[3]) ** 2) - - if (length > 0) { - if (currentLine.type === TYPES.HIP) { - innerLines.push(drawHipLine(points, canvas, roof, textMode, null, currentDegree, currentDegree)) - } else if (currentLine.type === TYPES.RIDGE) { - innerLines.push(drawRidgeLine(points, canvas, roof, textMode)) - } else if (currentLine.type === TYPES.NEW) { - const isDiagonal = Math.abs(points[0] - points[2]) >= 1 && Math.abs(points[1] - points[3]) >= 1 - if (isDiagonal && almostEqual(Math.abs(points[0] - points[2]), Math.abs(points[1] - points[3]))) { - innerLines.push(drawHipLine(points, canvas, roof, textMode, null, currentDegree, currentDegree)) - } - if (!isDiagonal) { - innerLines.push(drawRidgeLine(points, canvas, roof, textMode)) - } - } - proceedAnalysis.push(currentLine, partnerLine) - } - } else { - const allPoints = [currentLine.start, currentLine.end, partnerLine.start, partnerLine.end] - let points = [] - allPoints.forEach((point) => { - let count = 0 - allPoints.forEach((p) => { - if (almostEqual(point.x, p.x) && almostEqual(point.y, p.y)) count++ - }) - if (count === 1) points.push(point) - }) - - if (points.length === 2) { - const length = Math.sqrt((points[0].x - points[1].x) ** 2 + (points[0].y - points[1].y) ** 2) - if (length < EPSILON) return - const isDiagonal = Math.abs(points[0].x - points[1].x) >= 1 && Math.abs(points[0].y - points[1].y) >= 1 - if (isDiagonal) { - innerLines.push( - drawHipLine([points[0].x, points[0].y, points[1].x, points[1].y], canvas, roof, textMode, null, currentDegree, currentDegree), - ) - } else { - innerLines.push(drawRidgeLine([points[0].x, points[0].y, points[1].x, points[1].y], canvas, roof, textMode)) - } - proceedAnalysis.push(currentLine, partnerLine) - } - } - }) - }) - linesAnalysis = linesAnalysis.filter((line) => !proceedAnalysis.find((p) => p === line))*/ - - //하단 지붕 라인처리 - /* const downRoofLines = [] - baseLines.forEach((baseLine, index) => { - const nextLine = baseLines[(index + 1) % baseLines.length] - const prevLine = baseLines[(index - 1 + baseLines.length) % baseLines.length] - - const prevLineVector = { x: Math.sign(prevLine.x1 - prevLine.x2), y: Math.sign(prevLine.y1 - prevLine.y2) } - const nextLineVector = { x: Math.sign(nextLine.x1 - nextLine.x2), y: Math.sign(nextLine.y1 - nextLine.y2) } - - //반절마루 생성불가이므로 지붕선 분기를 해야하는지 확인 후 처리. - if ( - prevLineVector.x === nextLineVector.x && - prevLineVector.y === nextLineVector.y && - (prevLine.attributes.type === LINE_TYPE.WALLLINE.GABLE || nextLine.attributes.type === LINE_TYPE.WALLLINE.GABLE) - ) { - downRoofLines.push(index) - } - })*/ - - /*if (downRoofLines.length > 0) { - downRoofLines.forEach((index) => { - const currentLine = baseLines[index] - // const nextLine = baseLines[(index + 1) % baseLines.length] - // const prevLine = baseLines[(index - 1 + baseLines.length) % baseLines.length] - - const analyze = analyzeLine(currentLine) - if (analyze.isDiagonal) { - return - } - - const roofLine = analyze.roofLine - let roofPoint = [analyze.startPoint.x, analyze.startPoint.y, analyze.endPoint.x, analyze.endPoint.y] - - if (analyze.isVertical) { - roofPoint[0] = roofLine.x1 - roofPoint[2] = roofLine.x2 - } - if (analyze.isHorizontal) { - roofPoint[1] = roofLine.y1 - roofPoint[3] = roofLine.y2 - } - - console.log('analyze: ', analyze) - const findRidgeVector = { x: 0, y: 0 } - if (analyze.isVertical) { - // noinspection JSSuspiciousNameCombination - findRidgeVector.x = analyze.directionVector.y - } - if (analyze.isHorizontal) { - // noinspection JSSuspiciousNameCombination - findRidgeVector.y = analyze.directionVector.x - } - - console.log('findRidgeVector: ', findRidgeVector) - innerLines - .filter((line) => { - if (line.name === LINE_TYPE.SUBLINE.RIDGE) { - if (analyze.isVertical) { - const signX = Math.sign(currentLine.x1 - line.x1) - console.log('signX: ', signX) - return signX === findRidgeVector.x - } - if (analyze.isHorizontal) { - const signY = Math.sign(currentLine.y1 - line.y1) - console.log('signY: ', signY) - return signY === findRidgeVector.y - } - return false - } - return false - }) - .forEach((line) => { - if (line.name === LINE_TYPE.SUBLINE.RIDGE) { - const checkLine = new fabric.Line([line.x1, line.y1, line.x2, line.y2], { - stroke: 'red', - strokeWidth: 4, - parentId: roofId, - name: 'check', - }) - canvas.add(checkLine) - canvas.renderAll() - } - }) - - /!*const oppositeLines = [] - baseLines - .filter((line) => { - //평행한 반대방향 라인인지 확인. - const lineAnalyze = analyzeLine(line) - return ( - (analyze.isHorizontal && - lineAnalyze.directionVector.x !== analyze.directionVector.x && - lineAnalyze.directionVector.y === analyze.directionVector.y) || - (analyze.isVertical && - lineAnalyze.directionVector.x === analyze.directionVector.x && - lineAnalyze.directionVector.y !== analyze.directionVector.y) - ) - }) - .filter((line) => { - //라인이 현재라인에 overlap 되거나, 현재 라인을 full overlap하는지 확인. - if (analyze.isHorizontal) { - const currentMinX = Math.min(currentLine.x1, currentLine.x2) - const currentMaxX = Math.max(currentLine.x1, currentLine.x2) - const minX = Math.min(line.x1, line.x2) - const maxX = Math.max(line.x1, line.x2) - //full overlap - if (minX <= currentMinX && maxX >= currentMaxX) { - return true - } - //라인 포인트중 하나라도 현재 라인의 범위에 들어와있으면 overlap으로 판단. - if ((currentMinX <= minX && minX <= currentMaxX) || (currentMinX <= maxX && maxX <= currentMaxX)) { - return true - } - } - if (analyze.isVertical) { - const currentMinY = Math.min(currentLine.y1, currentLine.y2) - const currentMaxY = Math.max(currentLine.y1, currentLine.y2) - const minY = Math.min(line.y1, line.y2) - const maxY = Math.max(line.y1, line.y2) - //full overlap - if (minY <= currentMinY && maxY >= currentMaxY) { - return true - } - //라인 포인트중 하나라도 현재 라인의 범위에 들어와있으면 overlap으로 판단. - if ((currentMinY <= minY && minY <= currentMaxY) || (currentMinY <= maxY && maxY <= currentMaxY)) { - return true - } - } - return false - }) - .forEach((line, i) => { - const checkLine = new fabric.Line([line.x1, line.y1, line.x2, line.y2], { - stroke: 'green', - strokeWidth: 4, - parentId: roofId, - name: 'check', - }) - canvas.add(checkLine) - canvas.renderAll() - })*!/ - - // innerLines.push(drawRoofLine(roofPoint, canvas, roof, textMode)) - }) - }*/ - - /*if (linesAnalysis.length > 0) { - linesAnalysis - .filter((line) => { - const dx = line.end.x - line.start.x - const dy = line.end.y - line.start.y - const length = Math.sqrt(dx ** 2 + dy ** 2) - return length > 0 - }) - .forEach((line) => { - const startOnLine = roof.lines.find((l) => isPointOnLineNew(l, line.start)) - const endOnLine = roof.lines.find((l) => isPointOnLineNew(l, line.end)) - console.log('startOnLine, endOnLine: ', startOnLine, endOnLine) - const allLinesPoints = [] - innerLines.forEach((innerLine) => { - allLinesPoints.push({ x: innerLine.x1, y: innerLine.y1 }, { x: innerLine.x2, y: innerLine.y2 }) - }) - - if ( - allLinesPoints.filter((p) => almostEqual(p.x, line.start.x) && almostEqual(p.y, line.start.y)).length < 3 && - allLinesPoints.filter((p) => almostEqual(p.x, line.end.x) && almostEqual(p.y, line.end.y)).length === 0 - ) { - if (startOnLine || endOnLine) { - if (line.degree === 0) { - innerLines.push(drawRoofLine([line.start.x, line.start.y, line.end.x, line.end.y], canvas, roof, textMode)) - } else { - innerLines.push( - drawHipLine([line.start.x, line.start.y, line.end.x, line.end.y], canvas, roof, textMode, null, line.degree, line.degree), - ) - } - } - } - }) - }*/ - - //지붕선에 따라 라인추가 작업 처리. - /*const innerLinesPoints = [] - innerLines.forEach((line) => { - const hasCoord1 = innerLinesPoints.find((p) => p.x === line.x1 && p.y === line.y1) - const hasCoord2 = innerLinesPoints.find((p) => p.x === line.x2 && p.y === line.y2) - if (!hasCoord1) innerLinesPoints.push({ x: line.x1, y: line.y1 }) - if (!hasCoord2) innerLinesPoints.push({ x: line.x2, y: line.y2 }) - }) - roof.lines.forEach((line) => { - const splitPoint = [] - let hasOverlapLine = false - const minX = Math.min(line.x1, line.x2) - const maxX = Math.max(line.x1, line.x2) - const minY = Math.min(line.y1, line.y2) - const maxY = Math.max(line.y1, line.y2) - innerLines.forEach((innerLine) => { - const innerLineMinX = Math.min(innerLine.x1, innerLine.x2) - const innerLineMaxX = Math.max(innerLine.x1, innerLine.x2) - const innerLineMinY = Math.min(innerLine.y1, innerLine.y2) - const innerLineMaxY = Math.max(innerLine.y1, innerLine.y2) - if (innerLineMinX <= minX && innerLineMaxX >= maxX && innerLineMinY <= minY && innerLineMaxY >= maxY) { - hasOverlapLine = true - } - if (minX <= innerLineMinX && maxX >= innerLineMaxX && minY <= innerLineMinY && maxY >= innerLineMaxY) { - hasOverlapLine = true - } - }) - if (hasOverlapLine) return - - innerLinesPoints.forEach((point) => { - if ( - isPointOnLineNew(line, point) && - !(almostEqual(line.x1, point.x) && almostEqual(line.y1, point.y)) && - !(almostEqual(line.x2, point.x) && almostEqual(line.y2, point.y)) - ) { - const distance = Math.sqrt((point.x - line.x1) ** 2 + (point.y - line.y1) ** 2) - splitPoint.push({ point, distance }) - } - }) - if (splitPoint.length > 0) { - splitPoint.sort((a, b) => a[1] - b[1]) - let startPoint = { x: line.x1, y: line.y1 } - for (let i = 0; i < splitPoint.length; i++) { - const point = splitPoint[i].point - innerLines.push(drawRoofLine([startPoint.x, startPoint.y, point.x, point.y], canvas, roof, textMode)) - startPoint = point - } - innerLines.push(drawRoofLine([startPoint.x, startPoint.y, line.x2, line.y2], canvas, roof, textMode)) - } else { - innerLines.push(drawRoofLine([line.x1, line.y1, line.x2, line.y2], canvas, roof, textMode)) - } - })*/ } /** diff --git a/src/util/skeleton-utils.js b/src/util/skeleton-utils.js index de7bd91f..943f1f38 100644 --- a/src/util/skeleton-utils.js +++ b/src/util/skeleton-utils.js @@ -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. 연결이 끊어진 라인이 있을경우 찾아서 추가한다(동 이동일때) @@ -549,17 +527,6 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => { { 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], { @@ -596,9 +563,6 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => { }); - - - }else{ @@ -608,906 +572,908 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => { canvas.renderAll(); }); - //if((roof.moveUpDown??0 > 0) ) { + if (Math.abs(roof.moveUpDown ?? 0) > 0 || Math.abs(roof.moveFlowLine ?? 0) > 0) { + const getMoveUpDownLine = () => { + // 같은 라인이 없으므로 새 다각형 라인 생성 + //라인 편집 + // let i = 0 + 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 i = 0 - 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) + roofLineRects.forEach((roofLineRect) => { + canvas.remove(roofLineRect) + canvas.renderAll() + }) + let helpLines = canvas.getObjects().filter((obj) => obj.lineName === 'helpLine' && obj.roofId === roofId) + helpLines.forEach((helpLine) => { + canvas.remove(helpLine) + canvas.renderAll() + }) - roofLineRects.forEach((roofLineRect) => { - canvas.remove(roofLineRect) - canvas.renderAll() - }) + function sortCurrentRoofLines(lines) { + return [...lines].sort((a, b) => { + // Get all coordinates in a consistent order + const getCoords = (line) => { + const x1 = line.x1 ?? line.get('x1') + const y1 = line.y1 ?? line.get('y1') + const x2 = line.x2 ?? line.get('x2') + const y2 = line.y2 ?? line.get('y2') - let helpLines = canvas.getObjects().filter((obj) => obj.lineName === 'helpLine' && obj.roofId === roofId) - helpLines.forEach((helpLine) => { - canvas.remove(helpLine) - canvas.renderAll() - }) - - function sortCurrentRoofLines(lines) { - return [...lines].sort((a, b) => { - // Get all coordinates in a consistent order - const getCoords = (line) => { - const x1 = line.x1 ?? line.get('x1'); - const y1 = line.y1 ?? line.get('y1'); - const x2 = line.x2 ?? line.get('x2'); - const y2 = line.y2 ?? line.get('y2'); - - // Sort points left-to-right, then top-to-bottom - return x1 < x2 || (x1 === x2 && y1 < y2) - ? [x1, y1, x2, y2] - : [x2, y2, x1, y1]; - }; - - const aCoords = getCoords(a); - const bCoords = getCoords(b); - - // Compare each coordinate in order - for (let i = 0; i < 4; i++) { - if (Math.abs(aCoords[i] - bCoords[i]) > 0.1) { - return aCoords[i] - bCoords[i]; + // Sort points left-to-right, then top-to-bottom + return x1 < x2 || (x1 === x2 && y1 < y2) ? [x1, y1, x2, y2] : [x2, y2, x1, y1] } - } - return 0; - }); + + const aCoords = getCoords(a) + const bCoords = getCoords(b) + + // Compare each coordinate in order + for (let i = 0; i < 4; i++) { + if (Math.abs(aCoords[i] - bCoords[i]) > 0.1) { + return aCoords[i] - bCoords[i] + } + } + return 0 + }) + } + + // 각 라인 집합 정렬 + const sortWallLines = ensureCounterClockwiseLines(wallLines) + const sortWallBaseLines = ensureCounterClockwiseLines(wall.baseLines) + const sortRoofLines = ensureCounterClockwiseLines(roofLines) + + // roofLines의 방향에 맞춰 currentRoofLines의 방향을 조정 + const alignLineDirection = (sourceLines, targetLines) => { + return sourceLines.map((sourceLine) => { + // 가장 가까운 targetLine 찾기 + const nearestTarget = targetLines.reduce((nearest, targetLine) => { + const sourceCenter = { + x: (sourceLine.x1 + sourceLine.x2) / 2, + y: (sourceLine.y1 + sourceLine.y2) / 2, + } + const targetCenter = { + x: (targetLine.x1 + targetLine.x2) / 2, + y: (targetLine.y1 + targetLine.y2) / 2, + } + const distance = Math.hypot(sourceCenter.x - targetCenter.x, sourceCenter.y - targetCenter.y) + + return !nearest || distance < nearest.distance ? { line: targetLine, distance } : nearest + }, null)?.line + + if (!nearestTarget) return sourceLine + + // 방향이 반대인지 확인 (벡터 내적을 사용) + const sourceVec = { + x: sourceLine.x2 - sourceLine.x1, + y: sourceLine.y2 - sourceLine.y1, + } + const targetVec = { + x: nearestTarget.x2 - nearestTarget.x1, + y: nearestTarget.y2 - nearestTarget.y1, + } + + const dotProduct = sourceVec.x * targetVec.x + sourceVec.y * targetVec.y + + // 내적이 음수이면 방향이 반대이므로 뒤집기 + if (dotProduct < 0) { + return { + ...sourceLine, + x1: sourceLine.x2, + y1: sourceLine.y2, + x2: sourceLine.x1, + y2: sourceLine.y1, + } + } + + return sourceLine + }) + } + + console.log('wallBaseLines', wall.baseLines) + + //wall.baseLine은 움직인라인 + let movedLines = [] + + // 조건에 맞는 라인들만 필터링 + const validWallLines = [...wallLines].sort((a, b) => a.idx - b.idx).filter((wallLine, index) => wallLine.idx - 1 === index) + + console.log('', sortRoofLines, sortWallLines, sortWallBaseLines) + sortWallLines.length > 3 && + sortWallLines.forEach((wallLine, index) => { + + const roofLine = sortRoofLines[index] + const wallBaseLine = sortWallBaseLines[index] + + //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)) + + if (isSameLine2(wallBaseLine, wallLine)) { + return + } + + const movedStart = Math.abs(wallBaseLine.x1 - wallLine.x1) > EPSILON || Math.abs(wallBaseLine.y1 - wallLine.y1) > EPSILON + const movedEnd = Math.abs(wallBaseLine.x2 - wallLine.x2) > EPSILON || Math.abs(wallBaseLine.y2 - wallLine.y2) > EPSILON + + const fullyMoved = movedStart && movedEnd + + //반시계 방향 + let newPStart //= {x:roofLine.x1, y:roofLine.y1} + let newPEnd //= {x:movedLines.x2, y:movedLines.y2} + + //현재 roof는 무조건 시계방향 + + const getAddLine = (p1, p2, stroke = '') => { + movedLines.push({ index, p1, p2 }) + + //console.log("mergeLines:::::::", mergeLines); + const line = new QLine([p1.x, p1.y, p2.x, p2.y], { + parentId: roof.id, + fontSize: roof.fontSize, + stroke: 'black', + strokeWidth: 4, + name: 'eaveHelpLine', + lineName: 'eaveHelpLine', + visible: true, + roofId: roofId, + selectable: true, + hoverCursor: 'pointer', + attributes: { + type: 'eaveHelpLine', + isStart: true, + pitch: wallLine.attributes.pitch, + }, + }) + + //coordinateText(line) + canvas.add(line) + line.bringToFront() + canvas.renderAll() + return line + } + + //getAddLine(roofLine.startPoint, roofLine.endPoint, ) //외곽선을 그린다 + + newPStart = { x: roofLine.x1, y: roofLine.y1 } + newPEnd = { x: roofLine.x2, y: roofLine.y2 } + + const getInnerLines = (lines, point) => {} + let isIn = false + let isOut = false + + //두 포인트가 변경된 라인인 + if (fullyMoved) { + //반시계방향향 + + const mLine = getSelectLinePosition(wall, wallBaseLine) + + if (getOrientation(roofLine) === 'vertical') { + if (['left', 'right'].includes(mLine.position)) { + if (Math.abs(wallLine.x1 - wallBaseLine.x1) < 0.1 || Math.abs(wallLine.x2 - wallBaseLine.x2) < 0.1) { + return false + } + const isLeftPosition = mLine.position === 'left' + const isRightPosition = mLine.position === 'right' + const isInPosition = + (isLeftPosition && wallLine.x1 < wallBaseLine.x1) || + (isRightPosition && wallLine.x1 > wallBaseLine.x1) || + (isLeftPosition && wallLine.x2 < wallBaseLine.x2) || + (isRightPosition && wallLine.x2 > wallBaseLine.x2) + + const positionType = isInPosition ? 'in' : 'out' + + const condition = `${mLine.position}_${positionType}` + let isStartEnd = findInteriorPoint(wallBaseLine, sortWallBaseLines) + let sPoint, ePoint + if (condition === 'left_in') { + isIn = true + + if (isStartEnd.start) { + newPEnd.y = roofLine.y2 + newPEnd.x = roofLine.x2 + + const moveDist = Big(wallBaseLine.x1).minus(wallLine.x1).abs().toNumber() + ePoint = { x: wallBaseLine.x1, y: wallBaseLine.y1 } + newPStart.y = wallBaseLine.y1 + + findPoints.push({ x: ePoint.x, y: ePoint.y, position: 'left_in_start' }) + const newPointX = Big(roofLine.x1).plus(moveDist).abs().toNumber() + const pDist = Big(wallLine.x1).minus(roofLine.x1).toNumber() + const pLineY = Big(roofLine.y1).minus(0).abs().toNumber() + // let idx = 0 > index - 1 ? sortRoofLines.length : index + // 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: 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') + getAddLine({ x: newPointX, y: pLineY }, { x: ePoint.x, y: ePoint.y }, 'pink') + } + } + + if (isStartEnd.end) { + newPStart.y = roofLine.y1 + newPStart.x = roofLine.x1 + + const moveDist = Big(wallBaseLine.x2).minus(wallLine.x2).abs().toNumber() + ePoint = { x: wallBaseLine.x2, y: wallBaseLine.y2 } + newPEnd.y = wallBaseLine.y2 + + findPoints.push({ x: ePoint.x, y: ePoint.y, position: 'left_in_end' }) + const newPointX = Big(roofLine.x1).plus(moveDist).toNumber() + const pDist = Big(wallLine.x1).minus(roofLine.x1).abs().toNumber() + const pLineY = Big(roofLine.y2).minus(0).toNumber() + // let idx = sortRoofLines.length < index + 1 ? 0 : index + // 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: 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') + getAddLine({ x: newPointX, y: pLineY }, { x: ePoint.x, y: ePoint.y }, 'pink') + } + //getAddLine({ x: roofLine.x2, y: roofLine.y2 }, { x: newPointX, y: roofLine.y2 }, 'orange') + } + } else if (condition === 'left_out') { + console.log('left_out::::isStartEnd:::::', isStartEnd) + if (isStartEnd.start) { + const moveDist = Big(wallLine.x1).minus(wallBaseLine.x1).abs().toNumber() + const aStartY = Big(roofLine.y1).minus(moveDist).abs().toNumber() + const bStartY = Big(wallLine.y1).minus(moveDist).abs().toNumber() + const inLine = findLineContainingPoint(innerLines, { y: aStartY, x: roofLine.x2 }) + + const eLineY = Big(bStartY).minus(wallLine.y1).abs().toNumber() + newPStart.y = aStartY + newPEnd.y = roofLine.y2 //Big(roofLine.y2).minus(eLineY).toNumber() + // let idx = 0 >= index - 1 ? sortRoofLines.length : index + // 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.y1 - wallLine.y1) < 0.1) { + if (inLine) { + if (inLine.x1 < inLine.x2) { + getAddLine({ y: bStartY, x: wallLine.x2 }, { y: inLine.y2, x: inLine.x2 }, 'pink') + } else { + getAddLine({ y: inLine.y2, x: inLine.x2 }, { y: bStartY, x: wallLine.x2 }, 'pink') + } + } + getAddLine({ y: bStartY, x: wallLine.x2 }, { y: roofLine.y1, x: wallLine.x1 }, 'magenta') + getAddLine({ y: newLine.y1, x: newLine.x1 }, { y: newLine.y2, x: wallLine.x2 }, 'Gray') + findPoints.push({ y: aStartY, x: newPStart.x, position: 'left_out_start' }) + } else { + const cLineY = Big(wallBaseLine.x1).minus(wallLine.x1).abs().toNumber() + newPStart.y = Big(newPStart.y).minus(cLineY).toNumber() + const inLine = findLineContainingPoint(innerLines, { y: newPStart.y, x: newPStart.x }) + if (inLine) { + if (inLine.x1 < inLine.x2) { + getAddLine({ y: newPStart.y, x: newPStart.x }, { y: inLine.y2, x: inLine.x2 }, 'purple') + } else { + getAddLine({ y: inLine.y1, x: inLine.x1 }, { y: newPStart.y, x: newPStart.x }, 'purple') + } + } else { + //newPStart.y = wallLine.y1; + //외곽 라인 그리기 + const rLineM = Big(wallBaseLine.x2).minus(roofLine.x2).abs().toNumber() + newPStart.y = Big(wallBaseLine.y1).minus(rLineM).toNumber() + const inLine = findLineContainingPoint(innerLines, { y: newPStart.y, x: newPStart.x }) + if (inLine) { + if (inLine.x2 > inLine.x1) { + getAddLine({ y: newPStart.y, x: newPStart.x }, { y: inLine.y2, x: inLine.x2 }, 'purple') + } else { + getAddLine({ y: inLine.y1, x: inLine.x1 }, { y: newPEnd.y, x: newPEnd.x }, 'purple') + } + } + } + } + } + + if (isStartEnd.end) { + 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() + const inLine = findLineContainingPoint(innerLines, { y: aStartY, x: roofLine.x1 }) + console.log('startLines:::::::', inLine) + const eLineY = Big(bStartY).minus(wallLine.y2).abs().toNumber() + newPEnd.y = aStartY + newPStart.y = roofLine.y1 //Big(roofLine.y1).plus(eLineY).toNumber() + // let idx = sortRoofLines.length < index + 1 ? 0 : index + // 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.y2 - wallLine.y2) < 0.1) { + if (inLine) { + if (inLine.x1 < inLine.x2) { + getAddLine({ y: bStartY, x: wallLine.x1 }, { y: inLine.y2, x: inLine.x2 }, 'pink') + } else { + getAddLine({ y: inLine.y1, x: inLine.x1 }, { y: bStartY, x: wallLine.x1 }, 'pink') + } + } + getAddLine({ y: bStartY, x: wallLine.x1 }, { y: roofLine.y2, x: wallLine.x2 }, 'magenta') + getAddLine({ y: newLine.y2, x: newLine.x2 }, { y: newLine.y1, x: wallLine.x1 }, 'Gray') + findPoints.push({ y: aStartY, x: newPEnd.x, position: 'left_out_end' }) + } else { + const cLineY = Big(wallBaseLine.x2).minus(wallLine.x2).abs().toNumber() + newPEnd.y = Big(newPEnd.y).plus(cLineY).toNumber() + const inLine = findLineContainingPoint(innerLines, { y: newPEnd.y, x: newPEnd.x }) + if (inLine) { + if (inLine.x1 < inLine.x2) { + getAddLine({ y: newPEnd.y, x: newPEnd.x }, { y: inLine.y2, x: inLine.x2 }, 'purple') + } else { + getAddLine({ y: inLine.y1, x: inLine.x1 }, { y: newPEnd.y, x: newPEnd.x }, 'purple') + } + } else { + // newPEnd.y = wallLine.y2 + + //외곽 라인 그리기 + const rLineM = Big(wallBaseLine.x2).minus(roofLine.x2).abs().toNumber() + newPEnd.y = Big(wallBaseLine.y2).plus(rLineM).toNumber() + const inLine = findLineContainingPoint(innerLines, { y: newPEnd.y, x: newPEnd.x }) + if (inLine) { + if (inLine.x2 > inLine.x1) { + getAddLine({ y: newPEnd.y, x: newPEnd.x }, { y: inLine.y2, x: inLine.x2 }, 'purple') + } else { + getAddLine({ y: inLine.y1, x: inLine.x1 }, { y: newPEnd.y, x: newPEnd.x }, 'purple') + } + } + } + } + findPoints.push({ y: newPStart.y, x: newPEnd.x, position: 'left_out_end' }) + } + } else if (condition === 'right_in') { + if (isStartEnd.start) { + newPEnd.y = roofLine.y2 + newPEnd.x = roofLine.x2 + + const moveDist = Big(wallBaseLine.x1).minus(wallLine.x1).abs().toNumber() + ePoint = { x: wallBaseLine.x1, y: wallBaseLine.y1 } + newPStart.y = wallBaseLine.y1 + + findPoints.push({ x: ePoint.x, y: ePoint.y, position: 'right_in_start' }) + const newPointX = Big(roofLine.x1).minus(moveDist).abs().toNumber() + const pDist = Big(wallLine.x1).minus(roofLine.x1).abs().toNumber() + const pLineY = Big(roofLine.y1).minus(0).abs().toNumber() + // let idx = 0 >= index - 1 ? sortRoofLines.length : index + // 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: 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') + getAddLine({ x: newPointX, y: pLineY }, { x: ePoint.x, y: ePoint.y }, 'pink') + } + } + + if (isStartEnd.end) { + newPStart.y = roofLine.y1 + newPStart.x = roofLine.x1 + + const moveDist = Big(wallBaseLine.x2).minus(wallLine.x2).abs().toNumber() + ePoint = { x: wallBaseLine.x2, y: wallBaseLine.y2 } + newPEnd.y = wallBaseLine.y2 + + findPoints.push({ x: ePoint.x, y: ePoint.y, position: 'right_in_end' }) + const newPointX = Big(roofLine.x1).minus(moveDist).toNumber() + const pDist = Big(wallLine.x1).minus(roofLine.x1).abs().toNumber() + const pLineY = Big(roofLine.y2).minus(0).abs().toNumber() + // let idx = sortRoofLines.length < index + 1 ? 0 : index + // 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: 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') + getAddLine({ x: newPointX, y: pLineY }, { x: ePoint.x, y: ePoint.y }, 'pink') + } + getAddLine({ x: roofLine.x2, y: roofLine.y2 }, { x: newPointX, y: roofLine.y2 }, 'orange') + } + } else if (condition === 'right_out') { + console.log('right_out::::isStartEnd:::::', isStartEnd) + if (isStartEnd.start) { + //x1 inside + const moveDist = Big(wallLine.x1).minus(wallBaseLine.x1).abs().toNumber() + const aStartY = Big(roofLine.y1).plus(moveDist).abs().toNumber() + const bStartY = Big(wallLine.y1).plus(moveDist).abs().toNumber() + const inLine = findLineContainingPoint(innerLines, { y: aStartY, x: roofLine.x1 }) + console.log('startLines:::::::', inLine) + const eLineY = Big(bStartY).minus(wallLine.y1).abs().toNumber() + newPStart.y = aStartY + newPEnd.y = roofLine.y2 //Big(roofLine.y2).plus(eLineY).toNumber() + // let idx = 0 >= index - 1 ? sortRoofLines.length : index + // 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.y1 - wallLine.y1) < 0.1) { + if (inLine) { + if (inLine.x2 < inLine.x1) { + getAddLine({ y: bStartY, x: wallLine.x2 }, { y: inLine.y2, x: inLine.x2 }, 'pink') + } else { + getAddLine({ y: inLine.y1, x: inLine.x1 }, { y: bStartY, x: wallLine.x2 }, 'pink') + } + } + getAddLine({ y: bStartY, x: wallLine.x2 }, { y: roofLine.y1, x: wallLine.x1 }, 'magenta') + getAddLine({ y: newLine.y1, x: newLine.x1 }, { y: newLine.y2, x: wallLine.x2 }, 'Gray') + findPoints.push({ y: aStartY, x: newPEnd.x, position: 'right_out_start' }) + } else { + const cLineY = Big(wallBaseLine.x1).minus(wallLine.x1).abs().toNumber() + newPStart.y = Big(newPStart.y).plus(cLineY).toNumber() + const inLine = findLineContainingPoint(innerLines, { y: newPStart.y, x: newPStart.x }) + if (inLine) { + if (inLine.x2 < inLine.x1) { + getAddLine({ y: newPStart.y, x: newPStart.x }, { y: inLine.y2, x: inLine.x2 }, 'purple') + } else { + getAddLine({ y: inLine.y1, x: inLine.x1 }, { y: newPStart.y, x: newPStart.x }, 'purple') + } + } else { + //newPStart.y = wallLine.y1; + //외곽 라인 그리기 + const rLineM = Big(wallBaseLine.x1).minus(roofLine.x1).abs().toNumber() + newPStart.y = Big(wallBaseLine.y1).plus(rLineM).toNumber() + const inLine = findLineContainingPoint(innerLines, { y: newPStart.y, x: newPStart.x }) + if (inLine) { + if (inLine.x2 > inLine.x1) { + getAddLine({ y: newPStart.y, x: newPStart.x }, { y: inLine.y1, x: inLine.x1 }, 'purple') + } else { + getAddLine({ y: inLine.y2, x: inLine.x2 }, { y: newPStart.y, x: newPStart.x }, 'purple') + } + } + } + } + } + + if (isStartEnd.end) { + const moveDist = Big(wallLine.x1).minus(wallBaseLine.x1).abs().toNumber() + const aStartY = Big(roofLine.y2).minus(moveDist).abs().toNumber() + const bStartY = Big(wallLine.y2).minus(moveDist).abs().toNumber() + const inLine = findLineContainingPoint(innerLines, { y: aStartY, x: roofLine.x1 }) + console.log('startLines:::::::', inLine) + const eLineY = Big(bStartY).minus(wallLine.y2).abs().toNumber() + newPEnd.y = aStartY + newPStart.y = roofLine.y1 //Big(roofLine.y1).minus(eLineY).toNumber() + // let idx = sortRoofLines.length < index + 1 ? 0 : index + // const newLine = sortRoofLines[idx + 1] + + const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length; + const nextIndex = (index + 1) % sortRoofLines.length; + const newLine = sortRoofLines[prevIndex] + + if (inLine) { + if (inLine.x2 < inLine.x1) { + getAddLine({ y: bStartY, x: wallLine.x1 }, { y: inLine.y2, x: inLine.x2 }, 'pink') + } else { + getAddLine({ y: inLine.y1, x: inLine.x1 }, { y: bStartY, x: wallLine.x1 }, 'pink') + } + } + if (Math.abs(wallBaseLine.y2 - wallLine.y2) < 0.1) { + getAddLine({ y: bStartY, x: wallLine.x1 }, { y: roofLine.y2, x: wallLine.x2 }, 'magenta') + getAddLine({ y: newLine.y2, x: newLine.x2 }, { y: newLine.y1, x: wallLine.x1 }, 'Gray') + findPoints.push({ y: aStartY, x: newPEnd.x, position: 'right_out_end' }) + } else { + const cLineY = Big(wallBaseLine.x2).minus(wallLine.x2).abs().toNumber() + newPEnd.y = Big(newPEnd.y).minus(cLineY).toNumber() + const inLine = findLineContainingPoint(innerLines, { y: newPEnd.y, x: newPEnd.x }) + if (inLine) { + if (inLine.x2 < inLine.x1) { + getAddLine({ y: newPEnd.y, x: newPEnd.x }, { y: inLine.y2, x: inLine.x2 }, 'purple') + } else { + getAddLine({ y: inLine.y1, x: inLine.x1 }, { y: newPEnd.y, x: newPEnd.x }, 'purple') + } + } else { + //newPEnd.y = wallLine.y2; + + //외곽 라인 그리기 + const rLineM = Big(wallBaseLine.x2).minus(roofLine.x2).abs().toNumber() + newPEnd.y = Big(wallBaseLine.y2).minus(rLineM).toNumber() + const inLine = findLineContainingPoint(innerLines, { y: newPEnd.y, x: newPEnd.x }) + if (inLine) { + if (inLine.x2 > inLine.x1) { + getAddLine({ y: newPEnd.y, x: newPEnd.x }, { y: inLine.y1, x: inLine.x1 }, 'purple') + } else { + getAddLine({ y: inLine.y2, x: inLine.x2 }, { y: newPEnd.y, x: newPEnd.x }, 'purple') + } + } + } + } + } + } + } + } else if (getOrientation(roofLine) === 'horizontal') { + //red + + if (['top', 'bottom'].includes(mLine.position)) { + if (Math.abs(wallLine.y1 - wallBaseLine.y1) < 0.1 || Math.abs(wallLine.y2 - wallBaseLine.y2) < 0.1) { + return false + } + const isTopPosition = mLine.position === 'top' + const isBottomPosition = mLine.position === 'bottom' + const isInPosition = + (isTopPosition && wallLine.y1 < wallBaseLine.y1) || + (isBottomPosition && wallLine.y1 > wallBaseLine.y1) || + (isTopPosition && wallLine.y2 < wallBaseLine.y2) || + (isBottomPosition && wallLine.y2 > wallBaseLine.y2) + + const positionType = isInPosition ? 'in' : 'out' + const condition = `${mLine.position}_${positionType}` + let isStartEnd = findInteriorPoint(wallBaseLine, sortWallBaseLines) + + let sPoint, ePoint + + if (condition === 'top_in') { + if (isStartEnd.start) { + const moveDist = Big(wallLine.y1).minus(wallBaseLine.y1).abs().toNumber() + sPoint = { x: wallBaseLine.x1, y: wallBaseLine.y1 } + newPStart.x = wallBaseLine.x1 + + const newPointY = Big(roofLine.y2).plus(moveDist).toNumber() + + const pDist = Big(wallLine.y2).minus(roofLine.y2).abs().toNumber() + const pLineX = Big(roofLine.x1).minus(0).toNumber() + // 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') + findPoints.push({ x: sPoint.x, y: sPoint.y, position: 'top_in_start' }) + + if (Math.abs(wallBaseLine.x1 - wallLine.x1) < 0.1) { + getAddLine({ x: pLineX, y: pLineY }, { x: pLineX, y: newPointY }, 'green') + getAddLine({ x: pLineX, y: newPointY }, { x: sPoint.x, y: sPoint.y }, 'pink') + } + //getAddLine({ x: roofLine.x2, y: roofLine.y2 }, { x: roofLine.x2, y: newPointY }, 'orange') + } + + if (isStartEnd.end) { + const moveDist = Big(wallLine.y2).minus(wallBaseLine.y2).abs().toNumber() + sPoint = { x: wallBaseLine.x2, y: wallBaseLine.y2 } + newPEnd.x = wallBaseLine.x2 + + const newPointY = Big(roofLine.y1).plus(moveDist).toNumber() + + const pDist = Big(wallLine.y1).minus(roofLine.y1).abs().toNumber() + const pLineX = Big(roofLine.x2).minus(0).abs().toNumber() + + // 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') + findPoints.push({ x: sPoint.x, y: sPoint.y, position: 'top_in_end' }) + + if (Math.abs(wallBaseLine.x2 - wallLine.x2) < 0.1) { + getAddLine({ x: pLineX, y: pLineY }, { x: pLineX, y: newPointY }, 'green') + getAddLine({ x: pLineX, y: newPointY }, { x: sPoint.x, y: sPoint.y }, 'pink') + } + + //getAddLine({ x: roofLine.x1, y: roofLine.y1 }, { x: roofLine.x1, y: newPointY }, 'orange') + } + } else if (condition === 'top_out') { + console.log('top_out isStartEnd:::::::', isStartEnd) + + if (isStartEnd.start) { + const moveDist = Big(wallLine.y1).minus(wallBaseLine.y1).abs().toNumber() + const aStartX = Big(roofLine.x1).plus(moveDist).toNumber() + const bStartX = Big(wallLine.x1).plus(moveDist).toNumber() + const inLine = findLineContainingPoint(innerLines, { x: aStartX, y: newPEnd.y }) + + const eLineX = Big(bStartX).minus(wallLine.x1).abs().toNumber() + newPEnd.x = roofLine.x2 //Big(newPEnd.x).plus(eLineX).toNumber() + newPStart.x = aStartX + // let idx = 0 > index - 1 ? sortRoofLines.length : index + // 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.x1 - wallLine.x1) < 0.1) { + if (inLine) { + if (inLine.y2 > inLine.y1) { + getAddLine({ x: bStartX, y: wallLine.y1 }, { x: inLine.x2, y: inLine.y2 }, 'pink') + } else { + getAddLine({ x: inLine.x1, y: inLine.y1 }, { x: bStartX, y: wallLine.y1 }, 'pink') + } + } + getAddLine({ x: bStartX, y: wallLine.y1 }, { x: roofLine.x1, y: wallLine.y1 }, 'magenta') + getAddLine({ x: newLine.x1, y: newLine.y1 }, { x: newLine.x1, y: wallLine.y1 }, 'Gray') + findPoints.push({ x: aStartX, y: newPEnd.y, position: 'top_out_start' }) + } else { + const cLineX = Big(wallBaseLine.y1).minus(wallLine.y1).abs().toNumber() + newPStart.x = Big(newPStart.x).plus(cLineX).toNumber() + const inLine = findLineContainingPoint(innerLines, { y: newPStart.y, x: newPStart.x }) + if (inLine) { + if (inLine.y2 > inLine.y1) { + getAddLine({ y: newPStart.y, x: newPStart.x }, { y: inLine.y2, x: inLine.x2 }, 'purple') + } else { + getAddLine({ y: inLine.y1, x: inLine.x1 }, { y: newPStart.y, x: newPStart.x }, 'purple') + } + } else { + //외곽 라인 그리기 + const rLineM = Big(wallBaseLine.y1).minus(roofLine.y1).abs().toNumber() + newPStart.x = Big(wallBaseLine.x1).plus(rLineM).toNumber() + const inLine = findLineContainingPoint(innerLines, { y: newPStart.y, x: newPStart.x }) + if (inLine) { + if (inLine.y2 > inLine.y1) { + getAddLine({ y: newPStart.y, x: newPStart.x }, { y: inLine.y2, x: inLine.x2 }, 'purple') + } else { + getAddLine({ y: inLine.y1, x: inLine.x1 }, { y: newPStart.y, x: newPStart.x }, 'purple') + } + } + } + } + } + if (isStartEnd.end) { + const moveDist = Big(wallLine.y1).minus(wallBaseLine.y1).abs().toNumber() + const aStartX = Big(roofLine.x2).minus(moveDist).abs().toNumber() + const bStartX = Big(wallLine.x2).minus(moveDist).abs().toNumber() + const inLine = findLineContainingPoint(innerLines, { x: aStartX, y: newPEnd.y }) + console.log('startLines:::::::', inLine) + const eLineX = Big(bStartX).minus(wallLine.x2).abs().toNumber() + newPStart.x = roofLine.x1 //Big(newPStart.x).minus(eLineX).abs().toNumber() + newPEnd.x = aStartX + // let idx = sortRoofLines.length < index + 1 ? 0 : index + // 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.x2 - wallLine.x2) < 0.1) { + if (inLine) { + if (inLine.y2 > inLine.y1) { + getAddLine({ x: bStartX, y: wallLine.y1 }, { x: inLine.x2, y: inLine.y2 }, 'pink') + } else { + getAddLine({ x: inLine.x1, y: inLine.y1 }, { x: bStartX, y: wallLine.y1 }, 'pink') + } + } + getAddLine({ x: bStartX, y: wallLine.y1 }, { x: roofLine.x2, y: wallLine.y2 }, 'magenta') + getAddLine({ x: newLine.x2, y: newLine.y2 }, { x: newLine.x1, y: wallLine.y1 }, 'Gray') + findPoints.push({ x: aStartX, y: newPEnd.y, position: 'top_out_end' }) + } else { + const cLineX = Big(wallLine.y2).minus(wallBaseLine.y2).abs().toNumber() + newPEnd.x = Big(newPEnd.x).minus(cLineX).toNumber() + const inLine = findLineContainingPoint(innerLines, { y: newPEnd.y, x: newPEnd.x }) + if (inLine) { + if (inLine.y2 > inLine.y1) { + getAddLine({ y: newPEnd.y, x: newPEnd.x }, { y: inLine.y2, x: inLine.x2 }, 'purple') + } else { + getAddLine({ y: inLine.y1, x: inLine.x1 }, { y: newPEnd.y, x: newPEnd.x }, 'purple') + } + } else { + //newPEnd.x = wallLine.x2; + //외곽 라인 그리기 + const rLineM = Big(wallBaseLine.y2).minus(roofLine.y2).abs().toNumber() + newPEnd.x = Big(wallBaseLine.x2).minus(rLineM).toNumber() + const inLine = findLineContainingPoint(innerLines, { y: newPEnd.y, x: newPEnd.x }) + if (inLine) { + if (inLine.y1 > inLine.y2) { + getAddLine({ y: newPEnd.y, x: newPEnd.x }, { y: inLine.y1, x: inLine.x1 }, 'purple') + } else { + getAddLine({ y: inLine.y2, x: inLine.x2 }, { y: newPEnd.y, x: newPEnd.x }, 'purple') + } + } + } + } + } + } else if (condition === 'bottom_in') { + if (isStartEnd.start) { + const moveDist = Big(wallLine.y1).minus(wallBaseLine.y1).abs().toNumber() + sPoint = { x: wallBaseLine.x1, y: wallBaseLine.y1 } + newPStart.x = wallBaseLine.x1 + + const newPointY = Big(roofLine.y2).minus(moveDist).toNumber() + + const pDist = Big(wallLine.y2).minus(roofLine.y2).abs().toNumber() + const pLineX = Big(roofLine.x1).minus(0).abs().toNumber() + + // 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') + findPoints.push({ x: sPoint.x, y: sPoint.y, position: 'bottom_in_start' }) + + if (Math.abs(wallBaseLine.x1 - wallLine.x1) < 0.1) { + getAddLine({ x: pLineX, y: pLineY }, { x: pLineX, y: newPointY }, 'green') + getAddLine({ x: pLineX, y: newPointY }, { x: sPoint.x, y: sPoint.y }, 'pink') + } + getAddLine({ x: roofLine.x2, y: roofLine.y2 }, { x: roofLine.x2, y: newPointY }, 'orange') + } + + if (isStartEnd.end) { + const moveDist = Big(wallLine.y2).minus(wallBaseLine.y2).abs().toNumber() + sPoint = { x: wallBaseLine.x2, y: wallBaseLine.y2 } + newPEnd.x = wallBaseLine.x2 + + const newPointY = Big(roofLine.y1).minus(moveDist).toNumber() + + const pDist = Big(wallLine.y1).minus(roofLine.y1).abs().toNumber() + const pLineX = Big(roofLine.x2).minus(0).abs().toNumber() + + // 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') + findPoints.push({ x: sPoint.x, y: sPoint.y, position: 'bottom_in_end' }) + + if (Math.abs(wallBaseLine.x2 - wallLine.x2) < 0.1) { + getAddLine({ x: pLineX, y: pLineY }, { x: pLineX, y: newPointY }, 'green') + getAddLine({ x: pLineX, y: newPointY }, { x: sPoint.x, y: sPoint.y }, 'pink') + } + getAddLine({ x: roofLine.x1, y: roofLine.y1 }, { x: roofLine.x1, y: newPointY }, 'orange') + } + } else if (condition === 'bottom_out') { + console.log('bottom_out isStartEnd:::::::', isStartEnd) + if (isStartEnd.start) { + const moveDist = Big(wallLine.y1).minus(wallBaseLine.y1).abs().toNumber() + const aStartX = Big(roofLine.x1).minus(moveDist).abs().toNumber() + const bStartX = Big(wallLine.x1).minus(moveDist).abs().toNumber() + const inLine = findLineContainingPoint(innerLines, { x: aStartX, y: roofLine.y1 }) + console.log('startLines:::::::', inLine) + const eLineX = Big(bStartX).minus(wallLine.x1).abs().toNumber() + newPEnd.x = roofLine.x2 //Big(roofLine.x2).minus(eLineX).toNumber() + newPStart.x = aStartX + // let idx = 0 > index - 1 ? sortRoofLines.length : index + // 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.x1 - wallLine.x1) < 0.1) { + if (inLine) { + if (inLine.y2 < inLine.y1) { + getAddLine({ x: bStartX, y: wallLine.y1 }, { x: inLine.x2, y: inLine.y2 }, 'pink') + } else { + getAddLine({ x: inLine.x1, y: inLine.y1 }, { x: bStartX, y: wallLine.y1 }, 'pink') + } + } + getAddLine({ x: bStartX, y: wallLine.y1 }, { x: roofLine.x1, y: wallLine.y1 }, 'magenta') + getAddLine({ x: newLine.x1, y: newLine.y1 }, { x: newLine.x1, y: wallLine.y1 }, 'Gray') + findPoints.push({ x: aStartX, y: newPEnd.y, position: 'bottom_out_start' }) + } else { + const cLineX = Big(wallBaseLine.y1).minus(wallLine.y1).abs().toNumber() + newPStart.x = Big(newPStart.x).minus(cLineX).toNumber() + const inLine = findLineContainingPoint(innerLines, { y: newPStart.y, x: newPStart.x }) + if (inLine) { + if (inLine.y2 < inLine.y1) { + getAddLine({ y: newPStart.y, x: newPStart.x }, { y: inLine.y2, x: inLine.x2 }, 'purple') + } else { + getAddLine({ y: inLine.y1, x: inLine.x1 }, { y: newPStart.y, x: newPStart.x }, 'purple') + } + } else { + //newPStart.x = wallLine.x1; + //외곽 라인 그리기 + const rLineM = Big(wallBaseLine.y1).minus(roofLine.y1).abs().toNumber() + newPStart.x = Big(wallBaseLine.x1).minus(rLineM).toNumber() + const inLine = findLineContainingPoint(innerLines, { y: newPStart.y, x: newPStart.x }) + if (inLine) { + if (inLine.y2 > inLine.y1) { + getAddLine({ y: newPStart.y, x: newPStart.x }, { y: inLine.y1, x: inLine.x1 }, 'purple') + } else { + getAddLine({ y: inLine.y2, x: inLine.x2 }, { y: newPStart.y, x: newPStart.x }, 'purple') + } + } + } + } + } + + if (isStartEnd.end) { + 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() + const inLine = findLineContainingPoint(innerLines, { x: aStartX, y: roofLine.y1 }) + console.log('startLines:::::::', inLine) + const eLineX = Big(bStartX).minus(wallLine.x2).abs().toNumber() + newPEnd.x = aStartX + newPStart.x = roofLine.x1 //Big(roofLine.x1).plus(eLineX).toNumber() + // let idx = sortRoofLines.length < index + 1 ? 0 : index + // 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.x2 - wallLine.x2) < 0.1) { + if (inLine) { + if (inLine.y2 < inLine.y1) { + getAddLine({ x: bStartX, y: wallLine.y1 }, { x: inLine.x2, y: inLine.y2 }, 'pink') + } else { + getAddLine({ x: inLine.x1, y: inLine.y1 }, { x: bStartX, y: wallLine.y1 }, 'pink') + } + } + getAddLine({ x: bStartX, y: wallLine.y1 }, { x: roofLine.x2, y: wallLine.y2 }, 'magenta') + getAddLine({ x: newLine.x2, y: newLine.y2 }, { x: newLine.x1, y: wallLine.y1 }, 'Gray') + findPoints.push({ x: aStartX, y: newPEnd.y, position: 'bottom_out_end' }) + } else { + const cLineX = Big(wallBaseLine.y2).minus(wallLine.y2).abs().toNumber() + newPEnd.x = Big(newPEnd.x).plus(cLineX).toNumber() + const inLine = findLineContainingPoint(innerLines, { y: newPEnd.y, x: newPEnd.x }) + if (inLine) { + if (inLine.y2 < inLine.y1) { + getAddLine({ y: newPEnd.y, x: newPEnd.x }, { y: inLine.y2, x: inLine.x2 }, 'purple') + } else { + getAddLine({ y: inLine.y1, x: inLine.x1 }, { y: newPEnd.y, x: newPEnd.x }, 'purple') + } + } else { + //newPEnd.x = wallLine.x2; + //외곽 라인 그리기 + const rLineM = Big(wallBaseLine.y2).minus(roofLine.y2).abs().toNumber() + newPEnd.x = Big(wallBaseLine.x2).plus(rLineM).toNumber() + const inLine = findLineContainingPoint(innerLines, { y: newPEnd.y, x: newPEnd.x }) + if (inLine) { + if (inLine.y1 > inLine.y2) { + getAddLine({ y: newPEnd.y, x: newPEnd.x }, { y: inLine.y2, x: inLine.x2 }, 'purple') + } else { + getAddLine({ y: inLine.y1, x: inLine.x1 }, { y: newPEnd.y, x: newPEnd.x }, 'purple') + } + } + } + } + } + } + } + } + + getAddLine(newPStart, newPEnd, 'red') + //canvas.remove(roofLine) + } else { + getAddLine(roofLine.startPoint, roofLine.endPoint) + } + + canvas.renderAll() + }) } + getMoveUpDownLine() - - // function sortCurrentRoofLines(lines) { - // return [...lines].sort((a, b) => { - // const aX = a.x1 ?? a.get('x1') - // const aY = a.y1 ?? a.get('y1') - // const bX = b.x1 ?? b.get('x1') - // const bY = b.y1 ?? b.get('y1') - - // if (aX !== bX) return aX - bX - // return aY - bY - // }) - // } - - - // 각 라인 집합 정렬 - - // roofLines의 방향에 맞춰 currentRoofLines의 방향을 조정 - const alignLineDirection = (sourceLines, targetLines) => { - return sourceLines.map(sourceLine => { - // 가장 가까운 targetLine 찾기 - const nearestTarget = targetLines.reduce((nearest, targetLine) => { - const sourceCenter = { - x: (sourceLine.x1 + sourceLine.x2) / 2, - y: (sourceLine.y1 + sourceLine.y2) / 2 - }; - const targetCenter = { - x: (targetLine.x1 + targetLine.x2) / 2, - y: (targetLine.y1 + targetLine.y2) / 2 - }; - const distance = Math.hypot( - sourceCenter.x - targetCenter.x, - sourceCenter.y - targetCenter.y - ); - - return !nearest || distance < nearest.distance - ? { line: targetLine, distance } - : nearest; - }, null)?.line; - - if (!nearestTarget) return sourceLine; - - // 방향이 반대인지 확인 (벡터 내적을 사용) - const sourceVec = { - x: sourceLine.x2 - sourceLine.x1, - y: sourceLine.y2 - sourceLine.y1 - }; - const targetVec = { - x: nearestTarget.x2 - nearestTarget.x1, - y: nearestTarget.y2 - nearestTarget.y1 - }; - - const dotProduct = sourceVec.x * targetVec.x + sourceVec.y * targetVec.y; - - // 내적이 음수이면 방향이 반대이므로 뒤집기 - if (dotProduct < 0) { - return { - ...sourceLine, - x1: sourceLine.x2, - y1: sourceLine.y2, - x2: sourceLine.x1, - y2: sourceLine.y1 - }; - } - - return sourceLine; - }); - }; - - 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은 움직인라인 - const movedLines = [] - - // 조건에 맞는 라인들만 필터링 - const validWallLines = wallLines.filter((wallLine, index) => wallLine.idx - 1 === index); - - - validWallLines.forEach((wallLine, index) => { - - const originalIndex = wallLines.indexOf(wallLine); - const roofLine = sortRoofLines[originalIndex]; - const currentRoofLine = currentRoofLines[originalIndex]; - const moveLine = wall.baseLines[originalIndex]; - const wallBaseLine = wall.baseLines[originalIndex]; - - // const roofLine = sortRoofLines[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 외곽선 설정 - console.log("index::::", index) - console.log('roofLine:::',roofLine) - console.log('wallLine', wallLine) - console.log('wallBaseLine', wallBaseLine) - - - const origin = moveLine.attributes?.originPoint - if (!origin) return - - if (isSamePoint(moveLine, wallLine)) { - - return false - } - - const movedStart = Math.abs(moveLine.x1 - wallLine.x1) > EPSILON || Math.abs(moveLine.y1 - origin.y1) > EPSILON - const movedEnd = Math.abs(moveLine.x2 - wallLine.x2) > EPSILON || Math.abs(moveLine.y2 - origin.y2) > EPSILON - - - const fullyMoved = movedStart && movedEnd - - -//반시계 방향 - let newPStart //= {x:roofLine.x1, y:roofLine.y1} - let newPEnd //= {x:movedLines.x2, y:movedLines.y2} - -//현재 roof는 무조건 시계방향 - - const getAddLine = (p1, p2, stroke = '') => { - movedLines.push({ index, p1, p2 }) - -// Usage: - // let mergeLines = mergeMovedLines(movedLines); - //console.log("mergeLines:::::::", mergeLines); - const line = new QLine([p1.x, p1.y, p2.x, p2.y], { - parentId : roof.id, - fontSize : roof.fontSize, - stroke : '#3FBAE6', - strokeWidth: 4, - name : 'eaveHelpLine', - lineName : 'eaveHelpLine', - selectable : true, - visible : true, - roofId : roofId, - attributes : { - type: 'eaveHelpLine', - isStart : true, - pitch: wallLine.attributes.pitch, - } - }); - //coordinateText(line) - canvas.add(line) - canvas.renderAll(); - return line - } - - //getAddLine(roofLine.startPoint, roofLine.endPoint, ) //외곽선을 그린다 - - newPStart = { x: roofLine.x1, y: roofLine.y1 } - newPEnd = { x: roofLine.x2, y: roofLine.y2 } - - const getInnerLines = (lines, point) => { - - } - let isIn = false - let isOut = false - -//두 포인트가 변경된 라인인 - if (fullyMoved ) { - //반시계방향향 - - const mLine = getSelectLinePosition(wall, wallBaseLine) - - if (getOrientation(roofLine) === 'vertical') { - - if (['left', 'right'].includes(mLine.position)) { - if(Math.abs(wallLine.x1 - wallBaseLine.x1) < 0.1 || Math.abs(wallLine.x2 - wallBaseLine.x2) < 0.1) { - return false - } - const positionType = - (mLine.position === 'left' && wallLine.x1 < wallBaseLine.x1) || - (mLine.position === 'right' && wallLine.x1 > wallBaseLine.x1) - ? 'in' : 'out'; - const condition = `${mLine.position}_${positionType}`; - let isStartEnd = findInteriorPoint(wallBaseLine, sortedBaseLines) - let sPoint, ePoint; - if(condition === 'left_in') { - isIn = true - - if (isStartEnd.start ) { - newPEnd.y = roofLine.y2; - newPEnd.x = roofLine.x2; - - const moveDist = Big(wallBaseLine.x1).minus(wallLine.x1).abs().toNumber() - ePoint = {x: wallBaseLine.x1, y: wallBaseLine.y1}; - newPStart.y = wallBaseLine.y1 - - findPoints.push({ x: ePoint.x, y: ePoint.y, position: 'left_in_start' }); - const newPointX = Big(roofLine.x1).plus(moveDist).abs().toNumber() - const pDist = Big(wallLine.x1).minus(roofLine.x1).abs().toNumber() - const pLineY = Big(roofLine.y1).minus(0).abs().toNumber() - let idx = (0 > index - 1)?roofLines.length:index - const pLineX = roofLines[idx-1].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') - - if(Math.abs(wallBaseLine.y1 - wallLine.y1) < 0.1) { - getAddLine({ x: pLineX, y: pLineY }, { x: newPointX, y: pLineY }, 'green') - getAddLine({ x: newPointX, y: pLineY }, { x: ePoint.x, y: ePoint.y }, 'pink') - } - } - - if(isStartEnd.end) { - newPStart.y = roofLine.y1; - newPStart.x = roofLine.x1; - - const moveDist = Big(wallBaseLine.x2).minus(wallLine.x2).abs().toNumber() - ePoint = {x: wallBaseLine.x2, y: wallBaseLine.y2}; - newPEnd.y = wallBaseLine.y2 - - findPoints.push({ x: ePoint.x, y: ePoint.y, position: 'left_in_end' }); - const newPointX = Big(roofLine.x1).plus(moveDist).toNumber() - const pDist = Big(wallLine.x1).minus(roofLine.x1).abs().toNumber() - const pLineY = Big(roofLine.y2).minus(0).abs().toNumber() - let idx = (roofLines.length < index + 1)?0:index - const pLineX = roofLines[idx+1].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') - - if(Math.abs(wallBaseLine.y2 - wallLine.y2) < 0.1) { - getAddLine({ x: pLineX, y: pLineY }, { x: newPointX, y: pLineY }, 'green') - getAddLine({ x: newPointX, y: pLineY }, { x: ePoint.x, y: ePoint.y }, 'pink') - } - //getAddLine({ x: roofLine.x2, y: roofLine.y2 }, { x: newPointX, y: roofLine.y2 }, 'orange') - } - - }else if(condition === 'left_out') { - console.log("left_out::::isStartEnd:::::", isStartEnd); - if(isStartEnd.start){ - - const moveDist = Big(wallLine.x1).minus(wallBaseLine.x1).abs().toNumber() - const aStartY = Big(roofLine.y1).minus(moveDist).abs().toNumber() - const bStartY = Big(wallLine.y1).minus(moveDist).abs().toNumber() - const inLine = findLineContainingPoint(innerLines, { y: aStartY, x: roofLine.x2 }) - - const eLineY = Big(bStartY).minus(wallLine.y1).abs().toNumber() - newPStart.y = aStartY - newPEnd.y = roofLine.y2 //Big(roofLine.y2).minus(eLineY).toNumber() - let idx = (0 >= index - 1)?roofLines.length:index - const newLine = roofLines[idx-1]; - - if(Math.abs(wallBaseLine.y1 - wallLine.y1) < 0.1) { - if(inLine){ - if(inLine.x1 < inLine.x2) { - getAddLine({ y: bStartY, x: wallLine.x2 }, { y: inLine.y2, x: inLine.x2 }, 'pink') - }else{ - getAddLine({ y: inLine.y2, x: inLine.x2 },{ y: bStartY, x: wallLine.x2 }, 'pink') - } - - } - getAddLine({ y: bStartY, x: wallLine.x2 }, { y: roofLine.y1, x: wallLine.x1 }, 'magenta') - getAddLine({ y: newLine.y1, x: newLine.x1 }, { y: newLine.y2, x: wallLine.x2 }, 'Gray') - findPoints.push({ y: aStartY, x: newPStart.x, position: 'left_out_start' }); - }else{ - const cLineY = Big(wallBaseLine.x1).minus(wallLine.x1).abs().toNumber() - newPStart.y = Big(newPStart.y).minus(cLineY).toNumber(); - const inLine = findLineContainingPoint(innerLines, { y: newPStart.y, x: newPStart.x }) - if(inLine){ - if(inLine.x1 < inLine.x2) { - getAddLine({ y: newPStart.y, x: newPStart.x }, { y: inLine.y2, x: inLine.x2 }, 'purple') - }else{ - getAddLine({ y: inLine.y1, x: inLine.x1},{ y: newPStart.y, x: newPStart.x }, 'purple') - } - }else { - //newPStart.y = wallLine.y1; - //외곽 라인 그리기 - const rLineM = Big(wallBaseLine.x2).minus(roofLine.x2).abs().toNumber(); - newPStart.y = Big(wallBaseLine.y1).minus(rLineM).abs().toNumber(); - const inLine = findLineContainingPoint(innerLines, { y: newPStart.y, x: newPStart.x }) - if(inLine) { - if (inLine.x2 > inLine.x1) { - getAddLine({ y: newPStart.y, x: newPStart.x }, { y: inLine.y2, x: inLine.x2 }, 'purple') - } else { - getAddLine({ y: inLine.y1, x: inLine.x1 }, { y: newPEnd.y, x: newPEnd.x }, 'purple') - } - } - } - - } - } - - - if(isStartEnd.end){ - 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() - const inLine = findLineContainingPoint(innerLines, { y: aStartY, x: roofLine.x1 }) - console.log("startLines:::::::", inLine); - const eLineY = Big(bStartY).minus(wallLine.y2).abs().toNumber() - newPEnd.y = aStartY - newPStart.y = roofLine.y1//Big(roofLine.y1).plus(eLineY).toNumber() - let idx = (roofLines.length < index + 1)?0:index - const newLine = roofLines[idx+1]; - - if(Math.abs(wallBaseLine.y2 - wallLine.y2) < 0.1) { - if(inLine){ - if(inLine.x1 < inLine.x2) { - getAddLine({ y: bStartY, x: wallLine.x1 }, { y: inLine.y2, x: inLine.x2 }, 'pink') - }else{ - getAddLine({ y: inLine.y1, x: inLine.x1 }, { y: bStartY, x: wallLine.x1 }, 'pink') - } - } - getAddLine({ y: bStartY, x: wallLine.x1 }, { y: roofLine.y2, x: wallLine.x2 }, 'magenta') - getAddLine({ y: newLine.y2, x: newLine.x2 }, { y: newLine.y1, x: wallLine.x1 }, 'Gray') - findPoints.push({ y: aStartY, x: newPEnd.x, position: 'left_out_end' }); - }else{ - const cLineY = Big(wallBaseLine.x2).minus(wallLine.x2).abs().toNumber() - newPEnd.y = Big(newPEnd.y).plus(cLineY).toNumber(); - const inLine = findLineContainingPoint(innerLines, { y: newPEnd.y, x: newPEnd.x }) - if(inLine){ - if(inLine.x1 < inLine.x2) { - getAddLine({ y: newPEnd.y, x: newPEnd.x }, { y: inLine.y2, x: inLine.x2 }, 'purple') - }else{ - getAddLine({ y: inLine.y1, x: inLine.x1 },{ y: newPEnd.y, x: newPEnd.x }, 'purple') - } - }else { - - // newPEnd.y = wallLine.y2 - - //외곽 라인 그리기 - const rLineM = Big(wallBaseLine.x2).minus(roofLine.x2).abs().toNumber(); - newPEnd.y = Big(wallBaseLine.y2).plus(rLineM).abs().toNumber(); - const inLine = findLineContainingPoint(innerLines, { y: newPEnd.y, x: newPEnd.x }) - if(inLine) { - if (inLine.x2 > inLine.x1) { - getAddLine({ y: newPEnd.y, x: newPEnd.x }, { y: inLine.y2, x: inLine.x2 }, 'purple') - } else { - getAddLine({ y: inLine.y1, x: inLine.x1 }, { y: newPEnd.y, x: newPEnd.x }, 'purple') - } - } - } - - } - findPoints.push({ y: newPStart.y, x: newPEnd.x, position: 'left_out_end' }); - } - }else if(condition === 'right_in') { - if (isStartEnd.start ) { - - newPEnd.y = roofLine.y2; - newPEnd.x = roofLine.x2; - - const moveDist = Big(wallBaseLine.x1).minus(wallLine.x1).abs().toNumber() - ePoint = {x: wallBaseLine.x1, y: wallBaseLine.y1}; - newPStart.y = wallBaseLine.y1 - - findPoints.push({ x: ePoint.x, y: ePoint.y, position: 'right_in_start'}); - const newPointX = Big(roofLine.x1).minus(moveDist).abs().toNumber() - const pDist = Big(wallLine.x1).minus(roofLine.x1).abs().toNumber() - const pLineY = Big(roofLine.y1).minus(0).abs().toNumber() - let idx = (0 >= index - 1)?roofLines.length:index - const pLineX = roofLines[idx-1].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') - - if(Math.abs(wallBaseLine.y1 - wallLine.y1) < 0.1) { - getAddLine({ x: pLineX, y: pLineY }, { x: newPointX, y: pLineY }, 'green') - getAddLine({ x: newPointX, y: pLineY }, { x: ePoint.x, y: ePoint.y }, 'pink') - } - } - - if(isStartEnd.end) { - newPStart.y = roofLine.y1; - newPStart.x = roofLine.x1; - - const moveDist = Big(wallBaseLine.x2).minus(wallLine.x2).abs().toNumber() - ePoint = {x: wallBaseLine.x2, y: wallBaseLine.y2}; - newPEnd.y = wallBaseLine.y2 - - findPoints.push({ x: ePoint.x, y: ePoint.y, position: 'right_in_end' }); - const newPointX = Big(roofLine.x1).minus(moveDist).toNumber() - const pDist = Big(wallLine.x1).minus(roofLine.x1).abs().toNumber() - const pLineY = Big(roofLine.y2).minus(0).abs().toNumber() - let idx = (roofLines.length < index + 1)?0:index - const pLineX = roofLines[idx+1].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') - - if(Math.abs(wallBaseLine.y2 - wallLine.y2) < 0.1) { - getAddLine({ x: pLineX, y: pLineY }, { x: newPointX, y: pLineY }, 'green') - getAddLine({ x: newPointX, y: pLineY }, { x: ePoint.x, y: ePoint.y }, 'pink') - } - getAddLine({ x: roofLine.x2, y: roofLine.y2 }, { x: newPointX, y: roofLine.y2 }, 'orange') - } - - }else if(condition === 'right_out') { - console.log("right_out::::isStartEnd:::::", isStartEnd); - if (isStartEnd.start ) { //x1 inside - const moveDist = Big(wallLine.x1).minus(wallBaseLine.x1).abs().toNumber() - const aStartY = Big(roofLine.y1).plus(moveDist).abs().toNumber() - const bStartY = Big(wallLine.y1).plus(moveDist).abs().toNumber() - const inLine = findLineContainingPoint(innerLines, { y: aStartY, x: roofLine.x1 }) - console.log("startLines:::::::", inLine); - const eLineY = Big(bStartY).minus(wallLine.y1).abs().toNumber() - newPStart.y = aStartY - newPEnd.y = roofLine.y2//Big(roofLine.y2).plus(eLineY).toNumber() - let idx = (0 >= index - 1)?roofLines.length:index - const newLine = roofLines[idx-1]; - - if(Math.abs(wallBaseLine.y1 - wallLine.y1) < 0.1) { - if(inLine){ - if(inLine.x2 < inLine.x1) { - getAddLine({ y: bStartY, x: wallLine.x2 }, { y: inLine.y2, x: inLine.x2 }, 'pink') - }else{ - getAddLine({ y: inLine.y1, x: inLine.x1 },{ y: bStartY, x: wallLine.x2 }, 'pink') - } - } - getAddLine({ y: bStartY, x: wallLine.x2 }, { y: roofLine.y1, x: wallLine.x1 }, 'magenta') - getAddLine({ y: newLine.y1, x: newLine.x1 }, { y: newLine.y2, x: wallLine.x2 }, 'Gray') - findPoints.push({ y: aStartY, x: newPEnd.x, position: 'right_out_start' }); - }else{ - const cLineY = Big(wallBaseLine.x1).minus(wallLine.x1).abs().toNumber() - newPStart.y = Big(newPStart.y).plus(cLineY).toNumber(); - const inLine = findLineContainingPoint(innerLines, { y: newPStart.y, x: newPStart.x }) - if(inLine){ - if(inLine.x2 < inLine.x1 ) { - getAddLine({ y: newPStart.y, x: newPStart.x }, { y: inLine.y2, x: inLine.x2 }, 'purple') - }else{ - getAddLine({ y: inLine.y1, x: inLine.x1 },{ y: newPStart.y, x: newPStart.x }, 'purple') - } - }else { - //newPStart.y = wallLine.y1; - //외곽 라인 그리기 - const rLineM = Big(wallBaseLine.x1).minus(roofLine.x1).abs().toNumber(); - newPStart.y = Big(wallBaseLine.y1).plus(rLineM).abs().toNumber(); - const inLine = findLineContainingPoint(innerLines, { y: newPStart.y, x: newPStart.x }) - if(inLine){ - if(inLine.x2 > inLine.x1 ) { - getAddLine({ y: newPStart.y, x: newPStart.x }, { y: inLine.y1, x: inLine.x1 }, 'purple') - }else{ - getAddLine({ y: inLine.y2, x: inLine.x2 }, { y: newPStart.y, x: newPStart.x } , 'purple') - } - } - - } - - } - - } - - if(isStartEnd.end){ - const moveDist = Big(wallLine.x1).minus(wallBaseLine.x1).abs().toNumber() - const aStartY = Big(roofLine.y2).minus(moveDist).abs().toNumber() - const bStartY = Big(wallLine.y2).minus(moveDist).abs().toNumber() - const inLine = findLineContainingPoint(innerLines, { y: aStartY, x: roofLine.x1 }) - console.log("startLines:::::::", inLine); - const eLineY = Big(bStartY).minus(wallLine.y2).abs().toNumber() - newPEnd.y = aStartY - newPStart.y = roofLine.y1//Big(roofLine.y1).minus(eLineY).toNumber() - let idx = (roofLines.length < index + 1)?0:index - const newLine = roofLines[idx+1]; - if(inLine){ - if(inLine.x2 < inLine.x1) { - getAddLine({ y: bStartY, x: wallLine.x1 }, { y: inLine.y2, x: inLine.x2 }, 'pink') - }else{ - getAddLine({ y: inLine.y1, x: inLine.x1 },{ y: bStartY, x: wallLine.x1 }, 'pink') - } - } - if(Math.abs(wallBaseLine.y2 - wallLine.y2) < 0.1) { - getAddLine({ y: bStartY, x: wallLine.x1 }, { y: roofLine.y2, x: wallLine.x2 }, 'magenta') - getAddLine({ y: newLine.y2, x: newLine.x2 }, { y: newLine.y1, x: wallLine.x1 }, 'Gray') - findPoints.push({ y: aStartY, x: newPEnd.x, position: 'right_out_end' }); - }else{ - const cLineY = Big(wallBaseLine.x2).minus(wallLine.x2).abs().toNumber() - newPEnd.y = Big(newPEnd.y).minus(cLineY).toNumber(); - const inLine = findLineContainingPoint(innerLines, { y: newPEnd.y, x: newPEnd.x }) - if(inLine){ - if(inLine.x2 < inLine.x1) { - getAddLine({ y: newPEnd.y, x: newPEnd.x }, { y: inLine.y2, x: inLine.x2 }, 'purple') - }else{ - getAddLine({ y: inLine.y1, x: inLine.x1 }, { y: newPEnd.y, x: newPEnd.x }, 'purple') - } - }else { - //newPEnd.y = wallLine.y2; - - //외곽 라인 그리기 - const rLineM = Big(wallBaseLine.x2).minus(roofLine.x2).abs().toNumber(); - newPEnd.y = Big(wallBaseLine.y2).minus(rLineM).abs().toNumber(); - const inLine = findLineContainingPoint(innerLines, { y: newPEnd.y, x: newPEnd.x }) - if(inLine){ - if(inLine.x2 > inLine.x1 ) { - getAddLine({ y: newPEnd.y, x: newPEnd.x }, { y: inLine.y1, x: inLine.x1 }, 'purple') - }else{ - getAddLine({ y: inLine.y2, x: inLine.x2}, { y: newPEnd.y, x: newPEnd.x } , 'purple') - } - } - - } - - } - } - } - } - } else if (getOrientation(roofLine) === 'horizontal') { //red - - if (['top', 'bottom'].includes(mLine.position)) { - if(Math.abs(wallLine.y1 - wallBaseLine.y1) < 0.1 || Math.abs(wallLine.y2 - wallBaseLine.y2) < 0.1) { - return false - } - const positionType = - (mLine.position === 'top' && wallLine.y1 < wallBaseLine.y1) || - (mLine.position === 'bottom' && wallLine.y1 > wallBaseLine.y1) - ? 'in' : 'out'; - - const condition = `${mLine.position}_${positionType}`; - let isStartEnd = findInteriorPoint(wallBaseLine, sortedBaseLines) - - let sPoint, ePoint; - - if(condition === 'top_in') { - if (isStartEnd.start ) { - const moveDist = Big(wallLine.y1).minus(wallBaseLine.y1).abs().toNumber() - sPoint = {x: wallBaseLine.x1, y: wallBaseLine.y1}; - newPStart.x = wallBaseLine.x1; - - - const newPointY = Big(roofLine.y2).plus(moveDist).toNumber() - - const pDist = Big(wallLine.y2).minus(roofLine.y2).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 - 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' }); - - if(Math.abs(wallBaseLine.x1 - wallLine.x1) < 0.1) { - getAddLine({ x: pLineX, y: pLineY }, { x: pLineX, y: newPointY }, 'green') - getAddLine({ x: pLineX, y: newPointY }, { x: sPoint.x, y: sPoint.y }, 'pink') - } - //getAddLine({ x: roofLine.x2, y: roofLine.y2 }, { x: roofLine.x2, y: newPointY }, 'orange') - - } - - if(isStartEnd.end){ - const moveDist = Big(wallLine.y2).minus(wallBaseLine.y2).abs().toNumber() - sPoint = { x: wallBaseLine.x2, y: wallBaseLine.y2 } - newPEnd.x = wallBaseLine.x2 - - const newPointY = Big(roofLine.y1).plus(moveDist).toNumber() - - const pDist = Big(wallLine.y1).minus(roofLine.y1).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 - 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' }); - - if (Math.abs(wallBaseLine.x2 - wallLine.x2) < 0.1) { - getAddLine({ x: pLineX, y: pLineY }, { x: pLineX, y: newPointY }, 'green') - getAddLine({ x: pLineX, y: newPointY }, { x: sPoint.x, y: sPoint.y }, 'pink') - } - - - //getAddLine({ x: roofLine.x1, y: roofLine.y1 }, { x: roofLine.x1, y: newPointY }, 'orange') - } - - }else if(condition === 'top_out') { - console.log("top_out isStartEnd:::::::", isStartEnd); - - if (isStartEnd.start ) { - const moveDist = Big(wallLine.y1).minus(wallBaseLine.y1).abs().toNumber() - const aStartX = Big(roofLine.x1).plus(moveDist).toNumber() - const bStartX = Big(wallLine.x1).plus(moveDist).toNumber() - const inLine = findLineContainingPoint(innerLines, { x: aStartX, y: newPEnd.y }) - - const eLineX = Big(bStartX).minus(wallLine.x1).abs().toNumber() - newPEnd.x = roofLine.x2 //Big(newPEnd.x).plus(eLineX).toNumber() - newPStart.x = aStartX - let idx = (0 > index - 1)?roofLines.length:index - const newLine = roofLines[idx-1]; - - if(Math.abs(wallBaseLine.x1 - wallLine.x1) < 0.1) { - if(inLine){ - if(inLine.y2 > inLine.y1 ) { - getAddLine({ x: bStartX, y: wallLine.y1 }, { x: inLine.x2, y: inLine.y2 }, 'pink') - }else{ - getAddLine({ x: inLine.x1, y: inLine.y1 }, { x: bStartX, y: wallLine.y1 }, 'pink') - } - } - getAddLine({ x: bStartX, y: wallLine.y1 }, { x: roofLine.x1, y: wallLine.y1 }, 'magenta') - getAddLine({ x: newLine.x1, y: newLine.y1 }, { x: newLine.x1, y: wallLine.y1 }, 'Gray') - findPoints.push({ x: aStartX, y: newPEnd.y, position: 'top_out_start' }); - }else{ - const cLineX = Big(wallBaseLine.y1).minus(wallLine.y1).abs().toNumber() - newPStart.x = Big(newPStart.x).plus(cLineX).toNumber(); - const inLine = findLineContainingPoint(innerLines, { y: newPStart.y, x: newPStart.x }) - if(inLine){ - if(inLine.y2 > inLine.y1 ) { - getAddLine({ y: newPStart.y, x: newPStart.x }, { y: inLine.y2, x: inLine.x2 }, 'purple') - }else{ - getAddLine({ y: inLine.y1, x: inLine.x1 }, { y: newPStart.y, x: newPStart.x } , 'purple') - } - - }else { - //외곽 라인 그리기 - const rLineM = Big(wallBaseLine.y1).minus(roofLine.y1).abs().toNumber(); - newPStart.x = Big(wallBaseLine.x1).plus(rLineM).abs().toNumber(); - const inLine = findLineContainingPoint(innerLines, { y: newPStart.y, x: newPStart.x }) - if(inLine){ - if(inLine.y2 > inLine.y1 ) { - getAddLine({ y: newPStart.y, x: newPStart.x }, { y: inLine.y2, x: inLine.x2 }, 'purple') - }else{ - getAddLine({ y: inLine.y1, x: inLine.x1 }, { y: newPStart.y, x: newPStart.x } , 'purple') - } - } - } - - } - } - if(isStartEnd.end){ - const moveDist = Big(wallLine.y1).minus(wallBaseLine.y1).abs().toNumber() - const aStartX = Big(roofLine.x2).minus(moveDist).abs().toNumber() - const bStartX = Big(wallLine.x2).minus(moveDist).abs().toNumber() - const inLine = findLineContainingPoint(innerLines, { x: aStartX, y: newPEnd.y }) - console.log("startLines:::::::", inLine); - const eLineX = Big(bStartX).minus(wallLine.x2).abs().toNumber() - newPStart.x = roofLine.x1;//Big(newPStart.x).minus(eLineX).abs().toNumber() - newPEnd.x = aStartX - let idx = (roofLines.length < index + 1)?0:index - const newLine = roofLines[idx+1]; - - if(Math.abs(wallBaseLine.x2 - wallLine.x2) < 0.1) { - if(inLine){ - if(inLine.y2 > inLine.y1 ){ - getAddLine({ x: bStartX, y: wallLine.y1 }, { x: inLine.x2, y: inLine.y2 }, 'pink') - }else{ - getAddLine({ x: inLine.x1, y: inLine.y1 },{ x: bStartX, y: wallLine.y1 }, 'pink') - } - - } - getAddLine({ x: bStartX, y: wallLine.y1 }, { x: roofLine.x2, y: wallLine.y2 }, 'magenta') - getAddLine({ x: newLine.x2, y: newLine.y2 }, { x: newLine.x1, y: wallLine.y1 }, 'Gray') - findPoints.push({ x: aStartX, y: newPEnd.y, position: 'top_out_end' }); - }else{ - const cLineX = Big(wallLine.y2).minus(wallBaseLine.y2).abs().toNumber() - newPEnd.x = Big(newPEnd.x).minus(cLineX).toNumber(); - const inLine = findLineContainingPoint(innerLines, { y: newPEnd.y, x: newPEnd.x }) - if(inLine){ - if(inLine.y2 > inLine.y1 ) { - getAddLine({ y: newPEnd.y, x: newPEnd.x }, { y: inLine.y2, x: inLine.x2 }, 'purple') - }else{ - getAddLine({ y: inLine.y1, x: inLine.x1 },{ y: newPEnd.y, x: newPEnd.x }, 'purple') - } - }else { - //newPEnd.x = wallLine.x2; - //외곽 라인 그리기 - const rLineM = Big(wallBaseLine.y2).minus(roofLine.y2).abs().toNumber(); - newPEnd.x = Big(wallBaseLine.x2).minus(rLineM).abs().toNumber(); - const inLine = findLineContainingPoint(innerLines, { y: newPEnd.y, x: newPEnd.x }) - if(inLine){ - if(inLine.y1 > inLine.y2 ) { - getAddLine({ y: newPEnd.y, x: newPEnd.x }, { y: inLine.y1, x: inLine.x1 }, 'purple') - }else{ - getAddLine({ y: inLine.y2, x: inLine.x2 }, { y: newPEnd.y, x: newPEnd.x } , 'purple') - } - } - } - - } - } - }else if(condition === 'bottom_in') { - if (isStartEnd.start ) { - const moveDist = Big(wallLine.y1).minus(wallBaseLine.y1).abs().toNumber() - sPoint = {x: wallBaseLine.x1, y: wallBaseLine.y1}; - newPStart.x = wallBaseLine.x1; - - - const newPointY = Big(roofLine.y2).minus(moveDist).toNumber() - - const pDist = Big(wallLine.y2).minus(roofLine.y2).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 - 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' }); - - if(Math.abs(wallBaseLine.x1 - wallLine.x1) < 0.1) { - getAddLine({ x: pLineX, y: pLineY }, { x: pLineX, y: newPointY }, 'green') - getAddLine({ x: pLineX, y: newPointY }, { x: sPoint.x, y: sPoint.y }, 'pink') - } - getAddLine({ x: roofLine.x2, y: roofLine.y2 }, { x: roofLine.x2, y: newPointY }, 'orange') - } - - if(isStartEnd.end){ - const moveDist = Big(wallLine.y2).minus(wallBaseLine.y2).abs().toNumber() - sPoint = {x: wallBaseLine.x2, y: wallBaseLine.y2}; - newPEnd.x = wallBaseLine.x2; - - - const newPointY = Big(roofLine.y1).minus(moveDist).toNumber() - - const pDist = Big(wallLine.y1).minus(roofLine.y1).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 - 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' }); - - if(Math.abs(wallBaseLine.x2 - wallLine.x2) < 0.1) { - getAddLine({ x: pLineX, y: pLineY }, { x: pLineX, y: newPointY }, 'green') - getAddLine({ x: pLineX, y: newPointY }, { x: sPoint.x, y: sPoint.y }, 'pink') - } - getAddLine({ x: roofLine.x1, y: roofLine.y1 }, { x: roofLine.x1, y: newPointY }, 'orange') - - } - }else if(condition === 'bottom_out') { - console.log("bottom_out isStartEnd:::::::", isStartEnd); - if (isStartEnd.start ) { - const moveDist = Big(wallLine.y1).minus(wallBaseLine.y1).abs().toNumber() - const aStartX = Big(roofLine.x1).minus(moveDist).abs().toNumber() - const bStartX = Big(wallLine.x1).minus(moveDist).abs().toNumber() - const inLine = findLineContainingPoint(innerLines, { x: aStartX, y: roofLine.y1 }) - console.log("startLines:::::::", inLine); - const eLineX = Big(bStartX).minus(wallLine.x1).abs().toNumber() - newPEnd.x = roofLine.x2//Big(roofLine.x2).minus(eLineX).toNumber() - newPStart.x = aStartX - let idx = (0 > index - 1)?roofLines.length:index - const newLine = roofLines[idx-1]; - - - if(Math.abs(wallBaseLine.x1 - wallLine.x1) < 0.1) { - if(inLine){ - if(inLine.y2 < inLine.y1 ) { - getAddLine({ x: bStartX, y: wallLine.y1 }, { x: inLine.x2, y: inLine.y2 }, 'pink') - }else{ - getAddLine({ x: inLine.x1, y: inLine.y1 },{ x: bStartX, y: wallLine.y1 }, 'pink') - } - } - getAddLine({ x: bStartX, y: wallLine.y1 }, { x: roofLine.x1, y: wallLine.y1 }, 'magenta') - getAddLine({ x: newLine.x1, y: newLine.y1 }, { x: newLine.x1, y: wallLine.y1 }, 'Gray') - findPoints.push({ x: aStartX, y: newPEnd.y, position: 'bottom_out_start' }); - }else{ - const cLineX = Big(wallBaseLine.y1).minus(wallLine.y1).abs().toNumber() - newPStart.x = Big(newPStart.x).minus(cLineX).toNumber(); - const inLine = findLineContainingPoint(innerLines, { y: newPStart.y, x: newPStart.x }) - if(inLine){ - if(inLine.y2 < inLine.y1 ) { - getAddLine({ y: newPStart.y, x: newPStart.x }, { y: inLine.y2, x: inLine.x2 }, 'purple') - }else{ - getAddLine({ y: inLine.y1, x: inLine.x1 }, { y: newPStart.y, x: newPStart.x }, 'purple') - } - }else{ - //newPStart.x = wallLine.x1; - //외곽 라인 그리기 - const rLineM = Big(wallBaseLine.y1).minus(roofLine.y1).abs().toNumber(); - newPStart.x = Big(wallBaseLine.x1).minus(rLineM).abs().toNumber(); - const inLine = findLineContainingPoint(innerLines, { y: newPStart.y, x: newPStart.x }) - if(inLine){ - if(inLine.y2 > inLine.y1 ) { - getAddLine({ y: newPStart.y, x: newPStart.x }, { y: inLine.y1, x: inLine.x1 }, 'purple') - }else{ - getAddLine({ y: inLine.y2, x: inLine.x2 }, { y: newPStart.y, x: newPStart.x } , 'purple') - } - } - } - - } - } - - if(isStartEnd.end){ - 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() - const inLine = findLineContainingPoint(innerLines, { x: aStartX, y: roofLine.y1 }) - console.log("startLines:::::::", inLine); - const eLineX = Big(bStartX).minus(wallLine.x2).abs().toNumber() - newPEnd.x = aStartX - newPStart.x = roofLine.x1;//Big(roofLine.x1).plus(eLineX).toNumber() - let idx = (roofLines.length < index + 1)?0:index - const newLine = roofLines[idx + 1]; - - if(Math.abs(wallBaseLine.x2 - wallLine.x2) < 0.1) { - if(inLine){ - if(inLine.y2 < inLine.y1 ) { - getAddLine({ x: bStartX, y: wallLine.y1 }, { x: inLine.x2, y: inLine.y2 }, 'pink') - }else{ - getAddLine({ x: inLine.x1, y: inLine.y1 }, { x: bStartX, y: wallLine.y1 }, 'pink') - } - } - getAddLine({ x: bStartX, y: wallLine.y1 }, { x: roofLine.x2, y: wallLine.y2 }, 'magenta') - getAddLine({ x: newLine.x2, y: newLine.y2 }, { x: newLine.x1, y: wallLine.y1 }, 'Gray') - findPoints.push({ x: aStartX, y: newPEnd.y, position: 'bottom_out_end' }); - }else{ - const cLineX = Big(wallBaseLine.y2).minus(wallLine.y2).abs().toNumber() - newPEnd.x = Big(newPEnd.x).plus(cLineX).toNumber(); - const inLine = findLineContainingPoint(innerLines, { y: newPEnd.y, x: newPEnd.x }) - if(inLine){ - if(inLine.y2 < inLine.y1 ) { - getAddLine({ y: newPEnd.y, x: newPEnd.x }, { y: inLine.y2, x: inLine.x2 }, 'purple') - }else{ - getAddLine({ y: inLine.y1, x: inLine.x1 },{ y: newPEnd.y, x: newPEnd.x }, 'purple') - } - }else{ - //newPEnd.x = wallLine.x2; - //외곽 라인 그리기 - const rLineM = Big(wallBaseLine.y2).minus(roofLine.y2).abs().toNumber(); - newPEnd.x = Big(wallBaseLine.x2).plus(rLineM).abs().toNumber(); - const inLine = findLineContainingPoint(innerLines, { y: newPEnd.y, x: newPEnd.x }) - if(inLine){ - if(inLine.y1 > inLine.y2 ) { - getAddLine({ y: newPEnd.y, x: newPEnd.x }, { y: inLine.y2, x: inLine.x2 }, 'purple') - }else{ - getAddLine({ y: inLine.y1, x: inLine.x1 }, { y: newPEnd.y, x: newPEnd.x } , 'purple') - } - } - } - - } - } - } - } - } - - getAddLine(newPStart, newPEnd, 'red') - //canvas.remove(roofLine) - }else{ - getAddLine(roofLine.startPoint, roofLine.endPoint, ) - } - - - - canvas.renderAll() - }); - // } + } if (findPoints.length > 0) { // 모든 점에 대해 라인 업데이트를 누적 @@ -1796,6 +1762,115 @@ const isSameLine = (edgeStartX, edgeStartY, edgeEndX, edgeEndY, baseLine) => { // --- 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} @@ -2369,6 +2444,14 @@ function isSamePoint(p1, p2, tolerance = 0.1) { return Math.abs(p1.x - p2.x) < tolerance && Math.abs(p1.y - p2.y) < tolerance; } +function isSameLine2(line1, line2, tolerance = 0.1) { + return ( + Math.abs(line1.x1 - line2.x1) < tolerance && + Math.abs(line1.y1 - line2.y1) < tolerance && + Math.abs(line1.x2 - line2.x2) < tolerance && + Math.abs(line1.y2 - line2.y2) < tolerance + ); +} // 두 점을 지나는 직선의 기울기 계산 function calculateSlope(p1, p2) { // 수직선인 경우 (기울기 무한대) @@ -3014,17 +3097,32 @@ function pointToLineDistance(point, lineP1, lineP2) { const getOrientation = (line, eps = 0.1) => { - const x1 = line.get('x1') - const y1 = line.get('y1') - const x2 = line.get('x2') - const y2 = line.get('y2') - const dx = Math.abs(x2 - x1) - const dy = Math.abs(y2 - y1) + if (!line) { + console.error('line 객체가 유효하지 않습니다:', line); + return null; // 또는 적절한 기본값 반환 + } - if (dx < eps && dy >= eps) return 'vertical' - if (dy < eps && dx >= eps) return 'horizontal' - if (dx < eps && dy < eps) return 'point' - return 'diagonal' + // get 메서드가 있으면 사용하고, 없으면 직접 프로퍼티에 접근 + const getValue = (obj, key) => + obj && typeof obj.get === 'function' ? obj.get(key) : obj[key]; + + 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; + } } @@ -3393,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 !usedBaseIndices.has(idx)); - if(unused !== -1) { - sortedBaseLines[i] = baseLines[unused]; - usedBaseIndices.add(unused); - } else { - sortedBaseLines[i] = baseLines[0]; // 최후의 수단 - } - } - } - - return sortedBaseLines; -}; \ No newline at end of file