From b3af3cd0e003dc267baa7a0f445195fa5f23e334 Mon Sep 17 00:00:00 2001 From: Jaeyoung Lee Date: Tue, 25 Feb 2025 13:46:10 +0900 Subject: [PATCH 01/46] =?UTF-8?q?=EC=99=B8=EB=B2=BD=EC=84=A0=20=ED=8E=B8?= =?UTF-8?q?=EC=A7=91,=20=EC=98=A4=ED=94=84=EC=85=8B=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=20=EC=A0=81=EC=9A=A9=20=EB=B0=8F=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modal/wallLineOffset/type/Offset.jsx | 2 +- .../modal/wallLineOffset/type/WallLine.jsx | 4 +- src/hooks/roofcover/useOuterLineWall.js | 10 - src/hooks/roofcover/useRoofShapeSetting.js | 16 +- .../roofcover/useWallLineOffsetSetting.js | 346 +++++++++--------- src/hooks/useMode.js | 74 +++- src/hooks/usePolygon.js | 4 +- src/util/qpolygon-utils.js | 17 +- 8 files changed, 251 insertions(+), 222 deletions(-) diff --git a/src/components/floor-plan/modal/wallLineOffset/type/Offset.jsx b/src/components/floor-plan/modal/wallLineOffset/type/Offset.jsx index 418a9419..4e7545cb 100644 --- a/src/components/floor-plan/modal/wallLineOffset/type/Offset.jsx +++ b/src/components/floor-plan/modal/wallLineOffset/type/Offset.jsx @@ -74,7 +74,7 @@ export default function Offset({ length1Ref, arrow1Ref, currentWallLineRef }) {
- +
mm
diff --git a/src/components/floor-plan/modal/wallLineOffset/type/WallLine.jsx b/src/components/floor-plan/modal/wallLineOffset/type/WallLine.jsx index 581f57f3..92f6a10b 100644 --- a/src/components/floor-plan/modal/wallLineOffset/type/WallLine.jsx +++ b/src/components/floor-plan/modal/wallLineOffset/type/WallLine.jsx @@ -46,7 +46,7 @@ export default forwardRef(function WallLine({ length1Ref, length2Ref, arrow1Ref,
- +
mm
@@ -80,7 +80,7 @@ export default forwardRef(function WallLine({ length1Ref, length2Ref, arrow1Ref,
- +
mm
diff --git a/src/hooks/roofcover/useOuterLineWall.js b/src/hooks/roofcover/useOuterLineWall.js index 7e94f074..a1292ccf 100644 --- a/src/hooks/roofcover/useOuterLineWall.js +++ b/src/hooks/roofcover/useOuterLineWall.js @@ -143,7 +143,6 @@ export function useOuterLineWall(id, propertiesId) { const mouseDown = (e) => { let pointer = getIntersectMousePoint(e) pointer = { x: Big(pointer.x).round(1).toNumber(), y: Big(pointer.y).round(1).toNumber() } - console.log('mouseDown', pointer, points) if (points.length === 0) { setPoints((prev) => [...prev, pointer]) @@ -151,14 +150,11 @@ export function useOuterLineWall(id, propertiesId) { const lastPoint = points[points.length - 1] let newPoint = { x: pointer.x, y: pointer.y } const length = distanceBetweenPoints(lastPoint, newPoint) - console.log('length', length) if (verticalHorizontalMode) { const vector = { x: Big(pointer.x).minus(Big(points[points.length - 1].x)), y: Big(pointer.y).minus(Big(points[points.length - 1].y)), } - // const slope = Math.abs(vector.y / vector.x) // 기울기 계산 - console.log('vector', vector.x.toNumber(), vector.y.toNumber(), Math.abs(vector.y.toNumber() / vector.x.toNumber()) >= 1) const slope = vector.x.eq(0) ? Big(1) : vector.y.div(vector.x).abs() // 기울기 계산 let scaledVector @@ -167,13 +163,11 @@ export function useOuterLineWall(id, propertiesId) { // 기울기가 1 이상이면 x축 방향으로 그림 scaledVector = { x: 0, - // y: vector.y >= 0 ? Number(length) : -Number(length), y: vector.y.gte(0) ? Big(length).toNumber() : Big(length).neg().toNumber(), } } else { // 기울기가 1 미만이면 y축 방향으로 그림 scaledVector = { - // x: vector.x >= 0 ? Number(length) : -Number(length), x: vector.x.gte(0) ? Big(length).toNumber() : Big(length).neg().toNumber(), y: 0, } @@ -182,8 +176,6 @@ export function useOuterLineWall(id, propertiesId) { const verticalLength = scaledVector.y const horizontalLength = scaledVector.x - console.log('verticalLength', verticalLength, 'horizontalLength', horizontalLength) - newPoint = { x: Big(lastPoint.x).plus(horizontalLength).toNumber(), y: Big(lastPoint.y).plus(verticalLength).toNumber(), @@ -876,8 +868,6 @@ export function useOuterLineWall(id, propertiesId) { const firstPoint = points[0] - console.log('points 좌표 : ', points) - points.forEach((point, idx) => { if (idx === 0 || !isAllRightAngle) { return diff --git a/src/hooks/roofcover/useRoofShapeSetting.js b/src/hooks/roofcover/useRoofShapeSetting.js index e9b7bafe..ea21aecb 100644 --- a/src/hooks/roofcover/useRoofShapeSetting.js +++ b/src/hooks/roofcover/useRoofShapeSetting.js @@ -63,13 +63,6 @@ export function useRoofShapeSetting(id) { }, [jerkinHeadPitch]) useEffect(() => { - const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') - // if (!outerLineFix || outerLines.length === 0) { - // swalFire({ text: '외벽선이 없습니다.' }) - // // setShowRoofShapeSettingModal(false) - // closePopup(id) - // } - return () => { if (!isFixRef.current) { return @@ -77,6 +70,7 @@ export function useRoofShapeSetting(id) { const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') const pitchTexts = canvas.getObjects().filter((obj) => obj.name === 'pitchText') + canvas.remove(...pitchTexts) outerLines.forEach((line) => { let stroke, strokeWidth @@ -115,14 +109,6 @@ export function useRoofShapeSetting(id) { return } - /*const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') - outerLines.forEach((line) => { - line.set({ - stroke: '#000000', - strokeWidth: 4, - }) - })*/ - currentObject.set({ stroke: '#EA10AC', strokeWidth: 4, diff --git a/src/hooks/roofcover/useWallLineOffsetSetting.js b/src/hooks/roofcover/useWallLineOffsetSetting.js index 22af0f49..548b509c 100644 --- a/src/hooks/roofcover/useWallLineOffsetSetting.js +++ b/src/hooks/roofcover/useWallLineOffsetSetting.js @@ -6,6 +6,8 @@ import { useEvent } from '@/hooks/useEvent' import { useLine } from '@/hooks/useLine' import { useSwal } from '@/hooks/useSwal' import { usePopup } from '@/hooks/usePopup' +import Big from 'big.js' +import { outerLineFixState } from '@/store/outerLineAtom' // 외벽선 편집 및 오프셋 export function useWallLineOffsetSetting(id) { @@ -28,6 +30,8 @@ export function useWallLineOffsetSetting(id) { const [isLoading, setIsLoading] = useState(false) + const outerLineFix = useRecoilValue(outerLineFixState) + const drawLine = (point1, point2, idx, direction = currentWallLineRef.current.direction) => { const line = addLine([point1.x, point1.y, point2.x, point2.y], { stroke: 'black', @@ -59,7 +63,7 @@ export function useWallLineOffsetSetting(id) { useEffect(() => { const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') - if (outerLines.length === 0) { + if (!outerLineFix || outerLines.length === 0) { swalFire({ text: '외벽선이 없습니다.' }) closePopup(id) return @@ -277,7 +281,7 @@ export function useWallLineOffsetSetting(id) { } } - rearrangeOuterLine(currentIdx + 1) + reArrangeOuterLine(currentIdx + 1) drawLine(point1, point2, currentIdx) drawLine(point2, point3, currentIdx + 1) @@ -286,229 +290,217 @@ export function useWallLineOffsetSetting(id) { canvas.remove(currentWallLineRef.current) currentWallLineRef.current = null canvas.renderAll() + + canvas + .getObjects() + .filter((obj) => obj.name === 'outerLine') + .forEach((obj) => obj.fire('modified')) } - const rearrangeOuterLine = (idxParam) => { + /** + * outreLine의 index를 조절한다. + * @param idxParam + * @param isNegative + */ + const reArrangeOuterLine = (idxParam, isNegative = false) => { const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') outerLines.forEach((outerLine) => { if (outerLine.idx >= idxParam) { - outerLine.idx = outerLine.idx + 1 + outerLine.idx = isNegative ? outerLine.idx - 1 : outerLine.idx + 1 } }) } + /** + * offset 저장 + */ const handleOffsetSave = () => { - const direction = currentWallLineRef.current.direction - let canDirections = direction === 'left' || direction === 'right' ? ['up', 'down'] : ['left', 'right'] - const currentIdx = currentWallLineRef.current.idx + if (!currentObject) return + const currentLine = currentObject + const currentVector = currentLine.y1 === currentLine.y2 ? 'horizontal' : 'vertical' + const canDirections = currentVector === 'horizontal' ? ['up', 'down'] : ['left', 'right'] if (!canDirections.includes(arrow1Ref.current)) { alert('방향을 다시 선택하세요') return } - const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + outerLines.sort((a, b) => a.idx - b.idx) - const idx = currentWallLineRef.current.idx - const prevIdx = idx - 1 <= 0 ? outerLines.length : idx - 1 - const nextIdx = idx + 1 > outerLines.length ? 1 : idx + 1 + const currentIdx = currentLine.idx + const prevIdx = currentIdx - 1 <= 0 ? outerLines.length : currentIdx - 1 + const nextIdx = currentLine.idx + 1 > outerLines.length ? 1 : currentIdx + 1 - const currentLine = currentWallLineRef.current const prevLine = outerLines.find((line) => line.idx === prevIdx) const nextLine = outerLines.find((line) => line.idx === nextIdx) + const prevVector = prevLine.y1 === prevLine.y2 ? 'horizontal' : 'vertical' + const nextVector = nextLine.y1 === nextLine.y2 ? 'horizontal' : 'vertical' - const length = length1Ref.current.value / 10 - const currentLineX = Math.floor(Math.max(currentLine.x1, currentLine.x2)) - const currentLineY = Math.floor(Math.max(currentLine.y1, currentLine.y2)) - switch (arrow1Ref.current) { - case 'up': { - if (prevLine.direction === currentLine.direction) { - const newX = - currentLine.direction === 'left' - ? Math.floor(Math.max(currentLine.x1, currentLine.x2)) - : Math.floor(Math.min(currentLine.x1, currentLine.x2)) + const offsetLength = Big(Number(length1Ref.current.value)).div(10) + if (offsetLength.eq(0)) return - const newPoint1 = { x: newX, y: currentLineY - length } - const newPoint2 = { x: prevLine.x2, y: prevLine.y2 } - rearrangeOuterLine(currentIdx) - drawLine(newPoint1, newPoint2, currentIdx, 'top') + const currentLineMinX = Big(Math.max(currentLine.x1, currentLine.x2)) + const currentLineMaxX = Big(Math.max(currentLine.x1, currentLine.x2)) + const currentLineMinY = Big(Math.max(currentLine.y1, currentLine.y2)) + const currentLineMaxY = Big(Math.max(currentLine.y1, currentLine.y2)) - if (Math.abs(currentLineY - nextLine.y1) < 2) { - nextLine.set({ y1: currentLineY - length }) - } else { - nextLine.set({ y2: currentLineY - length }) - } - } else if (nextLine.direction === currentLine.direction) { - const newX = - currentLine.direction === 'left' - ? Math.floor(Math.min(currentLine.x1, currentLine.x2)) - : Math.floor(Math.max(currentLine.x1, currentLine.x2)) - - const newPoint1 = { x: newX, y: currentLineY - length } - const newPoint2 = { x: nextLine.x1, y: nextLine.y1 } - rearrangeOuterLine(currentIdx + 1) - drawLine(newPoint1, newPoint2, currentIdx + 1, 'top') - - if (Math.abs(currentLineY - prevLine.y1) < 2) { - prevLine.set({ y1: currentLineY - length }) - } else { - prevLine.set({ y2: currentLineY - length }) - } + if (currentVector === 'horizontal') { + const prevX = currentLine.x1 < currentLine.x2 ? Math.min(currentLine.x1, currentLine.x2) : Math.max(currentLine.x1, currentLine.x2) + const nextX = currentLine.x1 < currentLine.x2 ? Math.max(currentLine.x1, currentLine.x2) : Math.min(currentLine.x1, currentLine.x2) + if (arrow1Ref.current === 'up') { + currentLine.set({ y1: currentLineMaxY.minus(offsetLength).toNumber(), y2: currentLineMaxY.minus(offsetLength).toNumber() }) + if (prevVector === currentVector) { + const point1 = { x: prevX, y: prevLine.y2 } + const point2 = { x: prevX, y: currentLine.y1 } + reArrangeOuterLine(currentIdx) + drawLine(point1, point2, currentIdx, arrow1Ref.current) } else { - if (Math.abs(currentLineY - prevLine.y1) < 2) { - prevLine.set({ y1: prevLine.y1 - length }) + if (Big(prevLine.y1).minus(currentLineMinY).abs().lte(Big(prevLine.y2).minus(currentLineMinY).abs())) { + prevLine.set({ y1: currentLine.y1 }) } else { - prevLine.set({ y2: prevLine.y2 - length }) + prevLine.set({ y2: currentLine.y1 }) } - if (Math.abs(currentLineY - nextLine.y1) < 2) { - nextLine.set({ y1: nextLine.y1 - length }) - } else { - nextLine.set({ y2: nextLine.y2 - length }) + if (Big(prevLine.y1).minus(Big(prevLine.y2)).eq(0)) { + reArrangeOuterLine(currentIdx - 1, true) + canvas.remove(prevLine) + } + } + if (nextVector === currentVector) { + const point1 = { x: nextX, y: nextLine.y2 } + const point2 = { x: nextX, y: currentLine.y1 } + reArrangeOuterLine(currentIdx + 1) + drawLine(point1, point2, currentIdx + 1, arrow1Ref.current) + } else { + if (Big(nextLine.y1).minus(currentLineMaxY).abs().lte(Big(nextLine.y2).minus(currentLineMaxY).abs())) { + nextLine.set({ y1: currentLine.y1 }) + } else { + nextLine.set({ y2: currentLine.y1 }) + } + if (Big(nextLine.y1).minus(Big(nextLine.y2)).eq(0)) { + reArrangeOuterLine(currentIdx + 1, true) + canvas.remove(nextLine) } } - - currentLine.set({ y1: currentLine.y1 - length, y2: currentLine.y2 - length }) - - break } - case 'down': { - if (prevLine.direction === currentLine.direction) { - const newX = - currentLine.direction === 'left' - ? Math.floor(Math.max(currentLine.x1, currentLine.x2)) - : Math.floor(Math.min(currentLine.x1, currentLine.x2)) - const newPoint1 = { x: newX, y: currentLineY + length } - const newPoint2 = { x: prevLine.x2, y: prevLine.y2 } - rearrangeOuterLine(currentIdx) - drawLine(newPoint1, newPoint2, currentIdx, 'bottom') - if (Math.abs(currentLineY - nextLine.y1) < 2) { - nextLine.set({ y1: currentLineY + length }) - } else { - nextLine.set({ y2: currentLineY + length }) - } - } else if (nextLine.direction === currentLine.direction) { - const newX = - currentLine.direction === 'left' - ? Math.floor(Math.min(currentLine.x1, currentLine.x2)) - : Math.floor(Math.max(currentLine.x1, currentLine.x2)) - const newPoint1 = { x: newX, y: currentLineY + length } - const newPoint2 = { x: nextLine.x1, y: nextLine.y1 } - rearrangeOuterLine(currentIdx + 1) - drawLine(newPoint1, newPoint2, currentIdx + 1, 'bottom') - if (Math.abs(currentLineY - prevLine.y1) < 2) { - prevLine.set({ y1: currentLineY + length }) - } else { - prevLine.set({ y2: currentLineY + length }) - } + if (arrow1Ref.current === 'down') { + currentLine.set({ y1: currentLineMaxY.plus(offsetLength).toNumber(), y2: currentLineMaxY.plus(offsetLength).toNumber() }) + if (prevVector === currentVector) { + const point1 = { x: prevX, y: prevLine.y2 } + const point2 = { x: prevX, y: currentLine.y1 } + reArrangeOuterLine(currentIdx) + drawLine(point1, point2, currentIdx, arrow1Ref.current) } else { - if (Math.abs(currentLineY - prevLine.y1) < 2) { - prevLine.set({ y1: prevLine.y1 + length }) + if (Big(prevLine.y1).minus(currentLineMinY).abs().lte(Big(prevLine.y2).minus(currentLineMinY).abs())) { + prevLine.set({ y1: currentLine.y1 }) } else { - prevLine.set({ y2: prevLine.y2 + length }) + prevLine.set({ y2: currentLine.y1 }) } - if (Math.abs(currentLineY - nextLine.y1) < 2) { - nextLine.set({ y1: nextLine.y1 + length }) - } else { - nextLine.set({ y2: nextLine.y2 + length }) + if (Big(prevLine.y1).minus(Big(prevLine.y2)).eq(0)) { + reArrangeOuterLine(currentIdx - 1, true) + canvas.remove(prevLine) + } + } + if (nextVector === currentVector) { + const point1 = { x: nextX, y: nextLine.y2 } + const point2 = { x: nextX, y: currentLine.y1 } + reArrangeOuterLine(currentIdx + 1) + drawLine(point1, point2, currentIdx + 1, arrow1Ref.current) + } else { + if (Big(nextLine.y1).minus(currentLineMaxY).abs().lte(Big(nextLine.y2).minus(currentLineMaxY).abs())) { + nextLine.set({ y1: currentLine.y1 }) + } else { + nextLine.set({ y2: currentLine.y1 }) + } + if (Big(nextLine.y1).minus(Big(nextLine.y2)).eq(0)) { + reArrangeOuterLine(currentIdx + 1, true) + canvas.remove(nextLine) } } - - currentLine.set({ y1: currentLine.y1 + length, y2: currentLine.y2 + length }) - break } - case 'left': { - if (prevLine.direction === currentLine.direction) { - const newY = - currentLine.direction === 'top' - ? Math.floor(Math.max(currentLine.y1, currentLine.y2)) - : Math.floor(Math.min(currentLine.y1, currentLine.y2)) - const newPoint1 = { x: currentLineX - length, y: newY } - const newPoint2 = { x: prevLine.x2, y: prevLine.y2 } - rearrangeOuterLine(currentIdx) - drawLine(newPoint1, newPoint2, currentIdx, 'left') - if (Math.abs(currentLineX - nextLine.x1) < 2) { - nextLine.set({ x1: currentLineX - length }) - } else { - nextLine.set({ x2: currentLineX - length }) - } - } else if (nextLine.direction === currentLine.direction) { - const newY = - currentLine.direction === 'top' - ? Math.floor(Math.min(currentLine.y1, currentLine.y2)) - : Math.floor(Math.max(currentLine.y1, currentLine.y2)) - const newPoint1 = { x: currentLineX - length, y: newY } - const newPoint2 = { x: nextLine.x1, y: nextLine.y1 } - rearrangeOuterLine(currentIdx + 1) - drawLine(newPoint1, newPoint2, currentIdx + 1, 'left') - if (Math.abs(currentLineX - prevLine.x1) < 2) { - prevLine.set({ x1: currentLineX - length }) - } else { - prevLine.set({ x2: currentLineX - length }) - } - } else { - if (Math.abs(currentLineX - prevLine.x1) < 2) { - prevLine.set({ x1: prevLine.x1 - length }) - } else { - prevLine.set({ x2: prevLine.x2 - length }) - } + } else { + const prevY = currentLine.y1 < currentLine.y2 ? Math.min(currentLine.y1, currentLine.y2) : Math.max(currentLine.y1, currentLine.y2) + const nextY = currentLine.y1 < currentLine.y2 ? Math.max(currentLine.y1, currentLine.y2) : Math.min(currentLine.y1, currentLine.y2) + if (arrow1Ref.current === 'left') { + currentLine.set({ x1: currentLineMaxX.minus(offsetLength).toNumber(), x2: currentLineMaxX.minus(offsetLength).toNumber() }) - if (Math.abs(currentLineX - nextLine.x1) < 2) { - nextLine.set({ x1: nextLine.x1 - length }) + if (prevVector === currentVector) { + const point1 = { x: prevLine.x2, y: prevY } + const point2 = { x: currentLine.x1, y: prevY } + reArrangeOuterLine(currentIdx) + drawLine(point1, point2, currentIdx, arrow1Ref.current) + } else { + if (Big(prevLine.x1).minus(currentLineMinX).abs().lte(Big(prevLine.x2).minus(currentLineMinX).abs())) { + prevLine.set({ x1: currentLine.x1 }) } else { - nextLine.set({ x2: nextLine.x2 - length }) + prevLine.set({ x2: currentLine.x1 }) + } + if (Big(prevLine.x1).minus(Big(prevLine.x2)).eq(0)) { + reArrangeOuterLine(currentIdx - 1, true) + canvas.remove(prevLine) + } + } + if (nextVector === currentVector) { + const point1 = { x: currentLine.x2, y: nextY } + const point2 = { x: nextLine.x1, y: nextY } + reArrangeOuterLine(currentIdx + 1) + drawLine(point1, point2, currentIdx + 1, arrow1Ref.current) + } else { + if (Big(nextLine.x1).minus(currentLineMaxX).abs().lte(Big(nextLine.x2).minus(currentLineMaxX).abs())) { + nextLine.set({ x1: currentLine.x2 }) + } else { + nextLine.set({ x2: currentLine.x2 }) + } + if (Big(nextLine.x1).minus(Big(nextLine.x2)).eq(0)) { + reArrangeOuterLine(currentIdx + 1, true) + canvas.remove(nextLine) } } - - currentLine.set({ x1: currentLine.x1 - length, x2: currentLine.x2 - length }) - break } - case 'right': { - if (prevLine.direction === currentLine.direction) { - const newY = - currentLine.direction === 'top' - ? Math.floor(Math.max(currentLine.y1, currentLine.y2)) - : Math.floor(Math.min(currentLine.y1, currentLine.y2)) - const newPoint1 = { x: currentLineX + length, y: newY } - const newPoint2 = { x: prevLine.x2, y: prevLine.y2 } - rearrangeOuterLine(currentIdx) - drawLine(newPoint1, newPoint2, currentIdx, 'right') - if (Math.abs(currentLineX - nextLine.x1) < 2) { - nextLine.set({ x1: currentLineX + length }) - } else { - nextLine.set({ x2: currentLineX + length }) - } - } else if (nextLine.direction === currentLine.direction) { - const newY = - currentLine.direction === 'top' - ? Math.floor(Math.min(currentLine.y1, currentLine.y2)) - : Math.floor(Math.max(currentLine.y1, currentLine.y2)) - const newPoint1 = { x: currentLineX + length, y: newY } - const newPoint2 = { x: nextLine.x1, y: nextLine.y1 } - rearrangeOuterLine(currentIdx + 1) - drawLine(newPoint1, newPoint2, currentIdx + 1, 'right') + if (arrow1Ref.current === 'right') { + currentLine.set({ x1: currentLineMaxX.plus(offsetLength).toNumber(), x2: currentLineMaxX.plus(offsetLength).toNumber() }) - if (Math.abs(currentLineX - prevLine.x1) < 2) { - prevLine.set({ x1: currentLineX + length }) - } else { - prevLine.set({ x2: currentLineX + length }) - } + if (prevVector === currentVector) { + const point1 = { x: prevLine.x2, y: prevY } + const point2 = { x: currentLine.x1, y: prevY } + reArrangeOuterLine(currentIdx) + drawLine(point1, point2, currentIdx, arrow1Ref.current) } else { - if (Math.abs(currentLineX - prevLine.x1) < 2) { - prevLine.set({ x1: prevLine.x1 + length }) + if (Big(prevLine.x1).minus(currentLineMinX).abs().lte(Big(prevLine.x2).minus(currentLineMinX).abs())) { + prevLine.set({ x1: currentLine.x1 }) } else { - prevLine.set({ x2: prevLine.x2 + length }) + prevLine.set({ x2: currentLine.x1 }) } - if (Math.abs(currentLineX - nextLine.x1) < 2) { - nextLine.set({ x1: nextLine.x1 + length }) - } else { - nextLine.set({ x2: nextLine.x2 + length }) + + if (Big(prevLine.x1).minus(Big(prevLine.x2)).eq(0)) { + reArrangeOuterLine(currentIdx - 1, true) + canvas.remove(prevLine) } } + if (nextVector === currentVector) { + const point1 = { x: currentLine.x2, y: nextY } + const point2 = { x: nextLine.x1, y: nextY } + reArrangeOuterLine(currentIdx + 1) + drawLine(point1, point2, currentIdx + 1, arrow1Ref.current) + } else { + if (Big(nextLine.x1).minus(currentLineMaxX).abs().lte(Big(nextLine.x2).minus(currentLineMaxX).abs())) { + nextLine.set({ x1: currentLine.x2 }) + } else { + nextLine.set({ x2: currentLine.x2 }) + } - currentLine.set({ x1: currentLine.x1 + length, x2: currentLine.x2 + length }) - - break + if (Big(nextLine.x1).minus(Big(nextLine.x2)).eq(0)) { + reArrangeOuterLine(currentIdx + 1, true) + canvas.remove(nextLine) + } + } } } + + const newOuterLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + newOuterLines.sort((a, b) => a.idx - b.idx) + newOuterLines.forEach((line, idx) => { + line.fire('modified') + }) + canvas.renderAll() } diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index 06f1428a..19d43c5e 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -33,7 +33,7 @@ import { import { QLine } from '@/components/fabric/QLine' import { fabric } from 'fabric' import { QPolygon } from '@/components/fabric/QPolygon' -import offsetPolygon from '@/util/qpolygon-utils' +import offsetPolygon, { calculateAngle } from '@/util/qpolygon-utils' import { isObjectNotEmpty } from '@/util/common-utils' import * as turf from '@turf/turf' import { INPUT_TYPE, LINE_TYPE, Mode, POLYGON_TYPE } from '@/common/common' @@ -1771,6 +1771,16 @@ export function useMode() { wall.lines = afterLine.concat(beforeLine) //외벽선을 기준으로 polygon을 생성한다. 지붕선의 기준이 됨. + const divWallLines = [] + wall.lines.forEach((currentWall, index) => { + const nextWall = wall.lines[(index + 1) % wall.lines.length] + const currentAngle = calculateAngle(currentWall.startPoint, currentWall.endPoint) + const nextAngle = calculateAngle(nextWall.startPoint, nextWall.endPoint) + if (currentAngle === nextAngle) { + divWallLines.push({ currentWall: currentWall, nextWall: nextWall, index: index }) + } + }) + const polygon = createRoofPolygon(wall.points) const originPolygon = new QPolygon(wall.points, { fontSize: 0 }) originPolygon.setViewLengthText(false) @@ -1787,6 +1797,45 @@ export function useMode() { offsetPolygon = createPaddingPolygon(polygon, wall.lines).vertices } + if (divWallLines.length > 0) { + /** + * 외벽선을 분기한 횟수를 저장한다. 외벽선은 offset이 같지 않을때 분기한다. + */ + let addPoint = 0 + + divWallLines.forEach((line) => { + const currentWall = line.currentWall + const nextWall = line.nextWall + const index = line.index + addPoint + const xDiff = Big(currentWall.x1).minus(Big(nextWall.x1)) + const yDiff = Big(currentWall.y1).minus(Big(nextWall.y1)) + const offsetCurrentPoint = offsetPolygon[index] + let offsetNextPoint = offsetPolygon[(index + 1) % offsetPolygon.length] + line.index = index + + if (currentWall.attributes.offset !== nextWall.attributes.offset) { + const offsetPoint1 = { + x: xDiff.eq(0) ? offsetCurrentPoint.x : nextWall.x1, + y: yDiff.eq(0) ? offsetCurrentPoint.y : nextWall.y1, + } + const diffOffset = Big(nextWall.attributes.offset).minus(Big(currentWall.attributes.offset)) + const offsetPoint2 = { + x: yDiff.eq(0) ? offsetPoint1.x : Big(offsetPoint1.x).plus(diffOffset).toNumber(), + y: xDiff.eq(0) ? offsetPoint1.y : Big(offsetPoint1.y).plus(diffOffset).toNumber(), + } + const offsetPoint3 = { + x: yDiff.eq(0) ? offsetNextPoint.x : Big(offsetNextPoint.x).plus(diffOffset).toNumber(), + y: xDiff.eq(0) ? offsetNextPoint.y : Big(offsetNextPoint.y).plus(diffOffset).toNumber(), + } + offsetPolygon.splice(index + 1, 0, offsetPoint1, offsetPoint2) + offsetNextPoint = offsetPoint3 + addPoint++ + } else { + addPoint-- + } + }) + } + const roof = makePolygon( offsetPolygon.map((point) => { return { x1: point.x, y1: point.y } @@ -1806,6 +1855,8 @@ export function useMode() { roof.name = POLYGON_TYPE.ROOF roof.setWall(wall) + let roofWallIndex = 0 + roof.lines.forEach((line, index) => { const x1 = Big(line.x1) const x2 = Big(line.x2) @@ -1816,18 +1867,21 @@ export function useMode() { roofId: roof.id, planeSize: lineLength, actualSize: lineLength, - wallLine: wall.lines[index].id, - type: wall.lines[index].attributes.type, - offset: wall.lines[index].attributes.offset, - width: wall.lines[index].attributes.width, - pitch: wall.lines[index].attributes.pitch, - sleeve: wall.lines[index].attributes.sleeve || false, + wallLine: wall.lines[roofWallIndex].id, + type: wall.lines[roofWallIndex].attributes.type, + offset: wall.lines[roofWallIndex].attributes.offset, + width: wall.lines[roofWallIndex].attributes.width, + pitch: wall.lines[roofWallIndex].attributes.pitch, + sleeve: wall.lines[roofWallIndex].attributes.sleeve || false, + } + + const isDivLine = divWallLines.some((divLine) => divLine.index === index) + if (!isDivLine) { + roofWallIndex++ } }) wall.set({ - // originX: 'center', - // originY: 'center', attributes: { roofId: roof.id, }, @@ -1846,7 +1900,7 @@ export function useMode() { const y2 = Big(line.y2) const lineLength = x1.minus(x2).abs().pow(2).plus(y1.minus(y2).abs().pow(2)).sqrt().times(10).round().toNumber() line.attributes.roofId = roof.id - line.attributes.currentRoofId = roof.lines[index].id + // line.attributes.currentRoofId = roof.lines[index].id line.attributes.planeSize = lineLength line.attributes.actualSize = lineLength diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js index 99845d46..50470060 100644 --- a/src/hooks/usePolygon.js +++ b/src/hooks/usePolygon.js @@ -1,7 +1,7 @@ -import { ANGLE_TYPE, canvasState, currentAngleTypeSelector, fontFamilyState, fontSizeState, pitchTextSelector } from '@/store/canvasAtom' +import { ANGLE_TYPE, canvasState, currentAngleTypeSelector, pitchTextSelector } from '@/store/canvasAtom' import { useRecoilValue } from 'recoil' import { fabric } from 'fabric' -import { findAndRemoveClosestPoint, getDegreeByChon, getDegreeInOrientation, getDirectionByPoint, isPointOnLine } from '@/util/canvas-util' +import { findAndRemoveClosestPoint, getDegreeByChon, getDegreeInOrientation, isPointOnLine } from '@/util/canvas-util' import { QPolygon } from '@/components/fabric/QPolygon' import { isSamePoint, removeDuplicatePolygons } from '@/util/qpolygon-utils' import { flowDisplaySelector } from '@/store/settingAtom' diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index 0444c93d..74e4654f 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -23,11 +23,11 @@ export const defineQPloygon = () => { * @returns {number} */ export const calculateAngle = (point1, point2) => { - const deltaX = Big(point2?.x !== undefined ? point2.x : 0) - .minus(point1?.x !== undefined ? point1.x : 0) + const deltaX = Big(point2.x ?? 0) + .minus(point1.x ?? 0) .toNumber() - const deltaY = Big(point2?.y !== undefined ? point2.y : 0) - .minus(point1?.y !== undefined ? point1.y : 0) + const deltaY = Big(point2.y ?? 0) + .minus(point1.y ?? 0) .toNumber() const angleInRadians = Math.atan2(deltaY, deltaX) return angleInRadians * (180 / Math.PI) @@ -3525,7 +3525,14 @@ export const calcLinePlaneSize = (points) => { * @returns number */ export const calcLineActualSize = (points, degree) => { + const { x1, y1, x2, y2 } = points const planeSize = calcLinePlaneSize(points) - const height = Big(Math.tan(Big(degree).times(Math.PI / 180))).times(planeSize) + let height = Big(Math.tan(Big(degree).times(Math.PI / 180))).times(planeSize) + /** + * 대각선일 경우 높이 계산 변경 + */ + if (x1 !== x2 && y1 !== y2) { + height = Big(Math.tan(Big(degree).times(Math.PI / 180))).times(Big(x1).minus(x2).times(10).round()) + } return Big(planeSize).pow(2).plus(height.pow(2)).sqrt().abs().round().toNumber() } From 99a584f8cf9c6ae370d27092704832e62b13f551 Mon Sep 17 00:00:00 2001 From: Jaeyoung Lee Date: Mon, 17 Mar 2025 17:35:43 +0900 Subject: [PATCH 02/46] =?UTF-8?q?=EC=A7=80=EB=B6=95=EB=8D=AE=EA=B0=9C=20?= =?UTF-8?q?=EA=B7=B8=EB=A6=AC=EA=B8=B0=20=EB=A1=9C=EC=A7=81=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useMode.js | 14 +- src/util/qpolygon-utils.js | 2335 +++++++++++++++++++++++++++++++++++- 2 files changed, 2340 insertions(+), 9 deletions(-) diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index 19d43c5e..4dcbd6b7 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -1679,10 +1679,11 @@ export function useMode() { const offsetEdges = [] polygon.edges.forEach((edge, i) => { - const offset = + /* const offset = lines[i % lines.length].attributes.offset === undefined || lines[i % lines.length].attributes.offset === 0 ? 0.1 - : lines[i % lines.length].attributes.offset + : lines[i % lines.length].attributes.offset*/ + const offset = lines[i % lines.length].attributes.offset const dx = edge.outwardNormal.x * offset const dy = edge.outwardNormal.y * offset offsetEdges.push(createOffsetEdge(edge, dx, dy)) @@ -1717,10 +1718,12 @@ export function useMode() { const offsetEdges = [] polygon.edges.forEach((edge, i) => { - const offset = + /*const offset = lines[i % lines.length].attributes.offset === undefined || lines[i % lines.length].attributes.offset === 0 ? 0.1 - : lines[i % lines.length].attributes.offset + : lines[i % lines.length].attributes.offset*/ + const offset = lines[i % lines.length].attributes.offset + const dx = edge.inwardNormal.x * offset const dy = edge.inwardNormal.y * offset offsetEdges.push(createOffsetEdge(edge, dx, dy)) @@ -1769,6 +1772,8 @@ export function useMode() { }) wall.lines = afterLine.concat(beforeLine) + wall.baseLines = wall.lines + wall.colorLines = [] //외벽선을 기준으로 polygon을 생성한다. 지붕선의 기준이 됨. const divWallLines = [] @@ -1947,6 +1952,7 @@ export function useMode() { strokeWidth: wallStrokeWidth, selectable: false, }) + wall.colorLines.push(wallLine) canvas.add(wallLine) }) diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index 74e4654f..5b5c1bab 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -30,7 +30,9 @@ export const calculateAngle = (point1, point2) => { .minus(point1.y ?? 0) .toNumber() const angleInRadians = Math.atan2(deltaY, deltaX) - return angleInRadians * (180 / Math.PI) + return Big(angleInRadians * (180 / Math.PI)) + .round() + .toNumber() } function inwardEdgeNormal(vertex1, vertex2) { @@ -739,15 +741,2338 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { //Math.abs(line.x1 - line.x2) > 1 && Math.abs(line.y1 - line.y2) > 1 const hasNonParallelLines = roof.lines.filter((line) => Big(line.x1).minus(Big(line.x2)).gt(1) && Big(line.y1).minus(Big(line.y2)).gt(1)) if (hasNonParallelLines.length > 0) { - alert('대각선이 존재합니다.') + // alert('대각선이 존재합니다.') return } - drawRidge(roof, canvas, textMode) + const eavesType = [LINE_TYPE.WALLLINE.EAVES, LINE_TYPE.WALLLINE.HIPANDGABLE] + const gableType = [LINE_TYPE.WALLLINE.GABLE, LINE_TYPE.WALLLINE.JERKINHEAD] + // + // const eaves = roof.lines.filter((line) => eavesType.includes(line.attributes?.type)) + // const gables = roof.lines.filter((line) => gableType.includes(line.attributes?.type)) + + /** + * 외벽선 + */ + const baseLines = roof.wall.baseLines + const baseLinePoints = baseLines.map((line) => ({ x: line.x1, y: line.y1 })) + + console.log('지붕 마루 갯수 확인 : ', getMaxRidge(baseLines.length)) + + /** + * baseLine을 기준으로 확인용 polygon 작성 + */ + const checkWallPolygon = new QPolygon(baseLinePoints, {}) + + /** + * 외벽선이 시계방향인지 시계반대 방향인지 확인 + * @type {boolean} + */ + let counterClockwise = true + let signedArea = 0 + + baseLinePoints.forEach((point, index) => { + const nextPoint = baseLinePoints[(index + 1) % baseLinePoints.length] + signedArea += point.x * nextPoint.y - point.y * nextPoint.x + }) + + if (signedArea > 0) { + counterClockwise = false + } + + const drawEavesFirstLines = [] + const drawEavesSecondLines = [] + /** + * 모양을 판단하여 그린다. + */ + baseLines.forEach((currentLine, index) => { + /** + * 현재 라인이 처마유형일 경우 + */ + if (eavesType.includes(currentLine.attributes?.type)) { + const nextLine = baseLines[(index + 1) % baseLines.length] + const prevLine = baseLines[(index - 1 + baseLines.length) % baseLines.length] + /** + * 현재 라인이 처마인 경우 + */ + if (eavesType.includes(nextLine.attributes?.type)) { + const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint) + const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint) + /** + * 이전, 다음 라인이 처마일때 라인의 방향이 반대면 ㄷ 모양으로 판단한다. + */ + if ( + eavesType.includes(prevLine.attributes?.type) && + eavesType.includes(nextLine.attributes?.type) && + Big(prevAngle).minus(Big(nextAngle)).abs().eq(180) + ) { + const checkScale = Big(10) + const xVector = Big(nextLine.x2).minus(Big(nextLine.x1)) + const yVector = Big(nextLine.y2).minus(Big(nextLine.y1)) + const currentMidX = Big(currentLine.x1).plus(Big(currentLine.x2)).div(2) + const currentMidY = Big(currentLine.y1).plus(Big(currentLine.y2)).div(2) + + /** + * 다음라인 방향에 포인트를 확인해서 역방향 ㄷ 모양인지 판단한다. + * @type {{x: *, y: *}} + */ + const checkPoints = { + x: currentMidX.plus(checkScale.times(Math.sign(xVector.toNumber()))).toNumber(), + y: currentMidY.plus(checkScale.times(Math.sign(yVector.toNumber()))).toNumber(), + } + + // const checkMidLine = new fabric.Line([currentMidX, currentMidY, checkPoints.x, checkPoints.y], { + // stroke: 'blue', + // strokeWidth: 2, + // }) + // canvas.add(checkMidLine) + // canvas.renderAll() + + if (checkWallPolygon.inPolygon(checkPoints)) { + drawEavesFirstLines.push({ currentLine, prevLine, nextLine, size: currentLine.attributes.planeSize }) + } else { + drawEavesSecondLines.push({ currentLine, prevLine, nextLine, size: currentLine.attributes.planeSize }) + } + } else { + drawEavesSecondLines.push({ currentLine, prevLine, nextLine, size: currentLine.attributes.planeSize }) + } + } + } + }) + + drawEavesFirstLines.sort((a, b) => a.size - b.size) + + /** 추녀마루 */ + let baseHipLines = [] + /** 용마루 */ + let baseRidgeLines = [] + /** ⨆ 모양 처마에 추녀마루를 그린다. */ + drawEavesFirstLines.forEach((current) => { + const { currentLine, prevLine, nextLine } = current + let { x1, x2, y1, y2 } = currentLine + let beforePrevLine, afterNextLine + + /** 이전 라인의 경사 */ + const prevDegree = prevLine.attributes.pitch > 0 ? getDegreeByChon(prevLine.attributes.pitch) : prevLine.attributes.degree + /** 다음 라인의 경사 */ + const currentDegree = currentLine.attributes.pitch > 0 ? getDegreeByChon(currentLine.attributes.pitch) : currentLine.attributes.degree + + /** 이전 라인의 전라인, 다음 라인의 다음라인을 찾는다 */ + baseLines.forEach((line, index) => { + if (line === prevLine) { + beforePrevLine = baseLines[(index - 1 + baseLines.length) % baseLines.length] + } + if (line === nextLine) { + afterNextLine = baseLines[(index + 1) % baseLines.length] + } + }) + + /** 이전, 다음라인의 사잇각의 vector를 구한다. */ + let prevVector = getHalfAngleVector(prevLine, currentLine) + let nextVector = getHalfAngleVector(currentLine, nextLine) + + let prevHipVector = { x: Big(prevVector.x), y: Big(prevVector.y) } + let nextHipVector = { x: Big(nextVector.x), y: Big(nextVector.y) } + + /** 각 라인의 흐름 방향을 확인한다. */ + const currentAngle = calculateAngle(currentLine.startPoint, currentLine.endPoint) + const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint) + const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint) + const beforePrevAngle = calculateAngle(beforePrevLine.startPoint, beforePrevLine.endPoint) + const afterNextAngle = calculateAngle(afterNextLine.startPoint, afterNextLine.endPoint) + + /** 현재 라인의 길이 추녀마루 길이의 기준이 된다. */ + let currentSize = Big(x2) + .minus(Big(x1)) + .plus(Big(y2).minus(Big(y1))) + .abs() + + // console.log('currentLine : ', currentLine.attributes.planeSize) + + /** 이전 라인과의 사이 추녀마루의 각도를 확인한다, 각도가 지붕안쪽으로 향하지 않을때 반대로 처리한다.*/ + const prevCheckPoint = { + x: Big(x1).plus(Big(prevHipVector.x).times(10)), + y: Big(y1).plus(Big(prevHipVector.y).times(10)), + } + if (!checkWallPolygon.inPolygon(prevCheckPoint)) { + prevHipVector = { x: Big(prevHipVector.x).neg().toNumber(), y: Big(prevHipVector.y).neg().toNumber() } + } + + /** 다음 라인과의 사이 추녀마루의 각도를 확인한다, 각도가 지붕안쪽으로 향하지 않을때 반대로 처리한다.*/ + const nextCheckPoint = { + x: Big(x2).plus(Big(nextHipVector.x).times(10)), + y: Big(y2).plus(Big(nextHipVector.y).times(10)), + } + if (!checkWallPolygon.inPolygon(nextCheckPoint)) { + nextHipVector = { x: Big(nextHipVector.x).neg().toNumber(), y: Big(nextHipVector.y).neg().toNumber() } + } + + /** 현재 라인의 길이를 기준으로 추녀 마루의 길이를 삼각함수를 사용하여 판단한다.*/ + let hipLength = currentSize.div(2).pow(2).plus(currentSize.div(2).pow(2)).sqrt() + + /** + * 현재 라인에서 2번째 전 라인과 2번째 후 라인의 각도가 같을때 -_- 와 같은 형태로 판단하고 + * 맞은 편 외벽선까지의 거리를 확인후 currentSize 를 조정한다. + */ + if (currentAngle === beforePrevAngle && currentAngle === afterNextAngle) { + const xVector = Big(nextLine.x2).minus(Big(nextLine.x1)) + const yVector = Big(nextLine.y2).minus(Big(nextLine.y1)) + const currentMidX = Big(x1).plus(Big(x2)).div(2) + const currentMidY = Big(y1).plus(Big(y2)).div(2) + + const midLineEdge = { + vertex1: { x: currentMidX.toNumber(), y: currentMidY.toNumber() }, + vertex2: { + x: currentMidX.plus(currentSize.times(Math.sign(xVector))).toNumber(), + y: currentMidY.plus(currentSize.times(Math.sign(yVector))).toNumber(), + }, + } + + /*const checkMidLine = new fabric.Line([midLineEdge.vertex1.x, midLineEdge.vertex1.y, midLineEdge.vertex2.x, midLineEdge.vertex2.y], { + stroke: 'blue', + strokeWidth: 2, + }) + canvas.add(checkMidLine) + canvas.renderAll()*/ + + /** 현재 라인의 중심 지점에서 현재라인의 길이만큼 다음라인의 방향만큼 거리를 확인한다*/ + baseLines + .filter((line) => line !== currentLine) + .forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(midLineEdge, lineEdge) + /** 현재라인의 길이만큼 거리가 모자라면 해당 길이 만큼을 현재라인의 길이로 판단하고 나머지 계산을 진행한다.*/ + if (intersection && !intersection.isIntersectionOutside) { + const intersectionSize = Big(intersection.x) + .minus(Big(currentMidX)) + .plus(Big(intersection.y).minus(Big(currentMidY))) + if (intersectionSize.lt(currentSize)) { + currentSize = intersectionSize + } + } + }) + + hipLength = currentSize.div(2).pow(2).plus(currentSize.div(2).pow(2)).sqrt() + } else { + if (currentAngle !== beforePrevAngle && currentAngle !== afterNextAngle && beforePrevLine !== afterNextLine) { + console.log('4각 아님') + // console.log('currentLine : ', currentLine.attributes.planeSize) + const beforePrevX1 = beforePrevLine.x1, + beforePrevY1 = beforePrevLine.y1, + beforePrevX2 = beforePrevLine.x2, + beforePrevY2 = beforePrevLine.y2 + const afterNextX1 = afterNextLine.x1, + afterNextY1 = afterNextLine.y1, + afterNextX2 = afterNextLine.x2, + afterNextY2 = afterNextLine.y2 + + /** beforePrevLine 과 afterNextLine 을 연결하는 사각형의 경우 6각으로 판단 */ + const connectBAPoint = { x1: afterNextX2, y1: afterNextY2, x2: beforePrevX1, y2: beforePrevY1 } + const isConnect = + baseLines.filter( + (line) => + line.x1 === connectBAPoint.x1 && line.y1 === connectBAPoint.y1 && line.x2 === connectBAPoint.x2 && line.y2 === connectBAPoint.y2, + ).length > 0 + + /** 6각 */ + // console.log('isConnect : ', isConnect) + if (isConnect) { + const checkScale = currentSize.pow(2).plus(currentSize.pow(2)).sqrt() + const intersectBaseLine = [] + if (baseHipLines.filter((line) => line.x1 === x1 && line.y1 === y1).length === 0) { + const prevEndPoint = { + x: Big(x1).plus(Big(prevHipVector.x).times(checkScale)), + y: Big(y1).plus(Big(prevHipVector.y).times(checkScale)), + } + baseLines + .filter((line) => line !== currentLine && line !== prevLine && line !== nextLine) + .forEach((line) => { + const intersection = edgesIntersection( + { vertex1: { x: x1, y: y1 }, vertex2: prevEndPoint }, + { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }, + ) + if (intersection && !intersection.isIntersectionOutside) { + /*const intersectCircle = new fabric.Circle({ + left: intersection.x - 2, + top: intersection.y - 2, + radius: 4, + fill: 'red', + parentId: roof.id, + }) + canvas.add(intersectCircle) + canvas.renderAll()*/ + intersectBaseLine.push({ + intersection, + distance: Big(intersection.x) + .minus(Big(x1)) + .pow(2) + .plus(Big(intersection.y).minus(Big(y1)).pow(2)) + .sqrt(), + }) + } + }) + } + if (baseHipLines.filter((line) => line.x1 === x2 && line.y1 === y2).length === 0) { + const nextEndPoint = { + x: Big(x2).plus(Big(nextHipVector.x).times(checkScale)), + y: Big(y2).plus(Big(nextHipVector.y).times(checkScale)), + } + baseLines + .filter((line) => line !== currentLine && line !== prevLine && line !== nextLine) + .forEach((line) => { + const intersection = edgesIntersection( + { vertex1: { x: x2, y: y2 }, vertex2: nextEndPoint }, + { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }, + ) + if (intersection && !intersection.isIntersectionOutside) { + /*const intersectCircle = new fabric.Circle({ + left: intersection.x - 2, + top: intersection.y - 2, + radius: 4, + fill: 'orange', + parentId: roof.id, + }) + canvas.add(intersectCircle) + canvas.renderAll()*/ + intersectBaseLine.push({ + intersection, + distance: Big(intersection.x) + .minus(Big(x2)) + .pow(2) + .plus(Big(intersection.y).minus(Big(y2)).pow(2)) + .sqrt(), + }) + } + }) + } + const intersection = intersectBaseLine.reduce((prev, current) => (prev.distance < current.distance ? prev : current), intersectBaseLine[0]) + if (intersection) { + /*const intersectCircle = new fabric.Circle({ + left: intersection.intersection.x - 2, + top: intersection.intersection.y - 2, + radius: 4, + fill: 'green', + parentId: roof.id, + }) + canvas.add(intersectCircle) + canvas.renderAll()*/ + + hipLength = intersection.distance + } + } else { + //라인 확인용 + /*const checkCurrentLine = new fabric.Line([x1, y1, x2, y2], { + stroke: 'yellow', + strokeWidth: 4, + parentId: roof.id, + }) + canvas.add(checkCurrentLine) + canvas.renderAll()*/ + + const rightAngleLine = baseLines + .filter( + (line) => + line !== prevLine && + line !== nextLine && + (prevAngle === calculateAngle(line.startPoint, line.endPoint) || nextAngle === calculateAngle(line.startPoint, line.endPoint)), + ) + .filter((line) => { + const index = baseLines.indexOf(line) + const prevLine = baseLines[(index - 1 + baseLines.length) % baseLines.length] + const nextLine = baseLines[(index + 1) % baseLines.length] + const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint) + const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint) + switch (prevAngle) { + case 90: + return nextAngle === -90 + case -90: + return nextAngle === 90 + case 0: + return nextAngle === 180 + case 180: + return nextAngle === 0 + } + }) + + const oppositeCurrentLine = baseLines + .filter((line) => { + const angle = calculateAngle(line.startPoint, line.endPoint) + switch (currentAngle) { + case 90: + return angle === -90 + case -90: + return angle === 90 + case 0: + return angle === 180 + case 180: + return angle === 0 + } + }) + .filter((line) => { + const index = baseLines.indexOf(line) + const prevLine = baseLines[(index - 1 + baseLines.length) % baseLines.length] + const nextLine = baseLines[(index + 1) % baseLines.length] + const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint) + const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint) + switch (prevAngle) { + case 90: + return nextAngle === -90 + case -90: + return nextAngle === 90 + case 0: + return nextAngle === 180 + case 180: + return nextAngle === 0 + } + }) + + let checkHipPoints + if (baseHipLines.filter((line) => line.x1 === x1 && line.y1 === y1).length === 0) { + checkHipPoints = [ + x1, + y1, + Big(x1).plus(Big(prevHipVector.x).times(hipLength)).round(2), + Big(y1).plus(Big(prevHipVector.y).times(hipLength)).round(2), + ] + } + + if (baseHipLines.filter((line) => line.x1 === x2 && line.y1 === y2).length === 0) { + checkHipPoints = [ + x2, + y2, + Big(x2).plus(Big(nextHipVector.x).times(hipLength)).round(2), + Big(y2).plus(Big(nextHipVector.y).times(hipLength)).round(2), + ] + } + + /* const checkHipLine = new fabric.Line(checkHipPoints, { + stroke: 'red', + strokeWidth: 2, + parentId: roof.id, + }) + canvas.add(checkHipLine) + canvas.renderAll()*/ + + if (checkHipPoints) { + const intersectPoints = [] + rightAngleLine.forEach((line) => { + /*const checkRightAngle = new fabric.Line([line.x1, line.y1, line.x2, line.y2], { + stroke: 'red', + strokeWidth: 4, + parentId: roof.id, + }) + canvas.add(checkRightAngle) + canvas.renderAll()*/ + + const intersection = edgesIntersection( + { vertex1: { x: checkHipPoints[0], y: checkHipPoints[1] }, vertex2: { x: checkHipPoints[2], y: checkHipPoints[3] } }, + { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }, + ) + if (intersection) { + /*const checkRightAngleCircle = new fabric.Circle({ + left: intersection.x - 2, + top: intersection.y - 2, + radius: 4, + fill: 'red', + parentId: roof.id, + }) + canvas.add(checkRightAngleCircle) + canvas.renderAll()*/ + intersectPoints.push({ + intersection, + distance: Big(intersection.x) + .minus(Big(checkHipPoints[0])) + .pow(2) + .plus(Big(intersection.y).minus(Big(checkHipPoints[1])).pow(2)) + .sqrt(), + }) + } + }) + + oppositeCurrentLine.forEach((line) => { + /*const checkOpposite = new fabric.Line([line.x1, line.y1, line.x2, line.y2], { + stroke: 'blue', + strokeWidth: 4, + parentId: roof.id, + }) + canvas.add(checkOpposite) + canvas.renderAll() +*/ + const intersection = edgesIntersection( + { vertex1: { x: checkHipPoints[0], y: checkHipPoints[1] }, vertex2: { x: checkHipPoints[2], y: checkHipPoints[3] } }, + { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }, + ) + if (intersection) { + /*const checkOppositeCircle = new fabric.Circle({ + left: intersection.x - 2, + top: intersection.y - 2, + radius: 4, + fill: 'blue', + parentId: roof.id, + }) + canvas.add(checkOppositeCircle) + canvas.renderAll()*/ + intersectPoints.push({ + intersection, + distance: Big(intersection.x) + .minus(Big(checkHipPoints[0])) + .pow(2) + .plus(Big(intersection.y).minus(Big(checkHipPoints[1])).pow(2)) + .sqrt(), + }) + } + }) + + // console.log('intersectPoints : ', intersectPoints) + + const intersection = intersectPoints.reduce((prev, current) => (prev.distance.lt(current.distance) ? prev : current), intersectPoints[0]) + if (intersection) { + /*const checkHipCircle = new fabric.Circle({ + left: intersection.intersection.x - 2, + top: intersection.intersection.y - 2, + radius: 4, + fill: 'green', + parentId: roof.id, + }) + canvas.add(checkHipCircle) + canvas.renderAll()*/ + hipLength = intersection.distance.div(2) + } + } + } + } + } + + let prevHipLine, nextHipLine + /** 이전라인과의 연결지점에 추녀마루를 그린다. */ + if (baseHipLines.filter((line) => line.x1 === x1 && line.y1 === y1).length === 0) { + const prevEndPoint = { + x: Big(x1).plus(Big(prevHipVector.x).times(hipLength)).round(1), + y: Big(y1).plus(Big(prevHipVector.y).times(hipLength)).round(1), + } + + const intersectRidgeLine = [] + baseRidgeLines.forEach((line) => { + const intersection = edgesIntersection( + { vertex1: { x: x1, y: y1 }, vertex2: prevEndPoint }, + { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }, + ) + // console.log('intersection : ', intersection, 'prevEndPoint : ', prevEndPoint.x.toNumber(), prevEndPoint.y.toNumber()) + if (intersection && !intersection.isIntersectionOutside) { + intersectRidgeLine.push({ + intersection, + distance: Big(intersection.x) + .minus(Big(x1)) + .pow(2) + .plus(Big(intersection.y).minus(Big(y1)).pow(2)) + .sqrt(), + }) + } + }) + const intersectRidge = intersectRidgeLine.reduce((prev, current) => (prev.distance < current.distance ? prev : current), intersectRidgeLine[0]) + // console.log('intersectRidge : ', intersectRidge) + + if (intersectRidge) { + prevEndPoint.x = Big(intersectRidge.intersection.x).round(1) + prevEndPoint.y = Big(intersectRidge.intersection.y).round(1) + } + + const xVector = Math.sign(Big(prevEndPoint.x).minus(Big(x1)).neg().toNumber()) + const yVector = Math.sign(Big(prevEndPoint.y).minus(Big(y1)).neg().toNumber()) + /** 지붕 선까지의 곂침에 따른 길이를 파악하기 위한 scale*/ + let scale = Big(currentLine.attributes.offset).pow(2).plus(Big(prevLine.attributes.offset).pow(2)).sqrt() + scale = scale.eq(0) ? Big(1) : scale + + /** scale 만큼 추녀마루를 늘려서 겹치는 포인트를 확인한다. */ + const hipEdge = { + vertex1: { x: Big(x1).plus(scale.times(xVector)).toNumber(), y: Big(y1).plus(scale.times(yVector)).toNumber() }, + vertex2: prevEndPoint, + } + + /*const checkLine = new fabric.Line([hipEdge.vertex1.x, hipEdge.vertex1.y, hipEdge.vertex2.x, hipEdge.vertex2.y], { + stroke: 'yellow', + strokeWidth: 2, + parentId: roof.id, + }) + canvas.add(checkLine) + canvas.renderAll()*/ + + let intersectPoints = [] + /** 외벽선에서 추녀 마루가 겹치는 경우에 대한 확인*/ + roof.lines.forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(hipEdge, lineEdge) + if ( + intersection && + !intersection.isIntersectionOutside && + Math.sign(prevEndPoint.x - x1) === Math.sign(prevEndPoint.x - intersection.x) && + Math.sign(prevEndPoint.y - y1) === Math.sign(prevEndPoint.y - intersection.y) + ) { + /* const intersectPoint = new fabric.Circle({ + left: intersection.x - 2, + top: intersection.y - 2, + radius: 4, + fill: 'red', + parentId: roof.id, + }) + canvas.add(intersectPoint) + canvas.renderAll()*/ + + const intersectSize = prevEndPoint.x + .minus(Big(intersection.x)) + .pow(2) + .plus(prevEndPoint.y.minus(Big(intersection.y)).pow(2)) + .abs() + .sqrt() + .toNumber() + + intersectPoints.push({ + intersection, + size: intersectSize, + }) + } + }) + + intersectPoints = intersectPoints.reduce((prev, current) => { + return prev.size < current.size ? prev : current + }, intersectPoints[0]) + + // console.log('intersectPoints', intersectPoints) + + /** 겹치는 외벽선이 있을때 추녀마루를 외벽선 까지 늘려서 수정*/ + if (intersectPoints && intersectPoints.intersection) { + prevHipLine = drawHipLine( + [intersectPoints.intersection.x, intersectPoints.intersection.y, prevEndPoint.x.toNumber(), prevEndPoint.y.toNumber()], + canvas, + roof, + textMode, + null, + prevDegree, + currentDegree, + ) + baseHipLines.push({ x1, y1, x2: prevEndPoint.x.toNumber(), y2: prevEndPoint.y.toNumber(), line: prevHipLine }) + } + } + if (baseHipLines.filter((line) => line.x1 === x2 && line.y1 === y2).length === 0) { + const nextEndPoint = { + x: Big(x2).plus(Big(nextHipVector.x).times(hipLength)).round(1), + y: Big(y2).plus(Big(nextHipVector.y).times(hipLength)).round(1), + } + + const intersectRidgeLine = [] + baseRidgeLines.forEach((line) => { + // console.log('nextEndPoint : ', nextEndPoint.x.toNumber(), nextEndPoint.y.toNumber()) + // console.log('intersection : ', 'x : ', line.x1, 'y : ', line.y1, 'x : ', line.x2, 'y : ', line.y2) + const intersection = edgesIntersection( + { vertex1: { x: x2, y: y2 }, vertex2: nextEndPoint }, + { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }, + ) + // console.log('intersection : ', intersection, 'nextEndPoint : ', nextEndPoint.x.toNumber(), nextEndPoint.y.toNumber()) + if (intersection && !intersection.isIntersectionOutside) { + intersectRidgeLine.push({ + intersection, + distance: Big(intersection.x) + .minus(Big(x1)) + .pow(2) + .plus(Big(intersection.y).minus(Big(y1)).pow(2)) + .sqrt(), + }) + } + }) + const intersectRidge = intersectRidgeLine.reduce((prev, current) => (prev.distance < current.distance ? prev : current), intersectRidgeLine[0]) + // console.log('intersectRidge : ', intersectRidge) + + if (intersectRidge) { + nextEndPoint.x = Big(intersectRidge.intersection.x).round(1) + nextEndPoint.y = Big(intersectRidge.intersection.y).round(1) + } + + const xVector = Math.sign(Big(nextEndPoint.x).minus(Big(x2)).neg().toNumber()) + const yVector = Math.sign(Big(nextEndPoint.y).minus(Big(y2)).neg().toNumber()) + let scale = Big(currentLine.attributes.offset).pow(2).plus(Big(nextLine.attributes.offset).pow(2)).sqrt() + scale = scale.eq(0) ? Big(1) : scale + + const hipEdge = { + vertex1: { x: Big(x2).plus(scale.times(xVector)).toNumber(), y: Big(y2).plus(scale.times(yVector)).toNumber() }, + vertex2: nextEndPoint, + } + + /* const checkLine = new fabric.Line([hipEdge.vertex1.x, hipEdge.vertex1.y, hipEdge.vertex2.x, hipEdge.vertex2.y], { + stroke: 'yellow', + strokeWidth: 2, + parentId: roof.id, + }) + canvas.add(checkLine) + canvas.renderAll()*/ + + let intersectPoints = [] + roof.lines.forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(hipEdge, lineEdge) + // console.log('intersection', intersection) + if ( + intersection && + !intersection.isIntersectionOutside && + Math.sign(nextEndPoint.x - x2) === Math.sign(nextEndPoint.x - intersection.x) && + Math.sign(nextEndPoint.y - y2) === Math.sign(nextEndPoint.y - intersection.y) + ) { + /*const intersectPoint = new fabric.Circle({ + left: intersection.x - 2, + top: intersection.y - 2, + radius: 4, + fill: 'blue', + parentId: roof.id, + }) + canvas.add(intersectPoint) + canvas.renderAll()*/ + + // console.log('nextEndPoint', nextEndPoint.x.toNumber(), nextEndPoint.y.toNumber()) + // console.log('intersection', intersection.x, intersection.y) + + const intersectSize = nextEndPoint.x + .minus(Big(intersection.x)) + .pow(2) + .plus(nextEndPoint.y.minus(Big(intersection.y)).pow(2)) + .abs() + .sqrt() + .toNumber() + + intersectPoints.push({ + intersection, + size: intersectSize, + }) + } + }) + + intersectPoints = intersectPoints.reduce((prev, current) => { + return prev.size < current.size ? prev : current + }, intersectPoints[0]) + + // console.log('intersectPoints : ', intersectPoints) + + if (intersectPoints && intersectPoints.intersection) { + nextHipLine = drawHipLine( + [intersectPoints.intersection.x, intersectPoints.intersection.y, nextEndPoint.x.toNumber(), nextEndPoint.y.toNumber()], + canvas, + roof, + textMode, + null, + prevDegree, + currentDegree, + ) + baseHipLines.push({ x1: x2, y1: y2, x2: nextEndPoint.x.toNumber(), y2: nextEndPoint.y.toNumber(), line: nextHipLine }) + } + } + + // console.log('prevHipLine : ', prevHipLine) + // console.log('nextHipLine : ', nextHipLine) + /** 두 추녀마루가 한점에서 만나는 경우 해당 점을 기점으로 마루를 작성한다.*/ + if ( + prevHipLine !== undefined && + nextHipLine !== undefined && + Big(prevHipLine.x2).minus(Big(nextHipLine.x2)).abs().lte(1) && + Big(prevHipLine.y2).minus(Big(nextHipLine.y2)).abs().lte(1) + ) { + console.log('마루 작성') + const startPoint = { x: prevHipLine.x2, y: prevHipLine.y2 } + + // baseLines에서 가장 작은 x1과 가장 큰 x1, 가장 작은 y1과 가장 큰 y1을 계산 + let minX = Infinity + let maxX = -Infinity + let minY = Infinity + let maxY = -Infinity + + baseLines.forEach((line) => { + if (line.x1 < minX) { + minX = line.x1 + } + if (line.x1 > maxX) { + maxX = line.x1 + } + if (line.y1 < minY) { + minY = line.y1 + } + if (line.y1 > maxY) { + maxY = line.y1 + } + }) + const checkLength = Big(maxX) + .minus(Big(minX)) + .pow(2) + .plus(Big(maxY).minus(Big(minY)).pow(2)) + .sqrt() + + const currentMidX = Big(currentLine.x2).plus(Big(currentLine.x1)).div(2) + const currentMidY = Big(currentLine.y2).plus(Big(currentLine.y1)).div(2) + + const xVector = Big(currentMidX).minus(Big(startPoint.x)).round(0, Big.roundDown) + const yVector = Big(currentMidY).minus(Big(startPoint.y)).round(0, Big.roundDown) + + const checkEdges = { + vertex1: { x: startPoint.x, y: startPoint.y }, + vertex2: { + x: Big(startPoint.x).minus(checkLength.times(Math.sign(xVector))), + y: Big(startPoint.y).minus(checkLength.times(Math.sign(yVector))), + }, + } + + /*const checkLine = new fabric.Line([checkEdges.vertex1.x, checkEdges.vertex1.y, checkEdges.vertex2.x, checkEdges.vertex2.y], { + stroke: 'purple', + strokeWidth: 2, + parentId: roof.id, + }) + canvas.add(checkLine) + canvas.renderAll()*/ + + /** 맞은편 벽 까지의 길이 판단을 위한 교차되는 line*/ + const intersectBaseLine = [] + baseLines + .filter((line) => { + /** currentAngle 의 반대 각도인 라인 */ + const angle = calculateAngle(line.startPoint, line.endPoint) + switch (currentAngle) { + case 90: + return angle === -90 + case -90: + return angle === 90 + case 0: + return angle === 180 + case 180: + return angle === 0 + } + }) + .filter((line) => { + const currentMinX = Math.min(x1, x2) + const currentMaxX = Math.max(x1, x2) + const currentMinY = Math.min(y1, y2) + const currentMaxY = Math.max(y1, y2) + 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) + + /** currentLine 의 안쪽에 있거나 currentLine이 line의 안쪽에 있는 라인 */ + if (Big(currentLine.y1).minus(Big(currentLine.y2)).abs().lte(1)) { + return ( + (currentMinX <= lineMinX && lineMinX <= currentMaxX) || + (currentMinX <= lineMaxX && lineMaxX <= currentMaxX) || + (lineMinX <= currentMinX && currentMinX <= lineMaxX) || + (lineMinX <= currentMaxX && currentMaxX <= lineMaxX) + ) + } else { + return ( + (currentMinY <= lineMinY && lineMinY <= currentMaxY) || + (currentMinY <= lineMaxY && lineMaxY <= currentMaxY) || + (lineMinY <= currentMinY && currentMinY <= lineMaxY) || + (lineMinY <= currentMaxY && currentMaxY <= lineMaxY) + ) + } + }) + .forEach((line, index) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(checkEdges, lineEdge) + if (intersection) { + /*const intersectPoint = new fabric.Circle({ + left: intersection.x - 2, + top: intersection.y - 2, + radius: 4, + fill: 'green', + parentId: roof.id, + }) + canvas.add(intersectPoint) + canvas.renderAll()*/ + intersectBaseLine.push({ intersection, line }) + } + }) + + // console.log('intersectBaseLine : ', intersectBaseLine) + + /** 맞은편 라인 */ + const oppositeLine = intersectBaseLine.reduce((prev, current) => { + const prevDistance = Big(prev.intersection.x) + .minus(Big(startPoint.x)) + .pow(2) + .plus(Big(prev.intersection.y).minus(Big(startPoint.y)).pow(2)) + .sqrt() + const currentDistance = Big(current.intersection.x) + .minus(Big(startPoint.x)) + .pow(2) + .plus(Big(current.intersection.y).minus(Big(startPoint.y)).pow(2)) + .sqrt() + return prevDistance < currentDistance ? prev : current + }, intersectBaseLine[0]) + + // console.log('oppositeLine : ', oppositeLine) + + /** 맞은편 라인까지의 길이 = 전체 길이 - 현재라인의 길이 */ + const oppositeSize = oppositeLine + ? Big(oppositeLine.intersection.x) + .minus(Big(startPoint.x)) + .pow(2) + .plus(Big(oppositeLine.intersection.y).minus(Big(startPoint.y)).pow(2)) + .sqrt() + .minus(currentSize.div(2)) + .round(1) + : Infinity + + // console.log('startPoint : ', startPoint, 'currentSize : ', currentSize.toNumber()) + + /** 이전, 다음 라인중 길이가 짧은 길이*/ + const lineMinSize = + prevLine.attributes.planeSize < nextLine.attributes.planeSize + ? Big(prevLine.attributes.planeSize).div(10) + : Big(nextLine.attributes.planeSize).div(10) + + /** 마루의 길이는 이전 다음 라인중 짧은것의 길이와 현재라인부터 맞은편 라인까지의 길이에서 현재 라인의 길이를 뺀 것중 짧은 길이 */ + const ridgeSize = Big(Math.min(oppositeSize, lineMinSize)) + + // console.log('oppositeSize : ', oppositeSize, 'lineMinSize : ', lineMinSize.toNumber(), 'ridgeSize : ', ridgeSize.toNumber()) + + if (ridgeSize.gt(0)) { + const points = [ + startPoint.x, + startPoint.y, + Big(startPoint.x).minus(ridgeSize.times(Math.sign(xVector))), + Big(startPoint.y).minus(ridgeSize.times(Math.sign(yVector))), + ] + + /** 동일 라인이 있는지 확인. */ + if ( + baseRidgeLines.filter((line) => { + const ridgeMinX = Math.min(points[0], points[2]) + const ridgeMaxX = Math.max(points[0], points[2]) + const ridgeMinY = Math.min(points[1], points[3]) + const ridgeMaxY = Math.max(points[1], points[3]) + 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) + + return ridgeMinX === lineMinX && ridgeMaxX === lineMaxX && ridgeMinY === lineMinY && ridgeMaxY === lineMaxY + }).length > 0 + ) { + return + } + + /* const checkMidLine = new fabric.Line(points, { + stroke: 'blue', + strokeWidth: 2, + parentId: roof.id, + }) + canvas.add(checkMidLine) + canvas.renderAll()*/ + // console.log('points : ', points) + + /** 마루 생성 */ + const ridgeLine = drawRidgeLine(points, canvas, roof, textMode) + baseRidgeLines.push(ridgeLine) + + /** 포인트 조정*/ + baseHipLines + .filter((line) => line.line === prevHipLine || line.line === nextHipLine) + .forEach((line) => { + line.x2 = points[0] + line.y2 = points[1] + }) + prevHipLine.x2 = points[0] + prevHipLine.y2 = points[1] + nextHipLine.x2 = points[0] + nextHipLine.y2 = points[1] + prevHipLine.fire('modified') + nextHipLine.fire('modified') + } + } + }) + + /** 중복제거 */ + baseRidgeLines.forEach((ridge) => { + baseRidgeLines + .filter((ridge2) => ridge !== ridge2) + .forEach((ridge2) => { + let overlap = segmentsOverlap(ridge, ridge2) + if (overlap) { + roof.ridges = roof.ridges.filter((r) => r !== ridge && r !== ridge2) + roof.innerLines = roof.innerLines.filter((l) => l !== ridge && l !== ridge2) + roof.canvas.remove(ridge) + roof.canvas.remove(ridge2) + baseRidgeLines = baseRidgeLines.filter((r) => r !== ridge && r !== ridge2) + + let x1 = Math.min(ridge.x1, ridge2.x1, ridge.x2, ridge2.x2) + let x2 = Math.max(ridge.x1, ridge2.x1, ridge.x2, ridge2.x2) + let y1 = Math.min(ridge.y1, ridge2.y1, ridge.y2, ridge2.y2) + let y2 = Math.max(ridge.y1, ridge2.y1, ridge.y2, ridge2.y2) + + const newRidge = drawRidgeLine([x1, y1, x2, y2], canvas, roof, textMode) + roof.ridges.push(newRidge) + roof.innerLines.push(newRidge) + baseRidgeLines.push(newRidge) + } + }) + }) + + /** ㄴ 모양 처마에 추녀마루를 그린다. */ + drawEavesSecondLines.forEach((current) => { + const { currentLine, prevLine, nextLine } = current + let { x1, x2, y1, y2 } = currentLine + let beforePrevLine, afterNextLine + + /** 이전 라인의 경사 */ + const prevDegree = prevLine.attributes.pitch > 0 ? getDegreeByChon(prevLine.attributes.pitch) : prevLine.attributes.degree + /** 다음 라인의 경사 */ + const currentDegree = currentLine.attributes.pitch > 0 ? getDegreeByChon(currentLine.attributes.pitch) : currentLine.attributes.degree + + //라인 확인용 + /*const checkCurrentLine = new fabric.Line([x1, y1, x2, y2], { + stroke: 'red', + strokeWidth: 4, + parentId: roof.id, + }) + canvas.add(checkCurrentLine) + canvas.renderAll()*/ + // console.log('currentLine : ', currentLine.attributes.planeSize) + + /** 이전, 다음라인의 사잇각의 vector를 구한다. */ + let prevVector = getHalfAngleVector(prevLine, currentLine) + let nextVector = getHalfAngleVector(currentLine, nextLine) + + let prevHipVector = { x: Big(prevVector.x), y: Big(prevVector.y) } + let nextHipVector = { x: Big(nextVector.x), y: Big(nextVector.y) } + + /** 각 라인의 흐름 방향을 확인한다. */ + const currentAngle = calculateAngle(currentLine.startPoint, currentLine.endPoint) + /*const beforePrevAngle = calculateAngle(beforePrevLine.startPoint, beforePrevLine.endPoint) + const afterNextAngle = calculateAngle(afterNextLine.startPoint, afterNextLine.endPoint)*/ + + /** 현재 라인의 길이 추녀마루 길이의 기준이 된다. */ + let hipLength = Big(x2) + .minus(Big(x1)) + .plus(Big(y2).minus(Big(y1))) + .abs() + + // console.log('currentLine : ', currentLine.attributes.planeSize) + + /** 이전 라인과의 사이 추녀마루의 각도를 확인한다, 각도가 지붕안쪽으로 향하지 않을때 반대로 처리한다.*/ + const prevCheckPoint = { + x: Big(x1).plus(Big(prevHipVector.x).times(10)), + y: Big(y1).plus(Big(prevHipVector.y).times(10)), + } + if (!checkWallPolygon.inPolygon(prevCheckPoint)) { + prevHipVector = { x: Big(prevHipVector.x).neg(), y: Big(prevHipVector.y).neg() } + } + + /** 다음 라인과의 사이 추녀마루의 각도를 확인한다, 각도가 지붕안쪽으로 향하지 않을때 반대로 처리한다.*/ + const nextCheckPoint = { + x: Big(x2).plus(Big(nextHipVector.x).times(10)), + y: Big(y2).plus(Big(nextHipVector.y).times(10)), + } + if (!checkWallPolygon.inPolygon(nextCheckPoint)) { + nextHipVector = { x: Big(nextHipVector.x).neg(), y: Big(nextHipVector.y).neg() } + } + + let prevHipLine, nextHipLine + /** 이전라인과의 연결지점에 추녀마루를 그린다. */ + // console.log('이전라인 : ', baseHipLines.filter((line) => line.x1 === x1 && line.y1 === y1).length === 0) + if (baseHipLines.filter((line) => line.x1 === x1 && line.y1 === y1).length === 0) { + let prevEndPoint = { + x: Big(x1).plus(Big(prevHipVector.x).times(hipLength)).round(2), + y: Big(y1).plus(Big(prevHipVector.y).times(hipLength)).round(2), + } + + const prevEndEdge = { vertex1: { x: x1, y: y1 }, vertex2: prevEndPoint } + + // console.log('prevHipVector : ', prevHipVector.x.toNumber(), prevHipVector.x.s, prevHipVector.y.toNumber(), prevHipVector.y.s) + + const intersectBaseLine = [] + baseLines + .filter((line) => line !== currentLine && line !== prevLine && line !== nextLine) + .filter((line) => { + if (currentAngle === 0 || currentAngle === 180) { + return Big(line.y1).minus(Big(y1)).s === nextHipVector.y.s || Big(line.y2).minus(Big(y1)).s === nextHipVector.y.s + } else { + return Big(line.x1).minus(Big(x1)).s === nextHipVector.x.s || Big(line.x2).minus(Big(x1)).s === nextHipVector.x.s + } + }) + .forEach((line) => { + // console.log('line : ', line.attributes.planeSize) + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(prevEndEdge, lineEdge) + + if (intersection && Big(intersection.x - x1).s === nextHipVector.x.s && Big(intersection.y - y1).s === nextHipVector.y.s) { + /* const circle = new fabric.Circle({ + left: intersection.x - 2, + top: intersection.y - 2, + radius: 4, + fill: 'red', + parentId: roof.id, + }) + canvas.add(circle) + canvas.renderAll()*/ + const size = Big(intersection.x - x1) + .abs() + .pow(2) + .plus( + Big(intersection.y - y1) + .pow(2) + .abs(), + ) + .sqrt() + if (size.gt(0)) { + intersectBaseLine.push({ + intersection, + size, + }) + } + } + }) + + const intersectBase = intersectBaseLine.reduce((prev, current) => { + return prev.size < current.size ? prev : current + }, intersectBaseLine[0]) + + if (intersectBase) { + prevEndPoint = { + x: Big(x1) + .plus(Big(nextHipVector.x).times(intersectBase.size.div(2))) + .round(2), + y: Big(y1) + .plus(Big(nextHipVector.y).times(intersectBase.size.div(2))) + .round(2), + } + } + + const intersectRidgeLine = [] + baseRidgeLines.forEach((line) => { + const intersection = edgesIntersection( + { vertex1: { x: x1, y: y1 }, vertex2: prevEndPoint }, + { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }, + ) + /*const checkLine = new fabric.Line([x1, y1, prevEndPoint.x, prevEndPoint.y], { + stroke: 'red', + strokeWidth: 2, + parentId: roof.id, + }) + canvas.add(checkLine) + canvas.renderAll()*/ + + /* if (intersection) { + const intersectCircle = new fabric.Circle({ + left: intersection.x - 2, + top: intersection.y - 2, + radius: 4, + fill: 'red', + parentId: roof.id, + }) + canvas.add(intersectCircle) + canvas.renderAll() + }*/ + if (intersection && !intersection.isIntersectionOutside) { + intersectRidgeLine.push({ + intersection, + distance: Big(intersection.x) + .minus(Big(x1)) + .abs() + .pow(2) + .plus(Big(intersection.y).minus(Big(y1)).pow(2).abs()) + .sqrt(), + }) + } + }) + + const intersectRidge = intersectRidgeLine.reduce((prev, current) => (prev.distance < current.distance ? prev : current), intersectRidgeLine[0]) + + if (intersectRidge) { + prevEndPoint.x = Big(intersectRidge.intersection.x).round(1) + prevEndPoint.y = Big(intersectRidge.intersection.y).round(1) + } + + const xVector = Math.sign(Big(prevEndPoint.x).minus(Big(x1)).neg().toNumber()) + const yVector = Math.sign(Big(prevEndPoint.y).minus(Big(y1)).neg().toNumber()) + /** 지붕 선까지의 곂침에 따른 길이를 파악하기 위한 scale*/ + let scale = Big(currentLine.attributes.offset).pow(2).plus(Big(prevLine.attributes.offset).pow(2)).sqrt() + scale = scale.eq(0) ? Big(1) : scale + + /** scale 만큼 추녀마루를 늘려서 겹치는 포인트를 확인한다. */ + const hipEdge = { + vertex1: { x: Big(x1).plus(scale.times(xVector)).toNumber(), y: Big(y1).plus(scale.times(yVector)).toNumber() }, + vertex2: prevEndPoint, + } + + /*const checkLine = new fabric.Line([hipEdge.vertex1.x, hipEdge.vertex1.y, hipEdge.vertex2.x, hipEdge.vertex2.y], { + stroke: 'yellow', + strokeWidth: 2, + parentId: roof.id, + }) + canvas.add(checkLine) + canvas.renderAll()*/ + + let intersectPoints = [] + /** 외벽선에서 추녀 마루가 겹치는 경우에 대한 확인*/ + roof.lines.forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(hipEdge, lineEdge) + if ( + intersection && + !intersection.isIntersectionOutside && + Math.sign(prevEndPoint.x - x1) === Math.sign(prevEndPoint.x - intersection.x) && + Math.sign(prevEndPoint.y - y1) === Math.sign(prevEndPoint.y - intersection.y) + ) { + /*const intersectPoint = new fabric.Circle({ + left: intersection.x - 2, + top: intersection.y - 2, + radius: 4, + fill: 'cyan', + parentId: roof.id, + }) + canvas.add(intersectPoint) + canvas.renderAll()*/ + + const intersectSize = prevEndPoint.x + .minus(Big(intersection.x)) + .abs() + .pow(2) + .plus(prevEndPoint.y.minus(Big(intersection.y)).pow(2).abs()) + .sqrt() + .toNumber() + + intersectPoints.push({ + intersection, + size: intersectSize, + }) + } + }) + + intersectPoints = intersectPoints.reduce((prev, current) => { + return prev.size < current.size ? prev : current + }, intersectPoints[0]) + + // console.log('intersectPoints', intersectPoints) + + /** 겹치는 외벽선이 있을때 추녀마루를 외벽선 까지 늘려서 수정*/ + if (intersectPoints && intersectPoints.intersection) { + prevHipLine = drawHipLine( + [intersectPoints.intersection.x, intersectPoints.intersection.y, prevEndPoint.x.toNumber(), prevEndPoint.y.toNumber()], + canvas, + roof, + textMode, + null, + prevDegree, + currentDegree, + ) + baseHipLines.push({ x1, y1, x2: prevEndPoint.x.toNumber(), y2: prevEndPoint.y.toNumber(), line: prevHipLine }) + } + } + /** 다음라인과의 연결지점에 추녀마루를 그린다. */ + // console.log('다음라인 : ', baseHipLines.filter((line) => line.x1 === x2 && line.y1 === y2).length === 0) + if (baseHipLines.filter((line) => line.x1 === x2 && line.y1 === y2).length === 0) { + let nextEndPoint = { + x: Big(x2).plus(Big(nextHipVector.x).times(hipLength)).round(2), + y: Big(y2).plus(Big(nextHipVector.y).times(hipLength)).round(2), + } + + /*const nextEndLine = new fabric.Line([x2, y2, nextEndPoint.x, nextEndPoint.y], { + stroke: 'red', + strokeWidth: 2, + parentId: roof.id, + }) + canvas.add(nextEndLine) + canvas.renderAll()*/ + + const nextEndEdge = { + vertex1: { x: x2, y: y2 }, + vertex2: nextEndPoint, + } + + const intersectBaseLine = [] + baseLines + .filter((line) => line !== currentLine && line !== prevLine && line !== nextLine) + .filter((line) => { + if (currentAngle === 0 || currentAngle === 180) { + return Big(line.y1).minus(Big(y1)).s === nextHipVector.y.s || Big(line.y2).minus(Big(y1)).s === nextHipVector.y.s + } else { + return Big(line.x1).minus(Big(x1)).s === nextHipVector.x.s || Big(line.x2).minus(Big(x1)).s === nextHipVector.x.s + } + }) + .forEach((line) => { + // console.log('line : ', line.attributes.planeSize) + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(nextEndEdge, lineEdge) + + if (intersection && Big(intersection.x - x2).s === nextHipVector.x.s && Big(intersection.y - y2).s === nextHipVector.y.s) { + /*console.log('next intersection ============') + const circle = new fabric.Circle({ + left: intersection.x - 2, + top: intersection.y - 2, + radius: 4, + fill: 'red', + parentId: roof.id, + }) + canvas.add(circle) + canvas.renderAll()*/ + const size = Big(intersection.x - x2) + .abs() + .pow(2) + .plus( + Big(intersection.y - y2) + .abs() + .pow(2), + ) + .sqrt() + if (size.gt(0)) { + intersectBaseLine.push({ + intersection, + size, + }) + } + } + }) + + const intersectBase = intersectBaseLine.reduce((prev, current) => { + return prev.size.lt(current.size) ? prev : current + }, intersectBaseLine[0]) + + if (intersectBase) { + nextEndPoint = { + x: Big(x2) + .plus(Big(nextHipVector.x).times(intersectBase.size.div(2))) + .round(2), + y: Big(y2) + .plus(Big(nextHipVector.y).times(intersectBase.size.div(2))) + .round(2), + } + } + + const intersectRidgeLine = [] + baseRidgeLines.forEach((line) => { + // console.log('nextEndPoint : ', nextEndPoint.x.toNumber(), nextEndPoint.y.toNumber()) + // console.log('intersection : ', 'x : ', line.x1, 'y : ', line.y1, 'x : ', line.x2, 'y : ', line.y2) + const intersection = edgesIntersection( + { vertex1: { x: x2, y: y2 }, vertex2: nextEndPoint }, + { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }, + ) + + if (intersection && !intersection.isIntersectionOutside) { + intersectRidgeLine.push({ + intersection, + distance: Big(intersection.x) + .minus(Big(x1)) + .pow(2) + .plus(Big(intersection.y).minus(Big(y1)).pow(2)) + .sqrt(), + }) + } + }) + const intersectRidge = intersectRidgeLine.reduce((prev, current) => (prev.distance < current.distance ? prev : current), intersectRidgeLine[0]) + // console.log('intersectRidge : ', intersectRidge) + + if (intersectRidge) { + nextEndPoint.x = Big(intersectRidge.intersection.x).round(1) + nextEndPoint.y = Big(intersectRidge.intersection.y).round(1) + } + + const xVector = Math.sign(Big(nextEndPoint.x).minus(Big(x2)).neg().toNumber()) + const yVector = Math.sign(Big(nextEndPoint.y).minus(Big(y2)).neg().toNumber()) + let scale = Big(currentLine.attributes.offset).pow(2).plus(Big(nextLine.attributes.offset).pow(2)).sqrt() + scale = scale.eq(0) ? Big(1) : scale + + const hipEdge = { + vertex1: { x: Big(x2).plus(scale.times(xVector)).toNumber(), y: Big(y2).plus(scale.times(yVector)).toNumber() }, + vertex2: nextEndPoint, + } + + /* const checkLine = new fabric.Line([hipEdge.vertex1.x, hipEdge.vertex1.y, hipEdge.vertex2.x, hipEdge.vertex2.y], { + stroke: 'yellow', + strokeWidth: 2, + parentId: roof.id, + }) + canvas.add(checkLine) + canvas.renderAll()*/ + + let intersectPoints = [] + roof.lines.forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(hipEdge, lineEdge) + // console.log('intersection', intersection) + if ( + intersection && + !intersection.isIntersectionOutside && + Math.sign(nextEndPoint.x - x2) === Math.sign(nextEndPoint.x - intersection.x) && + Math.sign(nextEndPoint.y - y2) === Math.sign(nextEndPoint.y - intersection.y) + ) { + /*const intersectPoint = new fabric.Circle({ + left: intersection.x - 2, + top: intersection.y - 2, + radius: 4, + fill: 'blue', + parentId: roof.id, + }) + canvas.add(intersectPoint) + canvas.renderAll()*/ + + // console.log('nextEndPoint', nextEndPoint.x.toNumber(), nextEndPoint.y.toNumber()) + // console.log('intersection', intersection.x, intersection.y) + + const intersectSize = nextEndPoint.x + .minus(Big(intersection.x)) + .pow(2) + .plus(nextEndPoint.y.minus(Big(intersection.y)).pow(2)) + .abs() + .sqrt() + .toNumber() + + intersectPoints.push({ + intersection, + size: intersectSize, + }) + } + }) + + intersectPoints = intersectPoints.reduce((prev, current) => { + return prev.size < current.size ? prev : current + }, intersectPoints[0]) + + // console.log('intersectPoints : ', intersectPoints) + + if (intersectPoints && intersectPoints.intersection) { + nextHipLine = drawHipLine( + [intersectPoints.intersection.x, intersectPoints.intersection.y, nextEndPoint.x.toNumber(), nextEndPoint.y.toNumber()], + canvas, + roof, + textMode, + null, + prevDegree, + currentDegree, + ) + baseHipLines.push({ x1: x2, y1: y2, x2: nextEndPoint.x.toNumber(), y2: nextEndPoint.y.toNumber(), line: nextHipLine }) + } + } + }) + + // console.log('baseHipLines : ', baseHipLines) + // console.log('baseRidgeLines : ', baseRidgeLines) + /** 지붕선에 맞닫지 않은 부분을 확인하여 처리 한다.*/ + /** 1. 그려진 마루 중 추녀마루가 부재하는 경우를 판단. 부재는 연결된 라인이 홀수인 경우로 판단한다.*/ + let unFinishedRidge = [] + baseRidgeLines.forEach((current) => { + /*const checkLine = new fabric.Line([current.x1, current.y1, current.x2, current.y2], { + stroke: 'red', + strokeWidth: 2, + parentId: roof.id, + }) + canvas.add(checkLine) + canvas.renderAll()*/ + + let checkPoint = [ + { x: current.x1, y: current.y1, line: current, cnt: 0 }, + { x: current.x2, y: current.y2, line: current, cnt: 0 }, + ] + baseHipLines.forEach((line) => { + /*const checkPoint1 = new fabric.Circle({ + left: line.x1, + top: line.y1, + radius: 4, + fill: 'red', + parentId: roof.id, + }) + const checkPoint2 = new fabric.Circle({ + left: line.x2, + top: line.y2, + radius: 4, + fill: 'blue', + }) + canvas.add(checkPoint1) + canvas.add(checkPoint2) + canvas.renderAll()*/ + // console.log('current : ', current.x1, current.y1, current.x2, current.y2) + // console.log('line : ', line.x1, line.y1, line.x2, line.y2) + + if ((line.x1 === current.x1 && line.y1 === current.y1) || (line.x2 === current.x1 && line.y2 === current.y1)) { + checkPoint[0].cnt = checkPoint[0].cnt + 1 + } + if ((line.x1 === current.x2 && line.y1 === current.y2) || (line.x2 === current.x2 && line.y2 === current.y2)) { + checkPoint[1].cnt = checkPoint[1].cnt + 1 + } + }) + // console.log('checkPoint : ', checkPoint) + if (checkPoint[0].cnt === 0 || checkPoint[0].cnt % 2 !== 0) { + unFinishedRidge.push(checkPoint[0]) + } + if (checkPoint[1].cnt === 0 || checkPoint[1].cnt % 2 !== 0) { + unFinishedRidge.push(checkPoint[1]) + } + }) + // console.log('unFinishedRidge : ', unFinishedRidge) + //포인트 확인 + /*unFinishedRidge.forEach((current) => { + const checkCircle = new fabric.Circle({ + left: current.x, + top: current.y, + radius: 4, + fill: 'green', + parentId: roof.id, + }) + canvas.add(checkCircle) + canvas.renderAll() + })*/ + + /** 2. 그려진 추녀마루 중 완성되지 않은 것을 찾는다. 완성되지 않았다는 것은 연결된 포인트가 홀수인 경우로 판단한다.*/ + const findUnFinishedPoints = (baseHipLines) => { + let unFinishedPoint = [] + baseHipLines.forEach((current) => { + let checkPoint = [ + { x: current.x1, y: current.y1, checked: true, line: current.line }, + { x: current.x2, y: current.y2, checked: true, line: current.line }, + ] + baseLines.forEach((line) => { + if ((line.x1 === current.x1 && line.y1 === current.y1) || (line.x2 === current.x1 && line.y2 === current.y1)) { + checkPoint[0].checked = false + } + if ((line.x1 === current.x2 && line.y1 === current.y2) || (line.x2 === current.x2 && line.y2 === current.y2)) { + checkPoint[1].checked = false + } + }) + + const samePoints = [] + checkPoint + .filter((point) => point.checked) + .forEach((point) => { + baseHipLines.forEach((line) => { + if (line.x1 === point.x && line.y1 === point.y) { + samePoints.push({ x: point.x, y: point.y, line: line }) + } + if (line.x2 === point.x && line.y2 === point.y) { + samePoints.push({ x: point.x, y: point.y, line: line }) + } + }) + }) + if (samePoints.length > 0 && samePoints.length % 2 !== 0) { + unFinishedPoint.push(samePoints[0]) + } + }) + return unFinishedPoint + } + + let unFinishedPoint = findUnFinishedPoints(baseHipLines) + // console.log('unFinishedPoint : ', unFinishedPoint) + + /**3. 라인이 부재인 마루의 모자란 라인을 찾는다. 라인은 그려진 추녀마루 중에 완성되지 않은 추녀마루와 확인한다.*/ + /**3-1 라인을 그릴때 각도가 필요하기 때문에 각도를 구한다. 각도는 전체 지부의 각도가 같다면 하나로 처리 */ + let degreeAllLine = [] + baseLines.forEach((line) => { + const pitch = line.attributes.pitch + const degree = line.attributes.degree + // console.log('pitch : ', pitch, 'degree : ', degree) + degreeAllLine.push(pitch > 0 ? getDegreeByChon(pitch) : degree) + }) + + let currentDegree, prevDegree + degreeAllLine = [...new Set(degreeAllLine)] + // console.log('degreeAllLine : ', degreeAllLine) + currentDegree = degreeAllLine[0] + if (degreeAllLine.length === 1) { + prevDegree = currentDegree + } + + /*unFinishedPoint.forEach((current) => { + const circle = new fabric.Circle({ + left: current.x, + top: current.y, + radius: 4, + fill: 'red', + parentId: roof.id, + }) + canvas.add(circle) + canvas.renderAll() + })*/ + + /** 라인이 부재한 마루에 라인을 찾아 그린다.*/ + unFinishedRidge.forEach((current) => { + /*const checkLine = new fabric.Line([current.line.x1, current.line.y1, current.line.x2, current.line.y2], { + stroke: 'red', + strokeWidth: 2, + parentId: roof.id, + }) + canvas.add(checkLine) + canvas.renderAll()*/ + + /*const checkCircle = new fabric.Circle({ + left: current.x, + top: current.y, + radius: 4, + fill: 'green', + parentId: roof.id, + }) + canvas.add(checkCircle) + canvas.renderAll()*/ + + let checkPoints = [] + + unFinishedPoint + .filter( + (point) => + point.x !== current.x && + point.y !== current.y && + Big(point.x) + .minus(Big(current.x)) + .abs() + .minus(Big(point.y).minus(Big(current.y)).abs()) + .abs() + .lt(1), + ) + .forEach((point) => { + /*console.log('point : ', point.x, point.y) + const circle = new fabric.Circle({ + left: point.x, + top: point.y, + radius: 4, + fill: 'red', + parentId: roof.id, + }) + canvas.add(circle) + canvas.renderAll() + + const checkLine = new fabric.Line([current.x, current.y, point.x, point.y], { + stroke: 'green', + strokeWidth: 2, + parentId: roof.id, + }) + canvas.add(checkLine) + canvas.renderAll()*/ + + const pointEdge = { vertex1: { x: point.x, y: point.y }, vertex2: { x: current.x, y: current.y } } + let isIntersection = false + baseLines.forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(pointEdge, lineEdge) + if (intersection && !intersection.isIntersectionOutside) { + isIntersection = true + } + }) + if (!isIntersection) { + checkPoints.push({ + point, + size: Big(point.x) + .minus(Big(current.x)) + .abs() + .pow(2) + .plus(Big(point.y).minus(Big(current.y)).abs().pow(2)) + .sqrt(), + }) + } + }) + // console.log('checkPoints : ', checkPoints) + // console.log('current ridge: ', current) + /*const checkCurrent = new fabric.Circle({ + left: current.x, + top: current.y, + radius: 4, + fill: 'blue', + parentId: roof.id, + }) + canvas.add(checkCurrent) + canvas.renderAll()*/ + if (checkPoints.length > 0) { + /*checkPoints.forEach((point) => { + const checkPoints = new fabric.Circle({ + left: point.point.x, + top: point.point.y, + radius: 4, + fill: 'red', + parentId: roof.id, + }) + canvas.add(checkPoints) + canvas.renderAll() + })*/ + + checkPoints.sort((a, b) => a.size - b.size) + // console.log('Big(2).minus(Big(current.cnt) : ', Big(2).minus(Big(current.cnt)).toNumber(), current.cnt) + const maxCnt = Big(2).minus(Big(current.cnt)).toNumber() + for (let i = 0; i < maxCnt; i++) { + const checkPoint = checkPoints[i] + // console.log('current : ', current.line.attributes.planeSize) + // console.log('1. checkPoint : ', checkPoint) + if (checkPoint) { + let point = [checkPoint.point.x, checkPoint.point.y, current.x, current.y] + let hipBasePoint + + baseHipLines.forEach((line) => { + const checkAngel1 = calculateAngle({ x: point[0], y: point[1] }, { x: point[2], y: point[3] }) + const checkAngel2 = calculateAngle({ x: line.line.x1, y: line.line.y1 }, { x: line.line.x2, y: line.line.y2 }) + + const isConnectLine = + ((line.line.x1 === point[0] && line.line.y1 === point[1]) || (line.line.x2 === point[0] && line.line.y2 === point[1])) && + checkAngel1 === checkAngel2 + const isOverlap = segmentsOverlap(line.line, { x1: point[0], y1: point[1], x2: point[2], y2: point[3] }) + const isSameLine = + (point[0] === line.x2 && point[1] === line.y2 && point[2] === line.x1 && point[3] === line.y1) || + (point[0] === line.x1 && point[1] === line.y1 && point[2] === line.x2 && point[3] === line.y2) + + if (isConnectLine || isOverlap || isSameLine) { + /** 겹치는 추녀마루와 하나의 선으로 변경*/ + const mergePoint = [ + { x: point[0], y: point[1] }, + { x: point[2], y: point[3] }, + { x: line.line.x1, y: line.line.y1 }, + { x: line.line.x2, y: line.line.y2 }, + ] + /** baseHipLines도 조정*/ + /*const mergeBasePoint = [ + { x: point[0], y: point[1] }, + { x: point[2], y: point[3] }, + { x: line.x1, y: line.y1 }, + { x: line.x2, y: line.y2 }, + ]*/ + mergePoint.sort((a, b) => a.x - b.x) + // mergeBasePoint.sort((a, b) => a.x - b.x) + /*const checkLine = new fabric.Line([mergeBasePoint[0].x, mergeBasePoint[0].y, mergeBasePoint[3].x, mergeBasePoint[3].y], { + stroke: 'red', + strokeWidth: 4, + parentId: roof.id, + }) + canvas.add(checkLine) + canvas.renderAll()*/ + + hipBasePoint = { x1: line.x1, y1: line.y1, x2: line.x2, y2: line.y2 } + // line.x1 = mergeBasePoint[0].x + // line.y1 = mergeBasePoint[0].y + // line.x2 = mergeBasePoint[3].x + // line.y2 = mergeBasePoint[3].y + point = [mergePoint[0].x, mergePoint[0].y, mergePoint[3].x, mergePoint[3].y] + canvas.remove(line.line) + baseHipLines = baseHipLines.filter((baseLine) => baseLine.line !== line.line) + } + }) + + const hipLine = drawHipLine(point, canvas, roof, textMode, null, prevDegree, currentDegree) + if (hipBasePoint) { + baseHipLines.push({ x1: hipBasePoint.x1, y1: hipBasePoint.y1, x2: point[2], y2: point[3], line: hipLine }) + } else { + baseHipLines.push({ x1: point[0], y1: point[1], x2: point[2], y2: point[3], line: hipLine }) + } + current.cnt = current.cnt + 1 + } + } + } + + /** 라인이 다 그려지지 않은 경우 */ + if (current.cnt % 2 !== 0) { + // console.log('no checkPoints :', current) + + let basePoints = baseLinePoints + .filter((point) => + Big(point.x) + .minus(Big(current.x)) + .abs() + .minus(Big(point.y).minus(Big(current.y)).abs()) + .abs() + .lt(1), + ) + .filter((point) => { + const pointEdge = { vertex1: { x: current.x, y: current.y }, vertex2: { x: point.x, y: point.y } } + + const intersectPoints = [] + baseLines.forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(pointEdge, lineEdge) + if (intersection && !intersection.isIntersectionOutside) { + intersectPoints.push(intersection) + } + }) + return ( + !intersectPoints.filter( + (intersect) => !(Big(intersect.x).minus(Big(point.x)).abs().lt(1) && Big(intersect.y).minus(Big(point.y)).abs().lt(1)), + ).length > 0 + ) + }) + + /** hip을 그리기 위한 기본 길이*/ + let hipSize = 0 + if (basePoints.length > 0) { + basePoints.sort((a, b) => { + const aSize = Big(a.x) + .minus(Big(current.x)) + .abs() + .pow(2) + .plus(Big(a.y).minus(Big(current.y)).abs().pow(2)) + .sqrt() + const bSize = Big(b.x) + .minus(Big(current.x)) + .abs() + .pow(2) + .plus(Big(b.y).minus(Big(current.y)).abs().pow(2)) + .sqrt() + return aSize - bSize + }) + const baseHips = baseHipLines.filter((line) => line.x1 === basePoints[0].x && line.y1 === basePoints[0].y) + if (baseHips.length > 0) { + hipSize = Big(baseHips[0].line.attributes.planeSize).div(10) + } + } + + if (hipSize.eq(0)) { + const ridge = current.line + basePoints = baseHipLines.filter((line) => (line.x2 === ridge.x1 && line.y2 === ridge.y1) || (line.x2 === ridge.x2 && line.y2 === ridge.y2)) + basePoints.sort((a, b) => a.line.attributes.planeSize - b.line.attributes.planeSize) + hipSize = Big(basePoints[0].line.attributes.planeSize).div(10) + } + + hipSize = hipSize.times(hipSize).div(2).sqrt().round().toNumber() + + /** 현재 라인을 기준으로 45, 135, 225, 315 방향을 확인하기 위한 좌표, hip은 45도 방향으로만 그린다. */ + const checkEdge45 = { vertex1: { x: current.x, y: current.y }, vertex2: { x: current.x + hipSize, y: current.y - hipSize } } + const checkEdge135 = { vertex1: { x: current.x, y: current.y }, vertex2: { x: current.x + hipSize, y: current.y + hipSize } } + const checkEdge225 = { vertex1: { x: current.x, y: current.y }, vertex2: { x: current.x - hipSize, y: current.y + hipSize } } + const checkEdge315 = { vertex1: { x: current.x, y: current.y }, vertex2: { x: current.x - hipSize, y: current.y - hipSize } } + + let intersectPoints = [] + let notIntersect45 = true, + notIntersect135 = true, + notIntersect225 = true, + notIntersect315 = true + baseLines.forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection45 = edgesIntersection(checkEdge45, lineEdge) + const intersection135 = edgesIntersection(checkEdge135, lineEdge) + const intersection225 = edgesIntersection(checkEdge225, lineEdge) + const intersection315 = edgesIntersection(checkEdge315, lineEdge) + + if (intersection45 && !intersection45.isIntersectionOutside) { + intersectPoints.push(intersection45) + notIntersect45 = false + } + if (intersection135 && !intersection135.isIntersectionOutside) { + intersectPoints.push(intersection135) + notIntersect135 = false + } + if (intersection225 && !intersection225.isIntersectionOutside) { + intersectPoints.push(intersection225) + notIntersect225 = false + } + if (intersection315 && !intersection315.isIntersectionOutside) { + intersectPoints.push(intersection315) + notIntersect315 = false + } + }) + /** baseLine의 각 좌표와 교차하는 경우로 한정*/ + intersectPoints = intersectPoints.filter((is) => baseLinePoints.filter((point) => point.x === is.x && point.y === is.y).length > 0) + /** baseLine과 교차하는 좌표의 경우 이미 그려진 추녀마루가 존재한다면 제외한다. */ + intersectPoints = intersectPoints.filter((point) => baseHipLines.filter((hip) => hip.x1 === point.x && hip.y1 === point.y).length === 0) + /** baseLine과 교차하지 않는 포인트를 추가한다.*/ + if (notIntersect45) { + intersectPoints.push(checkEdge45.vertex2) + } + if (notIntersect135) { + intersectPoints.push(checkEdge135.vertex2) + } + if (notIntersect225) { + intersectPoints.push(checkEdge225.vertex2) + } + if (notIntersect315) { + intersectPoints.push(checkEdge315.vertex2) + } + intersectPoints.forEach((is) => { + const points = [current.x, current.y, is.x, is.y] + const hipLine = drawHipLine(points, canvas, roof, textMode, null, prevDegree, currentDegree) + baseHipLines.push({ x1: points[0], y1: points[1], x2: points[2], y2: points[3], line: hipLine }) + current.cnt = current.cnt + 1 + }) + } + }) + + /** hip이 짝수개가 맞다아있는데 마루와 연결되지 않는 포인트를 찾는다. 그려지지 않은 마루를 찾기 위함.*/ + let noRidgeHipPoints = baseHipLines + .filter((current) => { + const lines = baseHipLines + .filter((line) => line !== current) + .filter((line) => (line.x1 === current.x2 && line.y1 === current.y2) || (line.x2 === current.x2 && line.y2 === current.y2)) + + return lines.length !== 0 && lines.length % 2 !== 0 + }) + .filter( + (current) => + baseRidgeLines.filter((line) => (line.x1 === current.x2 && line.y1 === current.y2) || (line.x2 === current.x2 && line.y2 === current.y2)) + .length === 0, + ) + + noRidgeHipPoints.forEach((current) => { + const orthogonalPoints = noRidgeHipPoints.filter( + (point) => point !== current && ((current.x2 === point.x2 && current.y2 !== point.y2) || (current.x2 !== point.x2 && current.y2 === point.y2)), + ) + + /** 직교 하는 포인트가 존재 할때 마루를 그린다. */ + if (orthogonalPoints.length > 0) { + const points = [current.x2, current.y2, orthogonalPoints[0].x2, orthogonalPoints[0].y2] + const ridgeLine = drawRidgeLine(points, canvas, roof, textMode) + baseRidgeLines.push(ridgeLine) + /** array에서 object 삭제*/ + noRidgeHipPoints = noRidgeHipPoints.filter((point) => point !== current && point !== orthogonalPoints[0]) + } + }) + + /** 직교 하는 포인트가 없는 경우 남은 포인트 처리 */ + const checkEdgeLines = [] + noRidgeHipPoints.forEach((current) => { + /*const checkCircle = new fabric.Circle({ + left: current.x2, + top: current.y2, + radius: 4, + fill: 'red', + parentId: roof.id, + }) + canvas.add(checkCircle) + canvas.renderAll()*/ + noRidgeHipPoints.forEach((current) => { + noRidgeHipPoints + .filter((point) => point !== current) + .forEach((point) => { + checkEdgeLines.push( + { vertex1: { x: current.x2, y: current.y2 }, vertex2: { x: current.x2, y: point.y2 } }, + { vertex1: { x: current.x2, y: point.y2 }, vertex2: { x: point.x2, y: point.y2 } }, + { vertex1: { x: point.x2, y: point.y2 }, vertex2: { x: point.x2, y: current.y2 } }, + { vertex1: { x: point.x2, y: current.y2 }, vertex2: { x: current.x2, y: current.y2 } }, + ) + }) + }) + }) + // 라인 확인용 + /*checkEdgeLines.forEach((edge) => { + const checkLine = new fabric.Line([edge.vertex1.x, edge.vertex1.y, edge.vertex2.x, edge.vertex2.y], { + stroke: 'yellow', + strokeWidth: 2, + parentId: roof.id, + }) + canvas.add(checkLine) + canvas.renderAll() + })*/ + + /** 연결되지 않은 포인트를 찾아서 해당 포인트를 가지고 있는 라인을 찾는다. */ + let unFinishedPoints = [] + let unFinishedLines = [] + let intersectPoints = [] + baseHipLines.forEach((line) => { + if (baseLinePoints.filter((point) => point.x === line.x1 && point.y === line.y1).length === 0) { + unFinishedPoints.push({ x: line.x1, y: line.y1 }) + } + if (baseLinePoints.filter((point) => point.x === line.x2 && point.y === line.y2).length === 0) { + unFinishedPoints.push({ x: line.x2, y: line.y2 }) + } + }) + unFinishedPoints + .filter((point) => unFinishedPoints.filter((p) => p !== point && p.x === point.x && p.y === point.y).length === 0) + .forEach((point) => { + baseHipLines + .filter((line) => (line.x1 === point.x && line.y1 === point.y) || (line.x2 === point.x && line.y2 === point.y)) + .forEach((line) => unFinishedLines.push(line)) + }) + unFinishedLines.forEach((line) => { + /*const checkLine = new fabric.Line([line.x1, line.y1, line.x2, line.y2], { + stroke: 'red', + strokeWidth: 2, + parentId: roof.id, + }) + canvas.add(checkLine) + canvas.renderAll()*/ + + const xVector = Math.sign(Big(line.x2).minus(Big(line.x1))) + const yVector = Math.sign(Big(line.y2).minus(Big(line.y1))) + let lineIntersectPoints = [] + checkEdgeLines.forEach((edge) => { + const intersectEdge = edgesIntersection(edge, { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }) + const intersection = [] + if (intersectEdge) { + const isXVector = Math.sign(Big(intersectEdge.x).minus(Big(line.x1))) === xVector + const isYVector = Math.sign(Big(intersectEdge.y).minus(Big(line.y1))) === yVector + if (isXVector && isYVector) { + lineIntersectPoints.push({ + intersection: intersectEdge, + size: Big(intersectEdge.x) + .minus(Big(line.x1)) + .abs() + .pow(2) + .plus(Big(intersectEdge.y).minus(Big(line.y1)).abs().pow(2)) + .sqrt() + .round(1) + .toNumber(), + }) + } + } + }) + + lineIntersectPoints = lineIntersectPoints.filter( + (point, index, self) => index === self.findIndex((p) => p.intersection.x === point.intersection.x && p.intersection.y === point.intersection.y), + ) + lineIntersectPoints.sort((a, b) => a.size - b.size) + intersectPoints.push({ intersection: lineIntersectPoints[0].intersection, line }) + }) + + intersectPoints.forEach((intersection) => { + const intersectPoint = intersection.intersection + noRidgeHipPoints.forEach((hipPoint) => { + const angle = calculateAngle({ x: intersectPoint.x, y: intersectPoint.y }, { x: hipPoint.x2, y: hipPoint.y2 }) + if (angle === 0 || angle === 180 || angle === 90 || angle === -90) { + const ridgeLine = drawRidgeLine([intersectPoint.x, intersectPoint.y, hipPoint.x2, hipPoint.y2], canvas, roof, textMode) + baseRidgeLines.push(ridgeLine) + let baseHipLine = baseHipLines.filter((line) => line === intersection.line)[0] + baseHipLine.x2 = intersectPoint.x + baseHipLines.y2 = intersectPoint.y + intersection.line.x2 = intersectPoint.x + intersection.line.y2 = intersectPoint.y + /** 보조선 라인 조정*/ + const hipLine = intersection.line.line + /** 평면길이 */ + const planeSize = calcLinePlaneSize({ + x1: hipLine.x1, + y1: hipLine.y1, + x2: intersectPoint.x, + y2: intersectPoint.y, + }) + /** 실제길이 */ + const actualSize = + prevDegree === currentDegree + ? calcLineActualSize( + { + x1: hipLine.x1, + y1: hipLine.y1, + x2: intersectPoint.x, + y2: intersectPoint.y, + }, + currentDegree, + ) + : 0 + hipLine.set({ x2: intersectPoint.x, y2: intersectPoint.y, attributes: { roofId: roof.id, planeSize, actualSize } }) + hipLine.fire('modified') + intersectPoints = intersectPoints.filter((isp) => isp !== intersection) + noRidgeHipPoints = noRidgeHipPoints.filter((hip) => hip !== hipPoint) + } + }) + }) + + console.log('baseRidgeLines :', baseRidgeLines) + + const ridgeAllPoints = [] + baseRidgeLines.forEach((line) => ridgeAllPoints.push({ x: line.x1, y: line.y1 }, { x: line.x2, y: line.y2 })) + + console.log('ridgeAllPoints :', ridgeAllPoints) + + console.log('지붕 마루 갯수 확인 : ', baseRidgeLines.length, getMaxRidge(baseLines.length)) + + ridgeAllPoints.forEach((current) => { + console.log('current :', current) + + const ridgeLines = [], + hipLines = [] + + ridgeAllPoints + .filter((point) => point !== current) + .forEach((point) => { + /*canvas + .getObjects() + .filter((obj) => obj.name === 'checkPoint' || obj.name === 'checkLine') + .forEach((obj) => canvas.remove(obj)) + canvas.renderAll() + const checkPoint = new fabric.Circle({ + left: current.x, + top: current.y, + radius: 4, + fill: 'blue', + parentId: roof.id, + name: 'checkPoint', + }) + canvas.add(checkPoint) + canvas.renderAll()*/ + let checkRidgeLine, checkHipLine + /** 직선인 경우 마루 확인*/ + if ( + baseRidgeLines.length < getMaxRidge(baseLines.length) && + ((point.x === current.x && point.y !== current.y) || (point.x !== current.x && point.y === current.y)) + ) { + checkRidgeLine = { x1: current.x, y1: current.y, x2: point.x, y2: point.y } + } + /** 대각선인 경우 hip확인*/ + if ( + Big(point.x) + .minus(Big(current.x)) + .abs() + .eq(Big(point.y).minus(Big(current.y)).abs()) + ) { + checkHipLine = { x1: current.x, y1: current.y, x2: point.x, y2: point.y } + } + + if (checkRidgeLine) { + const ridgePoints = [checkRidgeLine.x1, checkRidgeLine.y1, checkRidgeLine.x2, checkRidgeLine.y2] + + let baseIntersection = false + const ridgeInterSection = [] + const ridgeEdge = { vertex1: { x: ridgePoints[0], y: ridgePoints[1] }, vertex2: { x: ridgePoints[2], y: ridgePoints[3] } } + baseLines.forEach((line) => { + const intersection = edgesIntersection(ridgeEdge, { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }) + if (intersection && !intersection.isIntersectionOutside) { + ridgeInterSection.push(intersection) + } + }) + baseRidgeLines.forEach((line) => { + const intersection = edgesIntersection(ridgeEdge, { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }) + if (intersection && !intersection.isIntersectionOutside) { + ridgeInterSection.push(intersection) + } + }) + baseHipLines.forEach((line) => { + const intersection = edgesIntersection(ridgeEdge, { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }) + if (intersection && !intersection.isIntersectionOutside) { + ridgeInterSection.push(intersection) + } + }) + const otherRidgeInterSection = ridgeInterSection.filter( + (intersection) => + !( + (intersection.x === ridgePoints[0] && intersection.y === ridgePoints[1]) || + (intersection.x === ridgePoints[2] && intersection.y === ridgePoints[3]) + ), + ) + const alreadyRidges = baseRidgeLines.filter( + (line) => + (line.x1 === ridgePoints[0] && line.y1 === ridgePoints[1] && line.x2 === ridgePoints[2] && line.y2 === ridgePoints[3]) || + (line.x1 === ridgePoints[2] && line.y1 === ridgePoints[3] && line.x2 === ridgePoints[0] && line.y2 === ridgePoints[1]), + ) + + if (!baseIntersection && alreadyRidges.length === 0 && otherRidgeInterSection.length === 0) { + const ridgeLine = drawRidgeLine(ridgePoints, canvas, roof, textMode) + baseRidgeLines.push(ridgeLine) + } + + // checkLine1 = { x1: checkRidgeLine.x1, y1: checkRidgeLine.y1, x2: checkRidgeLine.x2, y2: checkRidgeLine.y2 } + // checkLine2 = { x1: checkRidgeLine.x2, y1: checkRidgeLine.y2, x2: checkRidgeLine.x1, y2: checkRidgeLine.y1 } + } + if (checkHipLine) { + const hipPoints = [checkHipLine.x1, checkHipLine.y1, checkHipLine.x2, checkHipLine.y2] + + let baseIntersection = false + let hipInterSection = [] + const hipEdge = { vertex1: { x: hipPoints[0], y: hipPoints[1] }, vertex2: { x: hipPoints[2], y: hipPoints[3] } } + baseLines.forEach((line) => { + const intersection = edgesIntersection(hipEdge, { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }) + if (intersection && !intersection.isIntersectionOutside) { + baseIntersection = true + } + }) + baseRidgeLines.forEach((line) => { + const intersection = edgesIntersection(hipEdge, { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }) + if (intersection && !intersection.isIntersectionOutside) { + hipInterSection.push(intersection) + } + }) + baseHipLines.forEach((line) => { + const intersection = edgesIntersection(hipEdge, { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }) + if (intersection && !intersection.isIntersectionOutside) { + hipInterSection.push(intersection) + } + }) + const otherHipInterSection = hipInterSection.filter( + (intersection) => + !( + (intersection.x === hipPoints[0] && intersection.y === hipPoints[1]) || + (intersection.x === hipPoints[2] && intersection.y === hipPoints[3]) + ), + ) + const alreadyHips = baseHipLines.filter( + (line) => + (line.x1 === hipPoints[0] && line.y1 === hipPoints[1] && line.x2 === hipPoints[2] && line.y2 === hipPoints[3]) || + (line.x1 === hipPoints[2] && line.y1 === hipPoints[3] && line.x2 === hipPoints[0] && line.y2 === hipPoints[1]), + ) + + if (!baseIntersection && alreadyHips.length === 0 && otherHipInterSection.length === 0) { + const hipLine = drawHipLine(hipPoints, canvas, roof, textMode, null, prevDegree, currentDegree) + baseHipLines.push({ x1: hipPoints[0], y1: hipPoints[1], x2: hipPoints[2], y2: hipPoints[3], line: hipLine }) + } + } + }) + }) + + /** 연결 되지 않은 마루 갯수*/ + /*const missingCount = Big(baseRidgeLines.length) + .minus(connectRidgePoint.length + 1) + .toNumber() + + console.log('missingCount :', missingCount)*/ + /*baseRidgeLines.forEach((current) => { + baseRidgeLines + .filter((ridge) => ridge !== current) + .forEach((ridge) => { + /!** 직선인 경우 확인 *!/ + let checkLineEdge + if (current.x1 === ridge.x1 || current.y1 === ridge.y1) { + checkLineEdge = { vertex1: { x: current.x1, y: current.y1 }, vertex2: { x: ridge.x1, y: ridge.y1 } } + } else if (current.x2 === ridge.x1 || current.y2 === ridge.y1) { + checkLineEdge = { vertex1: { x: current.x2, y: current.y2 }, vertex2: { x: ridge.x1, y: ridge.y1 } } + } else if (current.x1 === ridge.x2 || current.y1 === ridge.y2) { + checkLineEdge = { vertex1: { x: current.x1, y: current.y1 }, vertex2: { x: ridge.x2, y: ridge.y2 } } + } else if (current.x2 === ridge.x2 || current.y2 === ridge.y2) { + checkLineEdge = { vertex1: { x: current.x2, y: current.y2 }, vertex2: { x: ridge.x2, y: ridge.y2 } } + } + + console.log('checkLineEdge :', checkLineEdge) + if (checkLineEdge) { + let hasIntersectLine = false + /!** 외벽선을 통과하는지 확인*!/ + baseLines.forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(checkLineEdge, lineEdge) + if (intersection && !intersection.isIntersectionOutside) { + hasIntersectLine = true + } + }) + + /!** hipLines를 통과하는지 확인*!/ + baseHipLines.forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(checkLineEdge, lineEdge) + if (intersection && !intersection.isIntersectionOutside) { + hasIntersectLine = true + } + }) + + // if (!hasIntersectLine) { + const checkLine = new fabric.Line([checkLineEdge.vertex1.x, checkLineEdge.vertex1.y, checkLineEdge.vertex2.x, checkLineEdge.vertex2.y], { + stroke: 'red', + strokeWidth: 2, + parentId: roof.id, + }) + canvas.add(checkLine) + canvas.renderAll() + // } + } + }) + })*/ + + /*drawRidge(roof, canvas, textMode) drawHips(roof, canvas, textMode) connectLinePoint(roof, canvas, textMode) modifyRidge(roof, canvas, textMode) - drawCenterLine(roof, canvas, textMode) + drawCenterLine(roof, canvas, textMode)*/ +} + +/** + * 추녀 마루를 그린다. + * @param points + * @param canvas + * @param roof + * @param textMode + * @param currentRoof + * @param prevDegree + * @param currentDegree + */ +const drawHipLine = (points, canvas, roof, textMode, currentRoof, prevDegree, currentDegree) => { + // console.log('drawHipLine : ', points) + const hip = new QLine(points, { + parentId: roof.id, + fontSize: roof.fontSize, + stroke: '#1083E3', + strokeWidth: 2, + name: LINE_TYPE.SUBLINE.HIP, + textMode: textMode, + attributes: { + roofId: roof.id, + // currentRoofId: currentRoof.id, + planeSize: calcLinePlaneSize({ + x1: points[0], + y1: points[1], + x2: points[2], + y2: points[3], + }), + actualSize: + prevDegree === currentDegree + ? calcLineActualSize( + { + x1: points[0], + y1: points[1], + x2: points[2], + y2: points[3], + }, + currentDegree, + ) + : 0, + }, + }) + + canvas.add(hip) + canvas.renderAll() + return hip +} + +/** + * 마루를 그린다. + * @param points + * @param canvas + * @param roof + * @param textMode + * @returns {*} + */ +const drawRidgeLine = (points, canvas, roof, textMode) => { + const ridge = new QLine(points, { + parentId: roof.id, + fontSize: roof.fontSize, + stroke: '#1083E3', + strokeWidth: 2, + name: LINE_TYPE.SUBLINE.RIDGE, + textMode: textMode, + attributes: { + roofId: roof.id, + planeSize: calcLinePlaneSize({ + x1: points[0], + y1: points[1], + x2: points[2], + y2: points[3], + }), + actualSize: calcLinePlaneSize({ + x1: points[0], + y1: points[1], + x2: points[2], + y2: points[3], + }), + }, + }) + canvas.add(ridge) + canvas.renderAll() + + return ridge +} + +/** + * 벡터를 정규화(Normalization)하는 함수 + * @param v + * @returns {{x: *, y: *}|{x: number, y: number}} + */ +const normalizeVector = (v) => { + /** 벡터의 크기(길이)*/ + const magnitude = Big(v.x).pow(2).plus(Big(v.y).pow(2)).sqrt() + if (magnitude.eq(0)) return { x: 0, y: 0 } // 크기가 0일 경우 (예외 처리) + return { x: Big(v.x).div(magnitude).toNumber(), y: Big(v.y).div(magnitude).toNumber() } +} + +/** + * 사잇각의 절반 방향 벡터 계산 함수 + * @param line1 + * @param line2 + * @returns {{x: *, y: *}|{x: number, y: number}} + */ +const getHalfAngleVector = (line1, line2) => { + const v1 = { x: Big(line1.x2).minus(Big(line1.x1)).toNumber(), y: Big(line1.y1).minus(Big(line1.y2)).toNumber() } + const v2 = { x: Big(line2.x2).minus(Big(line2.x1)).toNumber(), y: Big(line2.y1).minus(Big(line2.y2)).toNumber() } + + /** + * 벡터 정규화 + * @type {{x: *, y: *}|{x: number, y: number}} + */ + const unitV1 = normalizeVector(v1) // 첫 번째 벡터를 정규화 + const unitV2 = normalizeVector(v2) // 두 번째 벡터를 정규화 + + /** + * 두 벡터를 더합니다 + * @type {{x: *, y: *}} + */ + const summedVector = { x: Big(unitV1.x).plus(Big(unitV2.x)).toNumber(), y: Big(unitV1.y).plus(Big(unitV2.y)).toNumber() } + + /** 결과 벡터를 정규화하여 사잇각 벡터를 반환합니다 */ + return normalizeVector(summedVector) } /** @@ -757,7 +3082,7 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { * @param canvas * @param textMode */ -const drawRidge = (roof, canvas, textMode) => { +const drawRidges = (roof, canvas, textMode) => { console.log('roof.lines : ', roof.lines) const wallLines = canvas?.getObjects().find((object) => object.name === POLYGON_TYPE.WALL && object.attributes.roofId === roof.id).lines // 외벽의 라인 const roofLines = roof.lines // 지붕의 라인 From 10c2668a675471e2e178d9511eaf471ca0a67c8d Mon Sep 17 00:00:00 2001 From: Jaeyoung Lee Date: Mon, 17 Mar 2025 19:05:56 +0900 Subject: [PATCH 03/46] =?UTF-8?q?=EB=A7=88=EB=A3=A8=20=ED=98=95=EC=83=81?= =?UTF-8?q?=20=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20innerL?= =?UTF-8?q?ines=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/qpolygon-utils.js | 46 ++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index f6916e55..070fb096 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -846,6 +846,9 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { let baseHipLines = [] /** 용마루 */ let baseRidgeLines = [] + /** 용마루의 갯수*/ + let baseRidgeCount = 0 + /** ⨆ 모양 처마에 추녀마루를 그린다. */ drawEavesFirstLines.forEach((current) => { const { currentLine, prevLine, nextLine } = current @@ -1629,6 +1632,8 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { // console.log('oppositeSize : ', oppositeSize, 'lineMinSize : ', lineMinSize.toNumber(), 'ridgeSize : ', ridgeSize.toNumber()) if (ridgeSize.gt(0)) { + baseRidgeCount = baseRidgeCount + 1 + console.log('baseRidgeCount : ', baseRidgeCount) const points = [ startPoint.x, startPoint.y, @@ -1691,21 +1696,20 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { .forEach((ridge2) => { let overlap = segmentsOverlap(ridge, ridge2) if (overlap) { - roof.ridges = roof.ridges.filter((r) => r !== ridge && r !== ridge2) - roof.innerLines = roof.innerLines.filter((l) => l !== ridge && l !== ridge2) roof.canvas.remove(ridge) roof.canvas.remove(ridge2) baseRidgeLines = baseRidgeLines.filter((r) => r !== ridge && r !== ridge2) + baseRidgeCount = baseRidgeCount - 2 + let x1 = Math.min(ridge.x1, ridge2.x1, ridge.x2, ridge2.x2) let x2 = Math.max(ridge.x1, ridge2.x1, ridge.x2, ridge2.x2) let y1 = Math.min(ridge.y1, ridge2.y1, ridge.y2, ridge2.y2) let y2 = Math.max(ridge.y1, ridge2.y1, ridge.y2, ridge2.y2) const newRidge = drawRidgeLine([x1, y1, x2, y2], canvas, roof, textMode) - roof.ridges.push(newRidge) - roof.innerLines.push(newRidge) baseRidgeLines.push(newRidge) + baseRidgeCount = baseRidgeCount + 1 } }) }) @@ -2600,11 +2604,32 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { /** 직교 하는 포인트가 존재 할때 마루를 그린다. */ if (orthogonalPoints.length > 0) { + baseRidgeCount = baseRidgeCount + 1 const points = [current.x2, current.y2, orthogonalPoints[0].x2, orthogonalPoints[0].y2] const ridgeLine = drawRidgeLine(points, canvas, roof, textMode) baseRidgeLines.push(ridgeLine) /** array에서 object 삭제*/ - noRidgeHipPoints = noRidgeHipPoints.filter((point) => point !== current && point !== orthogonalPoints[0]) + // noRidgeHipPoints = noRidgeHipPoints.filter((point) => point !== current && point !== orthogonalPoints[0]) + } + }) + + /** 중복제거*/ + baseRidgeLines.forEach((current) => { + const sameRidge = baseRidgeLines.filter( + (line) => + line !== current && + ((line.x1 === current.x1 && line.y1 === current.y1 && line.x2 === current.x2 && line.y2 === current.y2) || + (line.x1 === current.x2 && line.y1 === current.y2 && line.x2 === current.x1 && line.y2 === current.y1)), + ) + + console.log('sameRidge : ', sameRidge) + if (sameRidge.length > 0) { + sameRidge.forEach((duplicateLine) => { + const index = baseRidgeLines.indexOf(duplicateLine) + if (index !== -1) { + baseRidgeLines.splice(index, 1) + } + }) } }) @@ -2709,6 +2734,7 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { noRidgeHipPoints.forEach((hipPoint) => { const angle = calculateAngle({ x: intersectPoint.x, y: intersectPoint.y }, { x: hipPoint.x2, y: hipPoint.y2 }) if (angle === 0 || angle === 180 || angle === 90 || angle === -90) { + baseRidgeCount = baseRidgeCount + 1 const ridgeLine = drawRidgeLine([intersectPoint.x, intersectPoint.y, hipPoint.x2, hipPoint.y2], canvas, roof, textMode) baseRidgeLines.push(ridgeLine) let baseHipLine = baseHipLines.filter((line) => line === intersection.line)[0] @@ -2753,11 +2779,9 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { console.log('ridgeAllPoints :', ridgeAllPoints) - console.log('지붕 마루 갯수 확인 : ', baseRidgeLines.length, getMaxRidge(baseLines.length)) + console.log('지붕 마루 갯수 확인 : ', baseRidgeLines.length, baseRidgeCount, getMaxRidge(baseLines.length)) ridgeAllPoints.forEach((current) => { - console.log('current :', current) - const ridgeLines = [], hipLines = [] @@ -2781,8 +2805,9 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { canvas.renderAll()*/ let checkRidgeLine, checkHipLine /** 직선인 경우 마루 확인*/ + console.log('baseRidgeCount :', baseRidgeCount, baseRidgeLines.length) if ( - baseRidgeLines.length < getMaxRidge(baseLines.length) && + baseRidgeCount < getMaxRidge(baseLines.length) && ((point.x === current.x && point.y !== current.y) || (point.x !== current.x && point.y === current.y)) ) { checkRidgeLine = { x1: current.x, y1: current.y, x2: point.x, y2: point.y } @@ -2835,6 +2860,7 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { ) if (!baseIntersection && alreadyRidges.length === 0 && otherRidgeInterSection.length === 0) { + baseRidgeCount = baseRidgeCount + 1 const ridgeLine = drawRidgeLine(ridgePoints, canvas, roof, textMode) baseRidgeLines.push(ridgeLine) } @@ -2887,6 +2913,8 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { }) }) + roof.innerLines = [...baseRidgeLines, ...baseHipLines.map((line) => line.line)] + /** 연결 되지 않은 마루 갯수*/ /*const missingCount = Big(baseRidgeLines.length) .minus(connectRidgePoint.length + 1) From 2537240bb0eac4d4ba08a583814037409722dcf8 Mon Sep 17 00:00:00 2001 From: Jaeyoung Lee Date: Tue, 18 Mar 2025 09:36:48 +0900 Subject: [PATCH 04/46] =?UTF-8?q?=EC=A4=91=EB=B3=B5=EB=90=9C=20=EB=A7=88?= =?UTF-8?q?=EB=A3=A8=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/qpolygon-utils.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index 070fb096..77bc8cae 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -2629,6 +2629,7 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { if (index !== -1) { baseRidgeLines.splice(index, 1) } + canvas.remove(duplicateLine) }) } }) From 3b4195207032276be8dd9ecef4aa348a83d9b805 Mon Sep 17 00:00:00 2001 From: Jaeyoung Lee Date: Tue, 18 Mar 2025 16:23:38 +0900 Subject: [PATCH 05/46] =?UTF-8?q?=EC=A7=80=EB=B6=95=20=EB=8D=AE=EA=B0=9C?= =?UTF-8?q?=20=EA=B3=84=EC=82=B0=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/qpolygon-utils.js | 490 +++++++------------------------------ 1 file changed, 82 insertions(+), 408 deletions(-) diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index ab828fb4..52551cdd 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -854,6 +854,13 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { /** ⨆ 모양 처마에 추녀마루를 그린다. */ drawEavesFirstLines.forEach((current) => { + // 확인용 라인, 포인트 제거 + /*canvas + .getObjects() + .filter((obj) => obj.name === 'checkPoint' || obj.name === 'checkLine') + .forEach((obj) => canvas.remove(obj)) + canvas.renderAll() +*/ const { currentLine, prevLine, nextLine } = current let { x1, x2, y1, y2 } = currentLine let beforePrevLine, afterNextLine @@ -934,13 +941,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { }, } - /*const checkMidLine = new fabric.Line([midLineEdge.vertex1.x, midLineEdge.vertex1.y, midLineEdge.vertex2.x, midLineEdge.vertex2.y], { - stroke: 'blue', - strokeWidth: 2, - }) - canvas.add(checkMidLine) - canvas.renderAll()*/ - /** 현재 라인의 중심 지점에서 현재라인의 길이만큼 다음라인의 방향만큼 거리를 확인한다*/ baseLines .filter((line) => line !== currentLine) @@ -981,7 +981,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { ).length > 0 /** 6각 */ - // console.log('isConnect : ', isConnect) if (isConnect) { const checkScale = currentSize.pow(2).plus(currentSize.pow(2)).sqrt() const intersectBaseLine = [] @@ -998,15 +997,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }, ) if (intersection && !intersection.isIntersectionOutside) { - /*const intersectCircle = new fabric.Circle({ - left: intersection.x - 2, - top: intersection.y - 2, - radius: 4, - fill: 'red', - parentId: roof.id, - }) - canvas.add(intersectCircle) - canvas.renderAll()*/ intersectBaseLine.push({ intersection, distance: Big(intersection.x) @@ -1031,15 +1021,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }, ) if (intersection && !intersection.isIntersectionOutside) { - /*const intersectCircle = new fabric.Circle({ - left: intersection.x - 2, - top: intersection.y - 2, - radius: 4, - fill: 'orange', - parentId: roof.id, - }) - canvas.add(intersectCircle) - canvas.renderAll()*/ intersectBaseLine.push({ intersection, distance: Big(intersection.x) @@ -1053,28 +1034,9 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } const intersection = intersectBaseLine.reduce((prev, current) => (prev.distance < current.distance ? prev : current), intersectBaseLine[0]) if (intersection) { - /*const intersectCircle = new fabric.Circle({ - left: intersection.intersection.x - 2, - top: intersection.intersection.y - 2, - radius: 4, - fill: 'green', - parentId: roof.id, - }) - canvas.add(intersectCircle) - canvas.renderAll()*/ - hipLength = intersection.distance } } else { - //라인 확인용 - /*const checkCurrentLine = new fabric.Line([x1, y1, x2, y2], { - stroke: 'yellow', - strokeWidth: 4, - parentId: roof.id, - }) - canvas.add(checkCurrentLine) - canvas.renderAll()*/ - const rightAngleLine = baseLines .filter( (line) => @@ -1151,39 +1113,14 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { ] } - /* const checkHipLine = new fabric.Line(checkHipPoints, { - stroke: 'red', - strokeWidth: 2, - parentId: roof.id, - }) - canvas.add(checkHipLine) - canvas.renderAll()*/ - if (checkHipPoints) { const intersectPoints = [] rightAngleLine.forEach((line) => { - /*const checkRightAngle = new fabric.Line([line.x1, line.y1, line.x2, line.y2], { - stroke: 'red', - strokeWidth: 4, - parentId: roof.id, - }) - canvas.add(checkRightAngle) - canvas.renderAll()*/ - const intersection = edgesIntersection( { vertex1: { x: checkHipPoints[0], y: checkHipPoints[1] }, vertex2: { x: checkHipPoints[2], y: checkHipPoints[3] } }, { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }, ) if (intersection) { - /*const checkRightAngleCircle = new fabric.Circle({ - left: intersection.x - 2, - top: intersection.y - 2, - radius: 4, - fill: 'red', - parentId: roof.id, - }) - canvas.add(checkRightAngleCircle) - canvas.renderAll()*/ intersectPoints.push({ intersection, distance: Big(intersection.x) @@ -1196,28 +1133,11 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { }) oppositeCurrentLine.forEach((line) => { - /*const checkOpposite = new fabric.Line([line.x1, line.y1, line.x2, line.y2], { - stroke: 'blue', - strokeWidth: 4, - parentId: roof.id, - }) - canvas.add(checkOpposite) - canvas.renderAll() -*/ const intersection = edgesIntersection( { vertex1: { x: checkHipPoints[0], y: checkHipPoints[1] }, vertex2: { x: checkHipPoints[2], y: checkHipPoints[3] } }, { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }, ) if (intersection) { - /*const checkOppositeCircle = new fabric.Circle({ - left: intersection.x - 2, - top: intersection.y - 2, - radius: 4, - fill: 'blue', - parentId: roof.id, - }) - canvas.add(checkOppositeCircle) - canvas.renderAll()*/ intersectPoints.push({ intersection, distance: Big(intersection.x) @@ -1229,19 +1149,8 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } }) - // console.log('intersectPoints : ', intersectPoints) - const intersection = intersectPoints.reduce((prev, current) => (prev.distance.lt(current.distance) ? prev : current), intersectPoints[0]) if (intersection) { - /*const checkHipCircle = new fabric.Circle({ - left: intersection.intersection.x - 2, - top: intersection.intersection.y - 2, - radius: 4, - fill: 'green', - parentId: roof.id, - }) - canvas.add(checkHipCircle) - canvas.renderAll()*/ hipLength = intersection.distance.div(2) } } @@ -1263,7 +1172,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { { vertex1: { x: x1, y: y1 }, vertex2: prevEndPoint }, { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }, ) - // console.log('intersection : ', intersection, 'prevEndPoint : ', prevEndPoint.x.toNumber(), prevEndPoint.y.toNumber()) if (intersection && !intersection.isIntersectionOutside) { intersectRidgeLine.push({ intersection, @@ -1276,7 +1184,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } }) const intersectRidge = intersectRidgeLine.reduce((prev, current) => (prev.distance < current.distance ? prev : current), intersectRidgeLine[0]) - // console.log('intersectRidge : ', intersectRidge) if (intersectRidge) { prevEndPoint.x = Big(intersectRidge.intersection.x).round(1) @@ -1295,14 +1202,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { vertex2: prevEndPoint, } - /*const checkLine = new fabric.Line([hipEdge.vertex1.x, hipEdge.vertex1.y, hipEdge.vertex2.x, hipEdge.vertex2.y], { - stroke: 'yellow', - strokeWidth: 2, - parentId: roof.id, - }) - canvas.add(checkLine) - canvas.renderAll()*/ - let intersectPoints = [] /** 외벽선에서 추녀 마루가 겹치는 경우에 대한 확인*/ roof.lines.forEach((line) => { @@ -1314,16 +1213,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { Math.sign(prevEndPoint.x - x1) === Math.sign(prevEndPoint.x - intersection.x) && Math.sign(prevEndPoint.y - y1) === Math.sign(prevEndPoint.y - intersection.y) ) { - /* const intersectPoint = new fabric.Circle({ - left: intersection.x - 2, - top: intersection.y - 2, - radius: 4, - fill: 'red', - parentId: roof.id, - }) - canvas.add(intersectPoint) - canvas.renderAll()*/ - const intersectSize = prevEndPoint.x .minus(Big(intersection.x)) .pow(2) @@ -1367,13 +1256,11 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const intersectRidgeLine = [] baseRidgeLines.forEach((line) => { - // console.log('nextEndPoint : ', nextEndPoint.x.toNumber(), nextEndPoint.y.toNumber()) - // console.log('intersection : ', 'x : ', line.x1, 'y : ', line.y1, 'x : ', line.x2, 'y : ', line.y2) const intersection = edgesIntersection( { vertex1: { x: x2, y: y2 }, vertex2: nextEndPoint }, { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }, ) - // console.log('intersection : ', intersection, 'nextEndPoint : ', nextEndPoint.x.toNumber(), nextEndPoint.y.toNumber()) + if (intersection && !intersection.isIntersectionOutside) { intersectRidgeLine.push({ intersection, @@ -1386,7 +1273,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } }) const intersectRidge = intersectRidgeLine.reduce((prev, current) => (prev.distance < current.distance ? prev : current), intersectRidgeLine[0]) - // console.log('intersectRidge : ', intersectRidge) if (intersectRidge) { nextEndPoint.x = Big(intersectRidge.intersection.x).round(1) @@ -1403,38 +1289,16 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { vertex2: nextEndPoint, } - /* const checkLine = new fabric.Line([hipEdge.vertex1.x, hipEdge.vertex1.y, hipEdge.vertex2.x, hipEdge.vertex2.y], { - stroke: 'yellow', - strokeWidth: 2, - parentId: roof.id, - }) - canvas.add(checkLine) - canvas.renderAll()*/ - let intersectPoints = [] roof.lines.forEach((line) => { const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } const intersection = edgesIntersection(hipEdge, lineEdge) - // console.log('intersection', intersection) if ( intersection && !intersection.isIntersectionOutside && Math.sign(nextEndPoint.x - x2) === Math.sign(nextEndPoint.x - intersection.x) && Math.sign(nextEndPoint.y - y2) === Math.sign(nextEndPoint.y - intersection.y) ) { - /*const intersectPoint = new fabric.Circle({ - left: intersection.x - 2, - top: intersection.y - 2, - radius: 4, - fill: 'blue', - parentId: roof.id, - }) - canvas.add(intersectPoint) - canvas.renderAll()*/ - - // console.log('nextEndPoint', nextEndPoint.x.toNumber(), nextEndPoint.y.toNumber()) - // console.log('intersection', intersection.x, intersection.y) - const intersectSize = nextEndPoint.x .minus(Big(intersection.x)) .pow(2) @@ -1454,8 +1318,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { return prev.size < current.size ? prev : current }, intersectPoints[0]) - // console.log('intersectPoints : ', intersectPoints) - if (intersectPoints && intersectPoints.intersection) { nextHipLine = drawHipLine( [intersectPoints.intersection.x, intersectPoints.intersection.y, nextEndPoint.x.toNumber(), nextEndPoint.y.toNumber()], @@ -2154,44 +2016,80 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } }) + /** baseHipLine 이 ridge에 붙지 않은 경우 확인 */ + baseHipLines.forEach((hipLine) => { + const ridgeCount = baseRidgeLines.filter( + (ridgeLine) => (hipLine.x2 === ridgeLine.x1 && hipLine.y2 === ridgeLine.y1) || (hipLine.x2 === ridgeLine.x2 && hipLine.y2 === ridgeLine.y2), + ).length + if (ridgeCount === 0) { + const hipXVector = Big(hipLine.x2).minus(hipLine.x1) + const hipYVector = Big(hipLine.y2).minus(hipLine.y1) + const hipSize = hipXVector.abs().pow(2).plus(hipYVector.abs().pow(2)).sqrt() + + const intersectRidgePoints = [] + + const hipLineEdge = { vertex1: { x: hipLine.x1, y: hipLine.y1 }, vertex2: { x: hipLine.x2, y: hipLine.y2 } } + baseRidgeLines.forEach((ridgeLine) => { + const ridgeLineEdge = { vertex1: { x: ridgeLine.x1, y: ridgeLine.y1 }, vertex2: { x: ridgeLine.x2, y: ridgeLine.y2 } } + const intersection = edgesIntersection(hipLineEdge, ridgeLineEdge) + + if (intersection) { + const intersectXVector = Big(intersection.x).minus(Big(hipLine.x1)) + const intersectYVector = Big(intersection.y).minus(Big(hipLine.y1)) + const intersectSize = intersectXVector.pow(2).plus(intersectYVector.pow(2)).sqrt() + + if (!intersection.isIntersectionOutside) { + intersectRidgePoints.push({ + x: intersection.x, + y: intersection.y, + size: intersectSize, + }) + } else if ( + ((intersection.x === ridgeLine.x1 && intersection.y === ridgeLine.y1) || + (intersection.x === ridgeLine.x2 && intersection.y === ridgeLine.y2)) && + Math.sign(hipXVector.toNumber()) === Math.sign(intersectXVector.toNumber()) && + Math.sign(hipYVector.toNumber()) === Math.sign(intersectYVector.toNumber()) && + intersectSize.gt(0) && + intersectSize.lt(hipSize) + ) { + intersectRidgePoints.push({ + x: intersection.x, + y: intersection.y, + size: intersectSize, + }) + } + } + }) + + intersectRidgePoints.sort((prev, current) => prev.size.minus(current.size).toNumber()) + if (intersectRidgePoints.length > 0) { + const oldPlaneSize = hipLine.line.attributes.planeSize + const oldActualSize = hipLine.line.attributes.actualSize + const theta = Big(Math.acos(Big(oldPlaneSize).div(oldActualSize))) + .times(180) + .div(Math.PI) + const planeSize = calcLinePlaneSize({ x1: hipLine.line.x1, y1: hipLine.line.y1, x2: hipLine.line.x2, y2: hipLine.line.y2 }) + hipLine.x2 = intersectRidgePoints[0].x + hipLine.y2 = intersectRidgePoints[0].y + hipLine.line.set({ x2: intersectRidgePoints[0].x, y2: intersectRidgePoints[0].y }) + hipLine.line.attributes.planeSize = planeSize + hipLine.line.attributes.actualSize = planeSize === oldActualSize ? 0 : Big(planeSize).div(theta).round(1).toNumber() + hipLine.line.fire('modified') + } + } + }) + // console.log('baseHipLines : ', baseHipLines) // console.log('baseRidgeLines : ', baseRidgeLines) /** 지붕선에 맞닫지 않은 부분을 확인하여 처리 한다.*/ /** 1. 그려진 마루 중 추녀마루가 부재하는 경우를 판단. 부재는 연결된 라인이 홀수인 경우로 판단한다.*/ let unFinishedRidge = [] baseRidgeLines.forEach((current) => { - /*const checkLine = new fabric.Line([current.x1, current.y1, current.x2, current.y2], { - stroke: 'red', - strokeWidth: 2, - parentId: roof.id, - }) - canvas.add(checkLine) - canvas.renderAll()*/ - let checkPoint = [ { x: current.x1, y: current.y1, line: current, cnt: 0 }, { x: current.x2, y: current.y2, line: current, cnt: 0 }, ] baseHipLines.forEach((line) => { - /*const checkPoint1 = new fabric.Circle({ - left: line.x1, - top: line.y1, - radius: 4, - fill: 'red', - parentId: roof.id, - }) - const checkPoint2 = new fabric.Circle({ - left: line.x2, - top: line.y2, - radius: 4, - fill: 'blue', - }) - canvas.add(checkPoint1) - canvas.add(checkPoint2) - canvas.renderAll()*/ - // console.log('current : ', current.x1, current.y1, current.x2, current.y2) - // console.log('line : ', line.x1, line.y1, line.x2, line.y2) - if ((line.x1 === current.x1 && line.y1 === current.y1) || (line.x2 === current.x1 && line.y2 === current.y1)) { checkPoint[0].cnt = checkPoint[0].cnt + 1 } @@ -2207,19 +2105,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { unFinishedRidge.push(checkPoint[1]) } }) - // console.log('unFinishedRidge : ', unFinishedRidge) - //포인트 확인 - /*unFinishedRidge.forEach((current) => { - const checkCircle = new fabric.Circle({ - left: current.x, - top: current.y, - radius: 4, - fill: 'green', - parentId: roof.id, - }) - canvas.add(checkCircle) - canvas.renderAll() - })*/ /** 2. 그려진 추녀마루 중 완성되지 않은 것을 찾는다. 완성되지 않았다는 것은 연결된 포인트가 홀수인 경우로 판단한다.*/ const findUnFinishedPoints = (baseHipLines) => { @@ -2259,7 +2144,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } let unFinishedPoint = findUnFinishedPoints(baseHipLines) - // console.log('unFinishedPoint : ', unFinishedPoint) /**3. 라인이 부재인 마루의 모자란 라인을 찾는다. 라인은 그려진 추녀마루 중에 완성되지 않은 추녀마루와 확인한다.*/ /**3-1 라인을 그릴때 각도가 필요하기 때문에 각도를 구한다. 각도는 전체 지부의 각도가 같다면 하나로 처리 */ @@ -2267,50 +2151,18 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { baseLines.forEach((line) => { const pitch = line.attributes.pitch const degree = line.attributes.degree - // console.log('pitch : ', pitch, 'degree : ', degree) degreeAllLine.push(pitch > 0 ? getDegreeByChon(pitch) : degree) }) let currentDegree, prevDegree degreeAllLine = [...new Set(degreeAllLine)] - // console.log('degreeAllLine : ', degreeAllLine) currentDegree = degreeAllLine[0] if (degreeAllLine.length === 1) { prevDegree = currentDegree } - /*unFinishedPoint.forEach((current) => { - const circle = new fabric.Circle({ - left: current.x, - top: current.y, - radius: 4, - fill: 'red', - parentId: roof.id, - }) - canvas.add(circle) - canvas.renderAll() - })*/ - /** 라인이 부재한 마루에 라인을 찾아 그린다.*/ unFinishedRidge.forEach((current) => { - /*const checkLine = new fabric.Line([current.line.x1, current.line.y1, current.line.x2, current.line.y2], { - stroke: 'red', - strokeWidth: 2, - parentId: roof.id, - }) - canvas.add(checkLine) - canvas.renderAll()*/ - - /*const checkCircle = new fabric.Circle({ - left: current.x, - top: current.y, - radius: 4, - fill: 'green', - parentId: roof.id, - }) - canvas.add(checkCircle) - canvas.renderAll()*/ - let checkPoints = [] unFinishedPoint @@ -2326,25 +2178,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { .lt(1), ) .forEach((point) => { - /*console.log('point : ', point.x, point.y) - const circle = new fabric.Circle({ - left: point.x, - top: point.y, - radius: 4, - fill: 'red', - parentId: roof.id, - }) - canvas.add(circle) - canvas.renderAll() - - const checkLine = new fabric.Line([current.x, current.y, point.x, point.y], { - stroke: 'green', - strokeWidth: 2, - parentId: roof.id, - }) - canvas.add(checkLine) - canvas.renderAll()*/ - const pointEdge = { vertex1: { x: point.x, y: point.y }, vertex2: { x: current.x, y: current.y } } let isIntersection = false baseLines.forEach((line) => { @@ -2366,37 +2199,11 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { }) } }) - // console.log('checkPoints : ', checkPoints) - // console.log('current ridge: ', current) - /*const checkCurrent = new fabric.Circle({ - left: current.x, - top: current.y, - radius: 4, - fill: 'blue', - parentId: roof.id, - }) - canvas.add(checkCurrent) - canvas.renderAll()*/ if (checkPoints.length > 0) { - /*checkPoints.forEach((point) => { - const checkPoints = new fabric.Circle({ - left: point.point.x, - top: point.point.y, - radius: 4, - fill: 'red', - parentId: roof.id, - }) - canvas.add(checkPoints) - canvas.renderAll() - })*/ - checkPoints.sort((a, b) => a.size - b.size) - // console.log('Big(2).minus(Big(current.cnt) : ', Big(2).minus(Big(current.cnt)).toNumber(), current.cnt) const maxCnt = Big(2).minus(Big(current.cnt)).toNumber() for (let i = 0; i < maxCnt; i++) { const checkPoint = checkPoints[i] - // console.log('current : ', current.line.attributes.planeSize) - // console.log('1. checkPoint : ', checkPoint) if (checkPoint) { let point = [checkPoint.point.x, checkPoint.point.y, current.x, current.y] let hipBasePoint @@ -2422,27 +2229,9 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { { x: line.line.x2, y: line.line.y2 }, ] /** baseHipLines도 조정*/ - /*const mergeBasePoint = [ - { x: point[0], y: point[1] }, - { x: point[2], y: point[3] }, - { x: line.x1, y: line.y1 }, - { x: line.x2, y: line.y2 }, - ]*/ mergePoint.sort((a, b) => a.x - b.x) - // mergeBasePoint.sort((a, b) => a.x - b.x) - /*const checkLine = new fabric.Line([mergeBasePoint[0].x, mergeBasePoint[0].y, mergeBasePoint[3].x, mergeBasePoint[3].y], { - stroke: 'red', - strokeWidth: 4, - parentId: roof.id, - }) - canvas.add(checkLine) - canvas.renderAll()*/ hipBasePoint = { x1: line.x1, y1: line.y1, x2: line.x2, y2: line.y2 } - // line.x1 = mergeBasePoint[0].x - // line.y1 = mergeBasePoint[0].y - // line.x2 = mergeBasePoint[3].x - // line.y2 = mergeBasePoint[3].y point = [mergePoint[0].x, mergePoint[0].y, mergePoint[3].x, mergePoint[3].y] canvas.remove(line.line) baseHipLines = baseHipLines.filter((baseLine) => baseLine.line !== line.line) @@ -2462,8 +2251,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { /** 라인이 다 그려지지 않은 경우 */ if (current.cnt % 2 !== 0) { - // console.log('no checkPoints :', current) - let basePoints = baseLinePoints .filter((point) => Big(point.x) @@ -2624,8 +2411,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { ((line.x1 === current.x1 && line.y1 === current.y1 && line.x2 === current.x2 && line.y2 === current.y2) || (line.x1 === current.x2 && line.y1 === current.y2 && line.x2 === current.x1 && line.y2 === current.y1)), ) - - console.log('sameRidge : ', sameRidge) if (sameRidge.length > 0) { sameRidge.forEach((duplicateLine) => { const index = baseRidgeLines.indexOf(duplicateLine) @@ -2640,15 +2425,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { /** 직교 하는 포인트가 없는 경우 남은 포인트 처리 */ const checkEdgeLines = [] noRidgeHipPoints.forEach((current) => { - /*const checkCircle = new fabric.Circle({ - left: current.x2, - top: current.y2, - radius: 4, - fill: 'red', - parentId: roof.id, - }) - canvas.add(checkCircle) - canvas.renderAll()*/ noRidgeHipPoints.forEach((current) => { noRidgeHipPoints .filter((point) => point !== current) @@ -2662,16 +2438,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { }) }) }) - // 라인 확인용 - /*checkEdgeLines.forEach((edge) => { - const checkLine = new fabric.Line([edge.vertex1.x, edge.vertex1.y, edge.vertex2.x, edge.vertex2.y], { - stroke: 'yellow', - strokeWidth: 2, - parentId: roof.id, - }) - canvas.add(checkLine) - canvas.renderAll() - })*/ /** 연결되지 않은 포인트를 찾아서 해당 포인트를 가지고 있는 라인을 찾는다. */ let unFinishedPoints = [] @@ -2693,20 +2459,11 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { .forEach((line) => unFinishedLines.push(line)) }) unFinishedLines.forEach((line) => { - /*const checkLine = new fabric.Line([line.x1, line.y1, line.x2, line.y2], { - stroke: 'red', - strokeWidth: 2, - parentId: roof.id, - }) - canvas.add(checkLine) - canvas.renderAll()*/ - const xVector = Math.sign(Big(line.x2).minus(Big(line.x1))) const yVector = Math.sign(Big(line.y2).minus(Big(line.y1))) let lineIntersectPoints = [] checkEdgeLines.forEach((edge) => { const intersectEdge = edgesIntersection(edge, { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }) - const intersection = [] if (intersectEdge) { const isXVector = Math.sign(Big(intersectEdge.x).minus(Big(line.x1))) === xVector const isYVector = Math.sign(Big(intersectEdge.y).minus(Big(line.y1))) === yVector @@ -2730,7 +2487,9 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { (point, index, self) => index === self.findIndex((p) => p.intersection.x === point.intersection.x && p.intersection.y === point.intersection.y), ) lineIntersectPoints.sort((a, b) => a.size - b.size) - intersectPoints.push({ intersection: lineIntersectPoints[0].intersection, line }) + if (lineIntersectPoints.length > 0) { + intersectPoints.push({ intersection: lineIntersectPoints[0].intersection, line }) + } }) intersectPoints.forEach((intersection) => { @@ -2776,40 +2535,15 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { }) }) - console.log('baseRidgeLines :', baseRidgeLines) - const ridgeAllPoints = [] baseRidgeLines.forEach((line) => ridgeAllPoints.push({ x: line.x1, y: line.y1 }, { x: line.x2, y: line.y2 })) - console.log('ridgeAllPoints :', ridgeAllPoints) - - console.log('지붕 마루 갯수 확인 : ', baseRidgeLines.length, baseRidgeCount, getMaxRidge(baseLines.length)) - ridgeAllPoints.forEach((current) => { - const ridgeLines = [], - hipLines = [] - ridgeAllPoints .filter((point) => point !== current) .forEach((point) => { - /*canvas - .getObjects() - .filter((obj) => obj.name === 'checkPoint' || obj.name === 'checkLine') - .forEach((obj) => canvas.remove(obj)) - canvas.renderAll() - const checkPoint = new fabric.Circle({ - left: current.x, - top: current.y, - radius: 4, - fill: 'blue', - parentId: roof.id, - name: 'checkPoint', - }) - canvas.add(checkPoint) - canvas.renderAll()*/ let checkRidgeLine, checkHipLine /** 직선인 경우 마루 확인*/ - console.log('baseRidgeCount :', baseRidgeCount, baseRidgeLines.length) if ( baseRidgeCount < getMaxRidge(baseLines.length) && ((point.x === current.x && point.y !== current.y) || (point.x !== current.x && point.y === current.y)) @@ -2868,9 +2602,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const ridgeLine = drawRidgeLine(ridgePoints, canvas, roof, textMode) baseRidgeLines.push(ridgeLine) } - - // checkLine1 = { x1: checkRidgeLine.x1, y1: checkRidgeLine.y1, x2: checkRidgeLine.x2, y2: checkRidgeLine.y2 } - // checkLine2 = { x1: checkRidgeLine.x2, y1: checkRidgeLine.y2, x2: checkRidgeLine.x1, y2: checkRidgeLine.y1 } } if (checkHipLine) { const hipPoints = [checkHipLine.x1, checkHipLine.y1, checkHipLine.x2, checkHipLine.y2] @@ -2919,61 +2650,12 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { roof.innerLines = [...baseRidgeLines, ...baseHipLines.map((line) => line.line)] - /** 연결 되지 않은 마루 갯수*/ - /*const missingCount = Big(baseRidgeLines.length) - .minus(connectRidgePoint.length + 1) - .toNumber() - - console.log('missingCount :', missingCount)*/ - /*baseRidgeLines.forEach((current) => { - baseRidgeLines - .filter((ridge) => ridge !== current) - .forEach((ridge) => { - /!** 직선인 경우 확인 *!/ - let checkLineEdge - if (current.x1 === ridge.x1 || current.y1 === ridge.y1) { - checkLineEdge = { vertex1: { x: current.x1, y: current.y1 }, vertex2: { x: ridge.x1, y: ridge.y1 } } - } else if (current.x2 === ridge.x1 || current.y2 === ridge.y1) { - checkLineEdge = { vertex1: { x: current.x2, y: current.y2 }, vertex2: { x: ridge.x1, y: ridge.y1 } } - } else if (current.x1 === ridge.x2 || current.y1 === ridge.y2) { - checkLineEdge = { vertex1: { x: current.x1, y: current.y1 }, vertex2: { x: ridge.x2, y: ridge.y2 } } - } else if (current.x2 === ridge.x2 || current.y2 === ridge.y2) { - checkLineEdge = { vertex1: { x: current.x2, y: current.y2 }, vertex2: { x: ridge.x2, y: ridge.y2 } } - } - - console.log('checkLineEdge :', checkLineEdge) - if (checkLineEdge) { - let hasIntersectLine = false - /!** 외벽선을 통과하는지 확인*!/ - baseLines.forEach((line) => { - const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } - const intersection = edgesIntersection(checkLineEdge, lineEdge) - if (intersection && !intersection.isIntersectionOutside) { - hasIntersectLine = true - } - }) - - /!** hipLines를 통과하는지 확인*!/ - baseHipLines.forEach((line) => { - const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } - const intersection = edgesIntersection(checkLineEdge, lineEdge) - if (intersection && !intersection.isIntersectionOutside) { - hasIntersectLine = true - } - }) - - // if (!hasIntersectLine) { - const checkLine = new fabric.Line([checkLineEdge.vertex1.x, checkLineEdge.vertex1.y, checkLineEdge.vertex2.x, checkLineEdge.vertex2.y], { - stroke: 'red', - strokeWidth: 2, - parentId: roof.id, - }) - canvas.add(checkLine) - canvas.renderAll() - // } - } - }) - })*/ + /** 확인용 라인, 포인트 제거 */ + canvas + .getObjects() + .filter((obj) => obj.name === 'checkPoint' || obj.name === 'checkLine') + .forEach((obj) => canvas.remove(obj)) + canvas.renderAll() /*drawRidge(roof, canvas, textMode) drawHips(roof, canvas, textMode) @@ -2993,7 +2675,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { * @param currentDegree */ const drawHipLine = (points, canvas, roof, textMode, currentRoof, prevDegree, currentDegree) => { - // console.log('drawHipLine : ', points) const hip = new QLine(points, { parentId: roof.id, fontSize: roof.fontSize, @@ -5882,16 +5563,9 @@ export const calcLinePlaneSize = (points) => { * @returns number */ export const calcLineActualSize = (points, degree) => { - const { x1, y1, x2, y2 } = points const planeSize = calcLinePlaneSize(points) - let height = Big(Math.tan(Big(degree).times(Math.PI / 180))).times(planeSize) - /** - * 대각선일 경우 높이 계산 변경 - */ - if (x1 !== x2 && y1 !== y2) { - height = Big(Math.tan(Big(degree).times(Math.PI / 180))).times(Big(x1).minus(x2).times(10).round()) - } - return Big(planeSize).pow(2).plus(height.pow(2)).sqrt().abs().round().toNumber() + const theta = Big(Math.cos(Big(degree).times(Math.PI).div(180))) + return Big(planeSize).div(theta).round(1).toNumber() } export const createLinesFromPolygon = (points) => { From b6e70f6eb0a628db9297ad4fbf58ab48bb6f8b6c Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Wed, 19 Mar 2025 09:52:35 +0900 Subject: [PATCH 06/46] =?UTF-8?q?innerLines=20=EC=B4=88=EA=B8=B0=20?= =?UTF-8?q?=EC=85=8B=ED=8C=85=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/common/useCanvasConfigInitialize.js | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/hooks/common/useCanvasConfigInitialize.js b/src/hooks/common/useCanvasConfigInitialize.js index 91d65edb..46b71588 100644 --- a/src/hooks/common/useCanvasConfigInitialize.js +++ b/src/hooks/common/useCanvasConfigInitialize.js @@ -9,7 +9,7 @@ import { globalFontAtom } from '@/store/fontAtom' import { useRoof } from '@/hooks/common/useRoof' import { usePolygon } from '@/hooks/usePolygon' import { useRoofFn } from '@/hooks/common/useRoofFn' -import { POLYGON_TYPE } from '@/common/common' +import { LINE_TYPE, POLYGON_TYPE } from '@/common/common' export function useCanvasConfigInitialize() { const canvas = useRecoilValue(canvasState) @@ -64,6 +64,7 @@ export function useCanvasConfigInitialize() { groupDimensionInit() reGroupInit() //그룹 객체 재그룹 moduleInit() + innerLinesInit() // innerLines 세팅 추가 } const gridInit = () => { @@ -228,5 +229,23 @@ export function useCanvasConfigInitialize() { }) } + const innerLinesInit = () => { + const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) + // innerLine이 세팅이 안되어있는경우 찾아서 세팅한다. + let innerLineTypes = Object.keys(LINE_TYPE.SUBLINE).map((key, value) => LINE_TYPE.SUBLINE[key]) + + roofs.forEach((roof) => { + roof.innerLines = canvas + .getObjects() + .filter( + (obj) => + obj.type === 'QLine' && + obj.attributes?.type !== 'pitchSizeLine' && + obj.attributes?.roofId === roof.id && + innerLineTypes.includes(obj.name), + ) + }) + } + return { canvasLoadInit, gridInit } } From 25240bc3c266886e6e04042ca436e342dd4e15ee Mon Sep 17 00:00:00 2001 From: Jaeyoung Lee Date: Mon, 24 Mar 2025 13:38:46 +0900 Subject: [PATCH 07/46] =?UTF-8?q?=EB=8F=99=EC=9D=B4=EB=8F=99,=20=ED=98=95?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80.?= =?UTF-8?q?=20=EC=9D=B4=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EB=B3=B4=EC=A1=B0?= =?UTF-8?q?=EC=84=A0=20=EC=9E=91=EC=84=B1=20=EC=88=98=EC=A0=95=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/common.js | 2 + src/components/fabric/QPolygon.js | 9 +- .../modal/movement/type/FlowLine.jsx | 11 +- .../floor-plan/modal/movement/type/Updown.jsx | 3 +- src/hooks/roofcover/useMovementSetting.js | 1190 +++++------------ src/hooks/roofcover/useRoofShapeSetting.js | 1 + src/hooks/useMode.js | 57 +- src/util/qpolygon-utils.js | 351 ++++- 8 files changed, 646 insertions(+), 978 deletions(-) diff --git a/src/common/common.js b/src/common/common.js index d82d43f0..d336244f 100644 --- a/src/common/common.js +++ b/src/common/common.js @@ -203,6 +203,8 @@ export const SAVE_KEY = [ 'fontWeight', 'dormerAttributes', 'toFixed', + 'startPoint', + 'endPoint', ] export const OBJECT_PROTOTYPE = [fabric.Line.prototype, fabric.Polygon.prototype, fabric.Triangle.prototype, fabric.Group.prototype] diff --git a/src/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js index f19687aa..65fe9d11 100644 --- a/src/components/fabric/QPolygon.js +++ b/src/components/fabric/QPolygon.js @@ -29,12 +29,13 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { this.texts = [] this.hips = [] this.ridges = [] - this.connectRidges = [] this.cells = [] this.innerLines = [] this.children = [] this.separatePolygon = [] this.toFixed = options.toFixed ?? 1 + this.baseLines = [] + // this.colorLines = [] // 소수점 전부 제거 points.forEach((point) => { @@ -211,6 +212,12 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { * @param settingModalFirstOptions */ drawHelpLine(settingModalFirstOptions) { + /* innerLines 초기화 */ + this.innerLines.forEach((line) => { + this.canvas.remove(line) + }) + this.canvas.renderAll() + let textMode = 'plane' const dimensionDisplay = settingModalFirstOptions?.dimensionDisplay.find((opt) => opt.selected).id diff --git a/src/components/floor-plan/modal/movement/type/FlowLine.jsx b/src/components/floor-plan/modal/movement/type/FlowLine.jsx index 565a9ff8..3c23a30a 100644 --- a/src/components/floor-plan/modal/movement/type/FlowLine.jsx +++ b/src/components/floor-plan/modal/movement/type/FlowLine.jsx @@ -15,13 +15,18 @@ export default function FlowLine({ FLOW_LINE_REF }) { const currentObject = useRecoilValue(currentObjectState) const handleFocus = () => { if (currentObject === null) { + FLOW_LINE_REF.POINTER_INPUT_REF.current.value = '' FLOW_LINE_REF.FILLED_INPUT_REF.current.value = '' FLOW_LINE_REF.FILLED_INPUT_REF.current.blur() } } const handleInput = (e) => { - const value = e.target.value.replace(/^0+/, '') - setFilledInput(value.replace(/[^0-9]/g, '')) + const regex = /^-?\d*$/ + let value = e.target.value + if (!regex.test(value) && value !== '') return + if (value.startsWith('0') || value === '-0') return + + setFilledInput(value.replace(/[^0-9-]/g, '')) } return ( <> @@ -47,7 +52,7 @@ export default function FlowLine({ FLOW_LINE_REF }) {
- {/**/} + {}
diff --git a/src/components/floor-plan/modal/movement/type/Updown.jsx b/src/components/floor-plan/modal/movement/type/Updown.jsx index bcc45590..786c7017 100644 --- a/src/components/floor-plan/modal/movement/type/Updown.jsx +++ b/src/components/floor-plan/modal/movement/type/Updown.jsx @@ -15,6 +15,7 @@ export default function Updown({ UP_DOWN_REF }) { const currentObject = useRecoilValue(currentObjectState) const handleFocus = () => { if (currentObject === null) { + UP_DOWN_REF.POINTER_INPUT_REF.current.value = '' UP_DOWN_REF.FILLED_INPUT_REF.current.value = '' UP_DOWN_REF.FILLED_INPUT_REF.current.blur() } @@ -48,7 +49,7 @@ export default function Updown({ UP_DOWN_REF }) {
- {/**/} + {}
diff --git a/src/hooks/roofcover/useMovementSetting.js b/src/hooks/roofcover/useMovementSetting.js index 1da5dcc8..558113ff 100644 --- a/src/hooks/roofcover/useMovementSetting.js +++ b/src/hooks/roofcover/useMovementSetting.js @@ -4,10 +4,9 @@ import { usePopup } from '@/hooks/usePopup' import { useMessage } from '@/hooks/useMessage' import { useEffect, useRef, useState } from 'react' import { useEvent } from '@/hooks/useEvent' -import { LINE_TYPE, POLYGON_TYPE } from '@/common/common' import { useSwal } from '@/hooks/useSwal' -import { QPolygon } from '@/components/fabric/QPolygon' -import { getDegreeByChon } from '@/util/canvas-util' +import { LINE_TYPE, POLYGON_TYPE } from '@/common/common' +import Big from 'big.js' //동선이동 형 올림 내림 export function useMovementSetting(id) { @@ -17,7 +16,6 @@ export function useMovementSetting(id) { } const canvas = useRecoilValue(canvasState) const { initEvent, addCanvasMouseEventListener } = useEvent() - // const { initEvent, addCanvasMouseEventListener } = useContext(EventContext) const { closePopup } = usePopup() const { getMessage } = useMessage() const currentObject = useRecoilValue(currentObjectState) @@ -31,939 +29,407 @@ export function useMovementSetting(id) { const { swalFire } = useSwal() const FLOW_LINE_REF = { + POINTER_INPUT_REF: useRef(null), FILLED_INPUT_REF: useRef(null), DOWN_LEFT_RADIO_REF: useRef(null), UP_RIGHT_RADIO_REF: useRef(null), } const UP_DOWN_REF = { + POINTER_INPUT_REF: useRef(null), FILLED_INPUT_REF: useRef(null), UP_RADIO_REF: useRef(null), DOWN_RADIO_REF: useRef(null), } + const CONFIRM_LINE_REF = useRef(null) + const FOLLOW_LINE_REF = useRef(null) + + /** 동선이동, 형이동 선택시 속성 처리*/ useEffect(() => { typeRef.current = type + selectedObject.current = null + if (FOLLOW_LINE_REF.current != null) { + canvas.remove(FOLLOW_LINE_REF.current) + canvas.renderAll() + FOLLOW_LINE_REF.current = null + } + if (CONFIRM_LINE_REF.current != null) { + canvas.remove(CONFIRM_LINE_REF.current) + canvas.renderAll() + CONFIRM_LINE_REF.current = null + } + clearRef() + + canvas.discardActiveObject() + /** 전체 object 선택 불가 */ canvas.getObjects().forEach((obj) => { obj.set({ selectable: false }) }) - const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) // 기존 wallLine의 visible false + + /** 지붕선 관련 속성 처리*/ + const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) roofs.forEach((roof) => { - roof.set({ selectable: false }) - roof.set({ strokeWidth: 1 }) roof.set({ stroke: '#000000' }) roof.innerLines.forEach((line) => { - line.bringToFront() - line.set({ selectable: false }) - line.set({ strokeWidth: 1 }) - line.set({ stroke: '#000000' }) - }) - roof.separatePolygon?.forEach((polygon) => { - polygon.bringToFront() - polygon.set({ selectable: false }) - polygon.set({ strokeWidth: 1 }) - polygon.set({ stroke: '#000000' }) + if (type === TYPE.FLOW_LINE && line.name === LINE_TYPE.SUBLINE.RIDGE) { + line.set({ selectable: true, strokeWidth: 5, stroke: '#1083E3' }) + line.bringToFront() + } else { + line.set({ selectable: false, strokeWidth: 2, stroke: '#000000' }) + } }) }) - if (type === TYPE.FLOW_LINE) { - roofs.forEach((roof) => { - roof.innerLines.forEach((line) => { - if (line.name === LINE_TYPE.SUBLINE.RIDGE) { - line.bringToFront() - line.set({ visible: true }) - line.set({ selectable: true }) - line.set({ strokeWidth: 4 }) - line.set({ stroke: '#1083E3' }) - } - }) + /** 외벽선 관련 속성 처리*/ + const walls = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL) + walls.forEach((wall) => { + if (wall.baseLines.length === 0) { + wall.baseLines = canvas.getObjects().filter((obj) => obj.name === 'baseLine' && obj.attributes.wallId === wall.id) + } + wall.baseLines.forEach((line) => { + if (type === TYPE.UP_DOWN) { + line.set({ selectable: true, visible: true, stroke: '#1083E3', strokeWidth: 5 }) + line.bringToFront() + } else { + line.set({ selectable: false, visible: false }) + } }) - } else if (type === TYPE.UP_DOWN) { - const wallLine = canvas.getObjects().filter((obj) => obj.name === 'wallLine') // 기존 outerLine의 selectable true - wallLine.forEach((line) => { - line.bringToFront() - line.set({ selectable: true }) - }) - } + }) + + /** outerLines 속성처리*/ + const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + outerLines.forEach((line) => line.set({ visible: false })) canvas.renderAll() }, [type]) + /** 팝업창이 열릴때,닫힐때 속성들을 처리*/ useEffect(() => { - canvas.renderAll() addCanvasMouseEventListener('mouse:move', mouseMoveEvent) addCanvasMouseEventListener('mouse:down', mouseDownEvent) return () => { - initEvent() - - const wallLines = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL) - wallLines.forEach((line) => { - line.set({ visible: true }) + if (FOLLOW_LINE_REF.current != null) { + canvas.remove(FOLLOW_LINE_REF.current) + FOLLOW_LINE_REF.current = null + } + if (CONFIRM_LINE_REF.current != null) { + canvas.remove(CONFIRM_LINE_REF.current) + CONFIRM_LINE_REF.current = null + } + const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) + roofs.forEach((roof) => { + roof.set({ stroke: '#1083E3' }) + roof.innerLines.forEach((line) => line.set({ selectable: true, strokeWidth: 2, stroke: '#1083E3' })) }) - - const wallLine = canvas.getObjects().filter((obj) => obj.name === 'wallLine') // 기존 outerLine의 selectable true - wallLine.forEach((line) => { - let wallStroke, wallStrokeWidth - switch (line.attributes.type) { - case LINE_TYPE.WALLLINE.EAVES: - wallStroke = '#45CD7D' - wallStrokeWidth = 4 - break - case LINE_TYPE.WALLLINE.HIPANDGABLE: - wallStroke = '#45CD7D' - wallStrokeWidth = 4 - break - case LINE_TYPE.WALLLINE.GABLE: - wallStroke = '#3FBAE6' - wallStrokeWidth = 4 - break - case LINE_TYPE.WALLLINE.JERKINHEAD: - wallStroke = '#3FBAE6' - wallStrokeWidth = 4 - break - case LINE_TYPE.WALLLINE.SHED: - wallStroke = '#000000' - wallStrokeWidth = 4 - break - case LINE_TYPE.WALLLINE.WALL: - wallStroke = '#000000' - wallStrokeWidth = 4 - break + const walls = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL) + walls.forEach((wall) => { + if (wall.baseLines.length === 0) { + wall.baseLines = canvas.getObjects().filter((obj) => obj.name === 'baseLine' && obj.attributes.wallId === wall.id) } - line.set({ selectable: false, stroke: wallStroke, strokeWidth: wallStrokeWidth }) + wall.baseLines.forEach((baseLine) => { + baseLine.set({ selectable: false, visible: false }) + }) }) - + const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + outerLines.forEach((line) => line.set({ visible: true })) canvas.renderAll() } }, []) + /** object 선택이 변경될 때 처리*/ useEffect(() => { - console.log('selectedObject.current : ', selectedObject.current) - if (selectedObject.current != null) { - let wallStroke - switch (selectedObject.current.attributes.type) { - case LINE_TYPE.WALLLINE.EAVES: - wallStroke = '#45CD7D' - break - case LINE_TYPE.WALLLINE.HIPANDGABLE: - wallStroke = '#45CD7D' - break - case LINE_TYPE.WALLLINE.GABLE: - wallStroke = '#3FBAE6' - break - case LINE_TYPE.WALLLINE.JERKINHEAD: - wallStroke = '#3FBAE6' - break - case LINE_TYPE.WALLLINE.SHED: - wallStroke = '#000000' - break - case LINE_TYPE.WALLLINE.WALL: - wallStroke = '#000000' - break - } - selectedObject.current.set({ stroke: wallStroke }) - selectedObject.current.bringToFront() - } - selectedObject.current = null - if (!currentObject) { - return + if (FOLLOW_LINE_REF.current != null) { + canvas.remove(FOLLOW_LINE_REF.current) + canvas.renderAll() + FOLLOW_LINE_REF.current = null } - clearRef() - selectedObject.current = currentObject - if (currentObject.name === 'wallLine') { - currentObject.set({ stroke: '#EA10AC' }) - currentObject.bringToFront() + if (selectedObject.current != null) { + selectedObject.current.set({ stroke: '#1083E3' }) + selectedObject.current = null } + if (!currentObject) return + + clearRef() + + canvas + .getObjects() + .filter((obj) => obj.name === 'checkPoint' || obj.name === 'checkLine') + .forEach((obj) => canvas.remove(obj)) + canvas.renderAll() + + if (CONFIRM_LINE_REF.current != null) { + canvas.remove(CONFIRM_LINE_REF.current) + canvas.renderAll() + CONFIRM_LINE_REF.current = null + } + + currentObject.set({ stroke: '#EA10AC' }) + selectedObject.current = currentObject + + const followLine = new fabric.Line([currentObject.x1, currentObject.y1, currentObject.x2, currentObject.y2], { + stroke: '#000000', + strokeWidth: 4, + selectable: false, + name: 'followLine', + }) + canvas.add(followLine) + FOLLOW_LINE_REF.current = followLine + + canvas.on('mouse:move', (event) => { + const mousePos = canvas.getPointer(event.e) + if (followLine.x1 === followLine.x2) { + followLine.left = mousePos.x - 2 + } else { + followLine.top = mousePos.y - 2 + } + canvas.renderAll() + }) + canvas.renderAll() }, [currentObject]) const clearRef = () => { if (type === TYPE.FLOW_LINE) { + FLOW_LINE_REF.POINTER_INPUT_REF.current.value = '' FLOW_LINE_REF.FILLED_INPUT_REF.current.value = '' FLOW_LINE_REF.DOWN_LEFT_RADIO_REF.current.checked = true FLOW_LINE_REF.UP_RIGHT_RADIO_REF.current.checked = false } if (type === TYPE.UP_DOWN) { + UP_DOWN_REF.POINTER_INPUT_REF.current.value = '' UP_DOWN_REF.FILLED_INPUT_REF.current.value = '' UP_DOWN_REF.UP_RADIO_REF.current.checked = true UP_DOWN_REF.DOWN_RADIO_REF.current.checked = false } } - const mouseDownEvent = (e) => { - if (typeRef.current === TYPE.FLOW_LINE) { - saveFlowLine(e) - } else { - saveUpDownLine(e) - } - } - const mouseMoveEvent = (e) => { + const target = canvas.getActiveObject() + if (!target) return + + const { top: targetTop, left: targetLeft } = target + const currentX = Big(canvas.getPointer(e.e).x).round(0, Big.roundUp) + const currentY = Big(canvas.getPointer(e.e).y).round(0, Big.roundUp) + let value = '' + if (target.y1 === target.y2) { + value = Big(targetTop).minus(currentY).times(10) + } else { + value = Big(targetLeft).minus(currentX).times(10).neg() + } if (typeRef.current === TYPE.FLOW_LINE) { - flowLineMoveEvent(e) + FLOW_LINE_REF.POINTER_INPUT_REF.current.value = value.toNumber() } else { - updownMoveEvent(e) + UP_DOWN_REF.POINTER_INPUT_REF.current.value = value.abs().toNumber() + const midX = Big(target.x1).plus(target.x2).div(2) + const midY = Big(target.y1).plus(target.y2).div(2) + const wall = canvas.getObjects().find((obj) => obj.id === target.attributes.wallId) + let checkPoint + if (target.y1 === target.y2) { + checkPoint = { x: midX.toNumber(), y: midY.plus(10).toNumber() } + if (wall.inPolygon(checkPoint)) { + if (value.s === -1) { + UP_DOWN_REF.UP_RADIO_REF.current.checked = false + UP_DOWN_REF.DOWN_RADIO_REF.current.checked = true + } else { + UP_DOWN_REF.UP_RADIO_REF.current.checked = true + UP_DOWN_REF.DOWN_RADIO_REF.current.checked = false + } + } else { + if (value.s === 1) { + UP_DOWN_REF.UP_RADIO_REF.current.checked = false + UP_DOWN_REF.DOWN_RADIO_REF.current.checked = true + } else { + UP_DOWN_REF.UP_RADIO_REF.current.checked = true + UP_DOWN_REF.DOWN_RADIO_REF.current.checked = false + } + } + } else { + checkPoint = { x: midX.plus(10).toNumber(), y: midY.toNumber() } + if (wall.inPolygon(checkPoint)) { + if (value.s === 1) { + UP_DOWN_REF.UP_RADIO_REF.current.checked = false + UP_DOWN_REF.DOWN_RADIO_REF.current.checked = true + } else { + UP_DOWN_REF.UP_RADIO_REF.current.checked = true + UP_DOWN_REF.DOWN_RADIO_REF.current.checked = false + } + } else { + if (value.s === -1) { + UP_DOWN_REF.UP_RADIO_REF.current.checked = false + UP_DOWN_REF.DOWN_RADIO_REF.current.checked = true + } else { + UP_DOWN_REF.UP_RADIO_REF.current.checked = true + UP_DOWN_REF.DOWN_RADIO_REF.current.checked = false + } + } + } } } - function edgesIntersection(edgeA, edgeB) { - const den = - (edgeB.vertex2.y - edgeB.vertex1.y) * (edgeA.vertex2.x - edgeA.vertex1.x) - - (edgeB.vertex2.x - edgeB.vertex1.x) * (edgeA.vertex2.y - edgeA.vertex1.y) - - if (den === 0) { - return null // lines are parallel or coincident - } - - const ua = - ((edgeB.vertex2.x - edgeB.vertex1.x) * (edgeA.vertex1.y - edgeB.vertex1.y) - - (edgeB.vertex2.y - edgeB.vertex1.y) * (edgeA.vertex1.x - edgeB.vertex1.x)) / - den - - const ub = - ((edgeA.vertex2.x - edgeA.vertex1.x) * (edgeA.vertex1.y - edgeB.vertex1.y) - - (edgeA.vertex2.y - edgeA.vertex1.y) * (edgeA.vertex1.x - edgeB.vertex1.x)) / - den - - // Edges are not intersecting but the lines defined by them are - const isIntersectionOutside = ua < 0 || ub < 0 || ua > 1 || ub > 1 - - return { - x: edgeA.vertex1.x + ua * (edgeA.vertex2.x - edgeA.vertex1.x), - y: edgeA.vertex1.y + ua * (edgeA.vertex2.y - edgeA.vertex1.y), - isIntersectionOutside, - } - } - - //동선 이동 마우스 클릭 이벤트 - const saveFlowLine = (e) => { - const target = selectedObject.current - if (!target) { - return - } - - let newPoint = [] - if (Math.sign(target.x1 - target.x2) !== 0) { - const sign = FLOW_LINE_REF.UP_RIGHT_RADIO_REF.current.checked ? 1 : -1 - newPoint = [ - target.x1, - target.y1 - sign * Number(FLOW_LINE_REF.FILLED_INPUT_REF.current.value / 10), - target.x2, - target.y2 - sign * Number(FLOW_LINE_REF.FILLED_INPUT_REF.current.value / 10), - ] - } else { - const sign = FLOW_LINE_REF.UP_RIGHT_RADIO_REF.current.checked ? -1 : 1 - newPoint = [ - target.x1 - sign * Number(FLOW_LINE_REF.FILLED_INPUT_REF.current.value / 10), - target.y1, - target.x2 - sign * Number(FLOW_LINE_REF.FILLED_INPUT_REF.current.value / 10), - target.y2, - ] - } - newPoint = newPoint.map((point) => Math.round(point * 10) / 10) - const cloned = new fabric.Line(newPoint, {}) - let currentObject = selectedObject.current - const currentX1 = currentObject.x1, - currentY1 = currentObject.y1, - currentX2 = currentObject.x2, - currentY2 = currentObject.y2 - - const currentMidX = (currentX1 + currentX2) / 2 - const currentMidY = (currentY1 + currentY2) / 2 - const clonedMidX = (cloned.x1 + cloned.x2) / 2 - const clonedMidY = (cloned.y1 + cloned.y2) / 2 - - const roof = canvas.getObjects().find((obj) => obj.id === currentObject.attributes.roofId) - let isOutside = false - roof.lines.forEach((line) => { - const intersection = edgesIntersection( - { vertex1: { x: currentMidX, y: currentMidY }, vertex2: { x: clonedMidX, y: clonedMidY } }, - { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }, - ) - if (intersection && !intersection.isIntersectionOutside) { - isOutside = true - } - }) - if (isOutside) { - swalFire({ text: getMessage('modal.movement.flow.line.move.alert'), icon: 'error' }) - return - } - currentObject.set({ x1: cloned.x1, y1: cloned.y1, x2: cloned.x2, y2: cloned.y2 }) - currentObject.setCoords() - const otherRidges = roof.innerLines.filter((line) => line.name === LINE_TYPE.SUBLINE.RIDGE && line.id !== currentObject.id) - const overlapRidges = otherRidges.filter((line) => { - if (currentObject.x1 === currentObject.x2 && line.x1 === line.x2 && 0 < Math.abs(currentObject.x1 - line.x1) < 1) { - if ( - currentObject.y1 <= line.y1 <= currentObject.y2 || - currentObject.y1 >= line.y1 >= currentObject.y2 || - currentObject.y1 <= line.y2 <= currentObject.y2 || - currentObject.y1 >= line.y2 >= currentObject.y2 - ) { - return true - } - } - if (currentObject.y1 === currentObject.y2 && line.y1 === line.y2 && 0 < Math.abs(currentObject.y1 - line.y1) < 1) { - if ( - currentObject.x1 <= line.x1 <= currentObject.x2 || - currentObject.x1 >= line.x1 >= currentObject.x2 || - currentObject.x1 <= line.x2 <= currentObject.x2 || - currentObject.x1 >= line.x2 >= currentObject.x2 - ) { - return true - } - } - }) - if (overlapRidges.length > 0) { - const minX = Math.min(...overlapRidges.flatMap((line) => [line.x1, line.x2]), currentObject.x1, currentObject.x2) - const maxX = Math.max(...overlapRidges.flatMap((line) => [line.x1, line.x2]), currentObject.x1, currentObject.x2) - const minY = Math.min(...overlapRidges.flatMap((line) => [line.y1, line.y2]), currentObject.y1, currentObject.y2) - const maxY = Math.max(...overlapRidges.flatMap((line) => [line.y1, line.y2]), currentObject.y1, currentObject.y2) - const newRidge = new fabric.Line([minX, minY, maxX, maxY], { - name: LINE_TYPE.SUBLINE.RIDGE, - selectable: true, - stroke: '#1083E3', - strokeWidth: 4, - attributes: { roofId: roof.id, currentRoofId: currentObject.attributes.currentRoofId }, - }) - - overlapRidges.forEach((line) => - line.attributes.currentRoofId.forEach((id) => { - if (!newRidge.attributes.currentRoofId.includes(id)) { - newRidge.attributes.currentRoofId.push(id) - } - }), - ) - canvas.add(newRidge) - newRidge.bringToFront() - roof.innerLines.push(newRidge) - - canvas.remove(currentObject) - roof.innerLines.forEach((innerLine, index) => { - if (innerLine.id === currentObject.id) { - roof.innerLines.splice(index, 1) - } - }) - - overlapRidges.forEach((line) => { - canvas.remove(line) - roof.innerLines.forEach((innerLine, index) => { - if (innerLine.id === line.id) { - roof.innerLines.splice(index, 1) - } - }) - }) - } - - if (roof.separatePolygon.length > 0) { - redrawSeparatePolygon(roof) - } else { - roof.innerLines - .filter((line) => currentObject !== line) - .filter( - (line) => - (line.x1 === currentX1 && line.y1 === currentY1) || - (line.x2 === currentX1 && line.y2 === currentY1) || - (line.x1 === currentX2 && line.y1 === currentY2) || - (line.x2 === currentX2 && line.y2 === currentY2), - ) - .forEach((line) => { - const lineDegree = 90 - Math.asin(line.attributes.planeSize / line.attributes.actualSize) * (180 / Math.PI) - if (line.x1 === currentX1 && line.y1 === currentY1) { - line.set({ x1: newPoint[0], y1: newPoint[1] }) - } else if (line.x2 === currentX1 && line.y2 === currentY1) { - line.set({ x2: newPoint[0], y2: newPoint[1] }) - } else if (line.x1 === currentX2 && line.y1 === currentY2) { - line.set({ x1: newPoint[2], y1: newPoint[3] }) - } else if (line.x2 === currentX2 && line.y2 === currentY2) { - line.set({ x2: newPoint[2], y2: newPoint[3] }) - } - line.setCoords() - line.attributes.planeSize = Math.round(Math.sqrt(Math.pow(line.x2 - line.x1, 2) + Math.pow(line.y2 - line.y1, 2)) * 10) - line.attributes.actualSize = Math.round( - Math.sqrt(Math.pow(line.attributes.planeSize, 2) + Math.pow(line.attributes.planeSize * Math.tan(lineDegree * (Math.PI / 180)), 2)), - ) - }) - } - - currentObject.set({ x1: cloned.x1, y1: cloned.y1, x2: cloned.x2, y2: cloned.y2 }) - currentObject.setCoords() - + const mouseDownEvent = (e) => { + canvas + .getObjects() + .filter((obj) => obj.name === 'checkPoint' || obj.name === 'checkLine') + .forEach((obj) => canvas.remove(obj)) canvas.renderAll() - canvas.discardActiveObject() - FLOW_LINE_REF.FILLED_INPUT_REF.current.value = '' - } - - //형 올림내림 마우스 클릭 이벤트 - const saveUpDownLine = (e) => { const target = selectedObject.current - if (!target) { - return - } + if (!target) return - const roof = canvas.getObjects().find((obj) => obj.id === target.attributes.roofId) - const wallLines = roof.wall.lines - const roofLines = roof.lines - const currentWall = wallLines.find((line) => line.id === target.attributes.currentWall) - let prevWall, nextWall - let prevEave, nextEave + const roofId = target.attributes.roofId + const followLine = canvas.getObjects().find((obj) => obj.name === 'followLine') - wallLines.forEach((wallLine, index) => { - if (wallLine.id === currentWall.id) { - prevWall = index === 0 ? wallLines[wallLines.length - 1] : wallLines[index - 1] - nextWall = index === wallLines.length - 1 ? wallLines[0] : wallLines[index + 1] - } - }) - - const eaves = roofLines.filter((line) => line.attributes?.type === LINE_TYPE.WALLLINE.EAVES) - - wallLines - .filter((line) => line.attributes.type === LINE_TYPE.WALLLINE.EAVES) - .forEach((eave, index) => { - if (eave.id === currentWall.id) { - prevEave = index === 0 ? eaves[eaves.length - 1] : eaves[index - 1] - nextEave = index === eaves.length - 1 ? eaves[0] : eaves[index + 1] - } - }) - - const currentRoof = roofLines.find((line) => line.id === target.attributes.currentRoof) - const currentRidges = roof.innerLines.filter( - (line) => line.name === LINE_TYPE.SUBLINE.RIDGE && line.attributes.currentRoof.includes(currentRoof.id), - ) - /*const prevWallRidges = roof.innerLines.filter( - (line) => line.name === LINE_TYPE.SUBLINE.RIDGE && line.attributes.currentRoof.includes(prevEave.id), - ) - const nextWallRidges = roof.innerLines.filter( - (line) => line.name === LINE_TYPE.SUBLINE.RIDGE && line.attributes.currentRoof.includes(nextEave.id), - ) - //라인 확인 작업 - /!* const roofLine = new fabric.Line([currentRoof.x1, currentRoof.y1, currentRoof.x2, currentRoof.y2], { - stroke: 'red', - strokeWidth: 5, + const confirmLine = new fabric.Line([followLine.x1, followLine.y1, followLine.x2, followLine.y2], { + left: followLine.left, + top: followLine.top, + stroke: '#000000', + strokeWidth: 4, selectable: false, + parentId: roofId, + name: 'confirmLine', + target, }) - canvas.add(roofLine) + canvas.add(confirmLine) + canvas.renderAll() + CONFIRM_LINE_REF.current = confirmLine - const prevEaves = new fabric.Line([prevEave.x1, prevEave.y1, prevEave.x2, prevEave.y2], { - stroke: 'yellow', - strokeWidth: 5, - selectable: false, - }) - canvas.add(prevEaves) - currentRidges.forEach((ridge) => ridge.set({ stroke: 'purple', strokeWidth: 5 })) - prevWallRidges.forEach((ridge) => ridge.set({ stroke: 'yellow', strokeWidth: 5 })) - nextWallRidges.forEach((ridge) => ridge.set({ stroke: 'green', strokeWidth: 5 })) - canvas.renderAll()*!/*/ - - console.log( - 'UP/DOWN', - UP_DOWN_REF.UP_RADIO_REF.current.checked, - UP_DOWN_REF.DOWN_RADIO_REF.current.checked, - UP_DOWN_REF.FILLED_INPUT_REF.current.value, - ) - - let compareLine - if (Math.sign(currentRoof.x1 - currentRoof.x2) === 0) { - const currentDiff = currentRidges[0].x1 - currentRoof.x1 - const prevDiff = prevEave.x1 - currentRoof.x1 - const nextDiff = nextEave.x1 - currentRoof.x1 - - console.log('currentDiff', Math.sign(currentDiff), currentDiff) - - if (UP_DOWN_REF.UP_RADIO_REF.current.checked) { - if (Math.sign(currentDiff) === 1) { - } else { - if (Math.sign(prevDiff) === 1 && Math.sign(nextDiff) === 1) { - } else { - } - } - } else { - if (Math.sign(currentDiff) === -1) { - } else { - if (Math.sign(prevDiff) === -1 && Math.sign(nextDiff) === -1) { - } else { - } - } - } - } else { - const currentDiff = currentRidges[0].y1 - currentRoof.y1 - const prevDiff = prevEave.y1 - currentRoof.y1 - const nextDiff = nextEave.y1 - currentRoof.y1 - - if (UP_DOWN_REF.UP_RADIO_REF.current.checked) { - if (Math.sign(currentDiff) === 1) { - } else { - if (Math.sign(prevDiff) === 1 && Math.sign(nextDiff) === 1) { - } else { - } - } - } else { - if (Math.sign(currentDiff) === -1) { - } else { - if (Math.sign(prevDiff) === -1 && Math.sign(nextDiff) === -1) { - } else { - } - } - } - } - - //확인 - /*const compareRoofLine = new fabric.Line([compareRoof.x1, compareRoof.y1, compareRoof.x2, compareRoof.y2], { - stroke: 'red', - strokeWidth: 5, - selectable: false, - }) - canvas.add(compareRoofLine) - canvas.renderAll()*/ - } - - const redrawSeparatePolygon = (roof) => { - roof.separatePolygon.forEach((polygon) => canvas.remove(polygon)) - roof.separatePolygon = [] - const roofLines = roof.lines - const wallLines = roof.wall.lines - const eaves = [] - roofLines.forEach((currentRoof, index) => { - if (currentRoof.attributes?.type === LINE_TYPE.WALLLINE.EAVES) { - eaves.push({ index: index, roof: currentRoof, length: currentRoof.attributes.planeSize }) - } - }) - const ridges = roof.innerLines.filter((line) => line.name === LINE_TYPE.SUBLINE.RIDGE) - eaves.sort((a, b) => a.length - b.length) - - const ridgeCount = eaves.length - 1 - console.log('ridgeCount', ridgeCount, 'ridges', ridges.length) - - const duplicatedEaves = [] - ridges.forEach((ridge) => { - const positiveLines = [] - const negativeLines = [] - ridge.attributes.currentRoof.forEach((id) => { - console.log('id : ', id) - const eave = roofLines.find((obj) => obj.id === id) - console.log('roof : ', eave) - if (ridge.x1 === ridge.x2) { - if (eave.y1 < eave.y2) { - positiveLines.push(eave) - } else { - negativeLines.push(eave) - } - } else { - if (eave.x1 < eave.x2) { - positiveLines.push(eave) - } else { - negativeLines.push(eave) - } - } - }) - if (positiveLines.length > 1) { - duplicatedEaves.push(positiveLines) - } - if (negativeLines.length > 1) { - duplicatedEaves.push(negativeLines) - } - }) - console.log('duplicatedEaves', duplicatedEaves) - - duplicatedEaves.forEach((duplicatedEave) => { - duplicatedEave.forEach((eave) => { - const index = eaves.findIndex((item) => item.roof.id === eave.id) - eaves.splice(index, 1) - }) - }) - - // if (ridgeCount === ridges.length) { - eaves.forEach((eave, i) => { - const index = eave.index, - currentRoof = eave.roof - const currentWall = wallLines[index] - const currentRidges = ridges.filter((ridge) => ridge.attributes.currentRoof.includes(eave.roof.id)) - let points = [] - const signX = Math.sign(currentRoof.x1 - currentRoof.x2) - let currentX1 = currentRoof.x1, - currentY1 = currentRoof.y1, - currentX2 = currentRoof.x2, - currentY2 = currentRoof.y2 - let changeX1 = [Math.min(currentRoof.x1, currentRoof.x2), Math.min(currentRoof.x1, currentRoof.x2)], - changeY1 = [Math.min(currentRoof.y1, currentRoof.y2), Math.min(currentRoof.y1, currentRoof.y2)], - changeX2 = [Math.max(currentRoof.x2, currentRoof.x1), Math.max(currentRoof.x2, currentRoof.x1)], - changeY2 = [Math.max(currentRoof.y2, currentRoof.y1), Math.max(currentRoof.y2, currentRoof.y1)] - - if (signX === 0) { - currentY1 = Math.min(currentRoof.y1, currentRoof.y2, currentWall.y1, currentWall.y2) - changeY1[1] = currentY1 - currentY2 = Math.max(currentRoof.y1, currentRoof.y2, currentWall.y1, currentWall.y2) - changeY2[1] = currentY2 - } else { - currentX1 = Math.min(currentRoof.x1, currentRoof.x2, currentWall.x1, currentWall.x2) - changeX1[1] = currentX1 - currentX2 = Math.max(currentRoof.x1, currentRoof.x2, currentWall.x1, currentWall.x2) - changeX2[1] = currentX2 - } - points.push({ x: currentX1, y: currentY1 }, { x: currentX2, y: currentY2 }) - - currentRidges.forEach((ridge) => { - let ridgeX1 = ridge.x1, - ridgeY1 = ridge.y1, - ridgeX2 = ridge.x2, - ridgeY2 = ridge.y2 - if (signX === 0) { - ridgeY1 = Math.min(ridge.y1, ridge.y2) - ridgeY2 = Math.max(ridge.y1, ridge.y2) - } else { - ridgeX1 = Math.min(ridge.x1, ridge.x2) - ridgeX2 = Math.max(ridge.x1, ridge.x2) - } - points.push({ x: ridgeX1, y: ridgeY1 }, { x: ridgeX2, y: ridgeY2 }) - }) - - points.forEach((point) => { - if (point.x === changeX1[0] && changeX1[0] !== changeX1[1]) { - point.x = changeX1[1] - } - if (point.x === changeX2[0] && changeX2[0] !== changeX2[1]) { - point.x = changeX2[1] - } - if (point.y === changeY1[0] && changeY1[0] !== changeY1[1]) { - point.y = changeY1[1] - } - if (point.y === changeY2[0] && changeY2[0] !== changeY2[1]) { - point.y = changeY2[1] - } - }) - //중복된 point 제거 - points = points.filter((point, index, self) => index === self.findIndex((p) => p.x === point.x && p.y === point.y)) - //point 정렬 (가장 좌측, 최상단의 점을 기준으로 삼는다.) - const startPoint = points - .filter((point) => point.x === Math.min(...points.map((point) => point.x))) - .reduce((prev, current) => { - return prev.y < current.y ? prev : current - }) - - const sortedPoints = [] - sortedPoints.push(startPoint) - - points.forEach((p, index) => { - if (index === 0) { - //시작점 다음 점 찾기, y좌표가 startPoint.y 보다 큰 점 중 x좌표가 가까운 점 - const underStartPoint = points.filter((point) => point.y > startPoint.y) - const nextPoint = underStartPoint - .filter((point) => point.x === startPoint.x) - .reduce((prev, current) => { - if (prev === undefined) { - return current - } - return Math.abs(prev.y - startPoint.y) < Math.abs(current.y - startPoint.y) ? prev : current - }, undefined) - if (nextPoint) { - sortedPoints.push(nextPoint) - } else { - const nextPoint = underStartPoint.reduce((prev, current) => { - const prevHypos = Math.sqrt(Math.abs(Math.pow(prev.x - startPoint.x, 2)) + Math.abs(Math.pow(prev.y - startPoint.y, 2))) - const currentHypos = Math.sqrt(Math.abs(Math.pow(current.x - startPoint.x, 2)) + Math.abs(Math.pow(current.y - startPoint.y, 2))) - return prevHypos < currentHypos ? prev : current - }, undefined) - sortedPoints.push(nextPoint) - } - } else { - const lastPoint = sortedPoints[sortedPoints.length - 1] - console.log('lastPoint', lastPoint) - const prevPoint = sortedPoints[sortedPoints.length - 2] - const otherPoints = points.filter((point) => sortedPoints.includes(point) === false) - const nextPoint = otherPoints.reduce((prev, current) => { - if (prev === undefined) { - const height = Math.abs(Math.sqrt(Math.abs(Math.pow(prevPoint.x - lastPoint.x, 2)) + Math.abs(Math.pow(prevPoint.y - lastPoint.y, 2)))) - const adjacent = Math.abs(Math.sqrt(Math.abs(Math.pow(current.x - lastPoint.x, 2)) + Math.abs(Math.pow(current.y - lastPoint.y, 2)))) - const hypotenuse = Math.abs(Math.sqrt(Math.abs(Math.pow(current.x - prevPoint.x, 2)) + Math.abs(Math.pow(current.y - prevPoint.y, 2)))) - - const angle = Math.round( - Math.acos((Math.pow(adjacent, 2) + Math.pow(height, 2) - Math.pow(hypotenuse, 2)) / (2 * adjacent * height)) * (180 / Math.PI), - ) - if (angle === 90) { - return current - } - } else { - return prev - } - }, undefined) - if (nextPoint) { - sortedPoints.push(nextPoint) - } else { - const nextPoint = otherPoints.reduce((prev, current) => { - if (prev !== undefined) { - const height = Math.abs( - Math.sqrt(Math.abs(Math.pow(prevPoint.x - lastPoint.x, 2)) + Math.abs(Math.pow(prevPoint.y - lastPoint.y, 2))), - ) - const adjacentC = Math.abs(Math.sqrt(Math.abs(Math.pow(current.x - lastPoint.x, 2)) + Math.abs(Math.pow(current.y - lastPoint.y, 2)))) - const hypotenuseC = Math.abs( - Math.sqrt(Math.abs(Math.pow(current.x - prevPoint.x, 2)) + Math.abs(Math.pow(current.y - prevPoint.y, 2))), - ) - const angleC = Math.round( - Math.acos((Math.pow(adjacentC, 2) + Math.pow(height, 2) - Math.pow(hypotenuseC, 2)) / (2 * adjacentC * height)) * (180 / Math.PI), - ) - const adjacentP = Math.abs(Math.sqrt(Math.abs(Math.pow(prev.x - lastPoint.x, 2)) + Math.abs(Math.pow(prev.y - lastPoint.y, 2)))) - const hypotenuseP = Math.abs(Math.sqrt(Math.abs(Math.pow(prev.x - prevPoint.x, 2)) + Math.abs(Math.pow(prev.y - prevPoint.y, 2)))) - const angleP = Math.round( - Math.acos((Math.pow(adjacentP, 2) + Math.pow(height, 2) - Math.pow(hypotenuseP, 2)) / (2 * adjacentP * height)) * (180 / Math.PI), - ) - if (Math.abs(90 - angleC) < Math.abs(90 - angleP)) { - return current - } else { - return prev - } - } else { - return current - } - }, undefined) - if (nextPoint) { - sortedPoints.push(nextPoint) - } - } - } - }) - - if (sortedPoints.length > 0) { - const roofPolygon = new QPolygon(sortedPoints, { - fill: 'transparent', - stroke: '#000000', - strokeWidth: 1, - selectable: false, - fontSize: roof.fontSize, - name: 'roofPolygon', - attributes: { - roofId: roof.id, - currentRoofId: currentRoof.id, - pitch: currentRoof.attributes.pitch, - degree: currentRoof.attributes.degree, - direction: currentRoof.direction, - }, - }) - const currentDegree = currentRoof.attributes.pitch > 0 ? getDegreeByChon(currentRoof.attributes.pitch) : currentRoof.attributes.degree - //지붕 각도에 따른 실측치 조정 - roofPolygon.lines.forEach((line) => { - line.attributes.planeSize = Math.round(Math.sqrt(Math.pow(line.x2 - line.x1, 2) + Math.pow(line.y2 - line.y1, 2)) * 10) - const slope = (line) => (line.x2 - line.x1 === 0 ? Infinity : (line.y2 - line.y1) / (line.x2 - line.x1)) - - if (currentDegree > 0 && slope(line) !== slope(currentRoof)) { - const height = Math.tan(currentDegree * (Math.PI / 180)) * line.attributes.planeSize - line.attributes.actualSize = Math.round(Math.sqrt(Math.pow(line.attributes.planeSize, 2) + Math.pow(height, 2))) - } else { - line.attributes.actualSize = line.attributes.planeSize - } - }) - roof.separatePolygon.push(roofPolygon) - canvas.add(roofPolygon) - canvas.renderAll() - } - }) - - duplicatedEaves.forEach((duplicatedEave) => { - const currentRoof = duplicatedEave[0] - let points = [] - duplicatedEave.forEach((eave) => { - points.push({ x: eave.x1, y: eave.y1 }, { x: eave.x2, y: eave.y2 }) - const currentRidges = ridges.filter((ridge) => ridge.attributes.currentRoof.includes(eave.id)) - currentRidges.forEach((ridge) => { - points.push({ x: ridge.x1, y: ridge.y1 }, { x: ridge.x2, y: ridge.y2 }) - }) - }) - console.log('points', points) - points = points.filter((point, index, self) => index === self.findIndex((p) => p.x === point.x && p.y === point.y)) - //point 정렬 (가장 좌측, 최상단의 점을 기준으로 삼는다.) - - const startPoint = points - .filter((point) => point.x === Math.min(...points.map((point) => point.x))) - .reduce((prev, current) => { - return prev.y < current.y ? prev : current - }) - - const sortedPoints = [] - sortedPoints.push(startPoint) - - points.forEach((p, index) => { - if (index === 0) { - //시작점 다음 점 찾기, y좌표가 startPoint.y 보다 큰 점 중 x좌표가 가까운 점 - const underStartPoint = points.filter((point) => point.y > startPoint.y) - const nextPoint = underStartPoint - .filter((point) => point.x === startPoint.x) - .reduce((prev, current) => { - if (prev === undefined) { - return current - } - return Math.abs(prev.y - startPoint.y) < Math.abs(current.y - startPoint.y) ? prev : current - }, undefined) - if (nextPoint) { - sortedPoints.push(nextPoint) - } else { - const nextPoint = underStartPoint.reduce((prev, current) => { - const prevHypos = Math.sqrt(Math.abs(Math.pow(prev.x - startPoint.x, 2)) + Math.abs(Math.pow(prev.y - startPoint.y, 2))) - const currentHypos = Math.sqrt(Math.abs(Math.pow(current.x - startPoint.x, 2)) + Math.abs(Math.pow(current.y - startPoint.y, 2))) - return prevHypos < currentHypos ? prev : current - }, undefined) - sortedPoints.push(nextPoint) - } - } else { - const lastPoint = sortedPoints[sortedPoints.length - 1] - console.log('lastPoint', lastPoint) - const prevPoint = sortedPoints[sortedPoints.length - 2] - const otherPoints = points.filter((point) => sortedPoints.includes(point) === false) - const nextPoint = otherPoints.reduce((prev, current) => { - if (prev === undefined) { - const height = Math.abs(Math.sqrt(Math.abs(Math.pow(prevPoint.x - lastPoint.x, 2)) + Math.abs(Math.pow(prevPoint.y - lastPoint.y, 2)))) - const adjacent = Math.abs(Math.sqrt(Math.abs(Math.pow(current.x - lastPoint.x, 2)) + Math.abs(Math.pow(current.y - lastPoint.y, 2)))) - const hypotenuse = Math.abs(Math.sqrt(Math.abs(Math.pow(current.x - prevPoint.x, 2)) + Math.abs(Math.pow(current.y - prevPoint.y, 2)))) - - const angle = Math.round( - Math.acos((Math.pow(adjacent, 2) + Math.pow(height, 2) - Math.pow(hypotenuse, 2)) / (2 * adjacent * height)) * (180 / Math.PI), - ) - if (angle === 90) { - return current - } - } else { - return prev - } - }, undefined) - if (nextPoint) { - sortedPoints.push(nextPoint) - } else { - const nextPoint = otherPoints.reduce((prev, current) => { - if (prev !== undefined) { - const height = Math.abs( - Math.sqrt(Math.abs(Math.pow(prevPoint.x - lastPoint.x, 2)) + Math.abs(Math.pow(prevPoint.y - lastPoint.y, 2))), - ) - const adjacentC = Math.abs(Math.sqrt(Math.abs(Math.pow(current.x - lastPoint.x, 2)) + Math.abs(Math.pow(current.y - lastPoint.y, 2)))) - const hypotenuseC = Math.abs( - Math.sqrt(Math.abs(Math.pow(current.x - prevPoint.x, 2)) + Math.abs(Math.pow(current.y - prevPoint.y, 2))), - ) - const angleC = Math.round( - Math.acos((Math.pow(adjacentC, 2) + Math.pow(height, 2) - Math.pow(hypotenuseC, 2)) / (2 * adjacentC * height)) * (180 / Math.PI), - ) - const adjacentP = Math.abs(Math.sqrt(Math.abs(Math.pow(prev.x - lastPoint.x, 2)) + Math.abs(Math.pow(prev.y - lastPoint.y, 2)))) - const hypotenuseP = Math.abs(Math.sqrt(Math.abs(Math.pow(prev.x - prevPoint.x, 2)) + Math.abs(Math.pow(prev.y - prevPoint.y, 2)))) - const angleP = Math.round( - Math.acos((Math.pow(adjacentP, 2) + Math.pow(height, 2) - Math.pow(hypotenuseP, 2)) / (2 * adjacentP * height)) * (180 / Math.PI), - ) - if (Math.abs(90 - angleC) < Math.abs(90 - angleP)) { - return current - } else { - return prev - } - } else { - return current - } - }, undefined) - if (nextPoint) { - sortedPoints.push(nextPoint) - } - } - } - }) - - if (sortedPoints.length > 0) { - const roofPolygon = new QPolygon(sortedPoints, { - fill: 'transparent', - stroke: '#000000', - strokeWidth: 1, - selectable: false, - fontSize: roof.fontSize, - name: 'roofPolygon', - attributes: { - roofId: roof.id, - currentRoofId: currentRoof.id, - pitch: currentRoof.attributes.pitch, - degree: currentRoof.attributes.degree, - direction: currentRoof.direction, - }, - }) - const currentDegree = currentRoof.attributes.pitch > 0 ? getDegreeByChon(currentRoof.attributes.pitch) : currentRoof.attributes.degree - //지붕 각도에 따른 실측치 조정 - roofPolygon.lines.forEach((line) => { - line.attributes.planeSize = Math.round(Math.sqrt(Math.pow(line.x2 - line.x1, 2) + Math.pow(line.y2 - line.y1, 2)) * 10) - const slope = (line) => (line.x2 - line.x1 === 0 ? Infinity : (line.y2 - line.y1) / (line.x2 - line.x1)) - - if (currentDegree > 0 && slope(line) !== slope(currentRoof)) { - const height = Math.tan(currentDegree * (Math.PI / 180)) * line.attributes.planeSize - line.attributes.actualSize = Math.round(Math.sqrt(Math.pow(line.attributes.planeSize, 2) + Math.pow(height, 2))) - } else { - line.attributes.actualSize = line.attributes.planeSize - } - }) - roof.separatePolygon.push(roofPolygon) - canvas.add(roofPolygon) - canvas.renderAll() - } - }) - console.log('roof.separatePolygon : ', roof.separatePolygon) - - ridges.forEach((ridge) => ridge.bringToFront()) - console.log('ridges : ', ridges) - } - - const flowLineMoveEvent = (e) => { - const target = canvas.getActiveObject() - if (!target) { - return - } - const { top: targetTop, left: targetLeft } = target - const currentX = canvas.getPointer(e.e).x - const currentY = Math.floor(canvas.getPointer(e.e).y) - - if (Math.sign(target.x1 - target.x2) !== 0) { - if (targetTop > currentY) { - FLOW_LINE_REF.UP_RIGHT_RADIO_REF.current.checked = true - FLOW_LINE_REF.FILLED_INPUT_REF.current.value = Math.floor((Number(Math.abs(Math.round(targetTop - currentY))) / 10000).toFixed(5) * 100000) - } else { - FLOW_LINE_REF.DOWN_LEFT_RADIO_REF.current.checked = true - FLOW_LINE_REF.FILLED_INPUT_REF.current.value = Math.floor((Number(Math.abs(Math.round(targetTop - currentY))) / 10000).toFixed(5) * 100000) - } - } else { - if (targetLeft < currentX) { - FLOW_LINE_REF.UP_RIGHT_RADIO_REF.current.checked = true - FLOW_LINE_REF.FILLED_INPUT_REF.current.value = Math.floor((Number(Math.abs(Math.round(currentX - targetLeft))) / 10000).toFixed(5) * 100000) - } else { - FLOW_LINE_REF.DOWN_LEFT_RADIO_REF.current.checked = true - FLOW_LINE_REF.FILLED_INPUT_REF.current.value = Math.floor((Number(Math.abs(Math.round(currentX - targetLeft))) / 10000).toFixed(5) * 100000) - } - } - - canvas?.renderAll() - } - - const updownMoveEvent = (e) => { - const target = canvas.getActiveObject() - if (!target) { - return - } - const { top: targetTop, left: targetLeft } = target - const currentX = canvas.getPointer(e.e).x - const currentY = Math.floor(canvas.getPointer(e.e).y) - - if (Math.sign(target.x1 - target.x2) !== 0) { - if (targetTop > currentY) { - UP_DOWN_REF.DOWN_RADIO_REF.current.checked = true - UP_DOWN_REF.FILLED_INPUT_REF.current.value = Math.floor((Number(Math.abs(targetTop - currentY)) / 10000).toFixed(5) * 100000) - } else { - UP_DOWN_REF.UP_RADIO_REF.current.checked = true - UP_DOWN_REF.FILLED_INPUT_REF.current.value = Math.floor((Number(Math.abs(targetTop - currentY)) / 10000).toFixed(5) * 100000) - } - } else { - if (targetLeft > currentX) { - UP_DOWN_REF.DOWN_RADIO_REF.current.checked = true - UP_DOWN_REF.FILLED_INPUT_REF.current.value = Math.floor((Number(Math.abs(targetLeft - currentX)) / 10000).toFixed(5) * 100000) - } else { - UP_DOWN_REF.UP_RADIO_REF.current.checked = true - UP_DOWN_REF.FILLED_INPUT_REF.current.value = Math.floor((Number(Math.abs(targetLeft - currentX)) / 10000).toFixed(5) * 100000) - } - } - - canvas?.renderAll() + handleSave() } const handleSave = () => { - if (type === TYPE.FLOW_LINE) { - saveFlowLine() + const target = selectedObject.current !== null ? selectedObject.current : CONFIRM_LINE_REF.current?.target + if (!target) return + + const roofId = target.attributes.roofId + const roof = canvas.getObjects().find((obj) => obj.id === roofId) + const wall = canvas.getObjects().find((obj) => obj.name === POLYGON_TYPE.WALL && obj.attributes.roofId === roofId) + const baseLines = wall.baseLines + let targetBaseLines = [] + + if (typeRef.current === TYPE.FLOW_LINE) { + const lineVector = + target.y1 === target.y2 + ? FLOW_LINE_REF.UP_RIGHT_RADIO_REF.current.checked + ? 'up' + : 'down' + : FLOW_LINE_REF.UP_RIGHT_RADIO_REF.current.checked + ? 'right' + : 'left' + switch (lineVector) { + case 'up': + baseLines + .filter((line) => line.y1 === line.y2 && line.y1 < target.y1) + .forEach((line) => targetBaseLines.push({ line, distance: Big(target.y1).minus(line.y1).abs().toNumber() })) + break + case 'down': + baseLines + .filter((line) => line.y1 === line.y2 && line.y1 > target.y1) + .forEach((line) => targetBaseLines.push({ line, distance: Big(line.y1).minus(target.y1).abs().toNumber() })) + break + case 'right': + baseLines + .filter((line) => line.x1 === line.x2 && line.x1 > target.x1) + .forEach((line) => targetBaseLines.push({ line, distance: Big(target.x1).minus(line.x1).abs().toNumber() })) + + break + case 'left': + baseLines + .filter((line) => line.x1 === line.x2 && line.x1 < target.x1) + .forEach((line) => targetBaseLines.push({ line, distance: Big(line.x1).minus(target.x1).abs().toNumber() })) + break + } } else { - saveUpDownLine() + targetBaseLines.push({ line: target, distance: 0 }) } + + targetBaseLines.sort((a, b) => a.distance - b.distance) + targetBaseLines = targetBaseLines.filter((line) => line.distance === targetBaseLines[0].distance) + let value + if (typeRef.current === TYPE.FLOW_LINE) { + value = + FLOW_LINE_REF.FILLED_INPUT_REF.current.value !== '' + ? Big(FLOW_LINE_REF.FILLED_INPUT_REF.current.value).times(2) + : Big(FLOW_LINE_REF.POINTER_INPUT_REF.current.value).times(2) + if (target.y1 === target.y2) { + value = value.neg() + } + } else { + value = + UP_DOWN_REF.FILLED_INPUT_REF.current.value !== '' + ? Big(UP_DOWN_REF.FILLED_INPUT_REF.current.value) + : Big(UP_DOWN_REF.POINTER_INPUT_REF.current.value) + + const midX = Big(target.x1).plus(target.x2).div(2) + const midY = Big(target.y1).plus(target.y2).div(2) + const wall = canvas.getObjects().find((obj) => obj.id === target.attributes.wallId) + let checkPoint + if (target.y1 === target.y2) { + checkPoint = { x: midX.toNumber(), y: midY.plus(10).times(value.s).toNumber() } + } else { + checkPoint = { x: midX.plus(10).toNumber().times(value.s), y: midY.toNumber() } + } + + const inPolygon = wall.inPolygon(checkPoint) + + console.log('inPolygon', inPolygon) + if (UP_DOWN_REF.UP_RADIO_REF.current.checked && inPolygon) { + value = value.neg() + } else if (UP_DOWN_REF.DOWN_RADIO_REF.current.checked && !inPolygon) { + value = value.neg() + } + } + value = value.div(10) + targetBaseLines.forEach((target) => { + const currentLine = target.line + const index = baseLines.findIndex((line) => line === currentLine) + const nextLine = baseLines[(index + 1) % baseLines.length] + const prevLine = baseLines[(index - 1 + baseLines.length) % baseLines.length] + let deltaX = 0 + let deltaY = 0 + if (currentLine.y1 === currentLine.y2) { + deltaY = value.toNumber() + } else { + deltaX = value.toNumber() + } + + currentLine.set({ + x1: currentLine.x1 + deltaX, + y1: currentLine.y1 + deltaY, + x2: currentLine.x2 + deltaX, + y2: currentLine.y2 + deltaY, + startPoint: { x: currentLine.x1 + deltaX, y: currentLine.y1 + deltaY }, + endPoint: { x: currentLine.x2 + deltaX, y: currentLine.y2 + deltaY }, + }) + + nextLine.set({ + x1: nextLine.x1 + deltaX, + y1: nextLine.y1 + deltaY, + startPoint: { x: nextLine.x1 + deltaX, y: nextLine.y1 + deltaY }, + }) + + prevLine.set({ + x2: prevLine.x2 + deltaX, + y2: prevLine.y2 + deltaY, + endPoint: { x: prevLine.x2 + deltaX, y: prevLine.y2 + deltaY }, + }) + + canvas.renderAll() + }) + + if (CONFIRM_LINE_REF.current !== null) { + canvas.remove(CONFIRM_LINE_REF.current) + CONFIRM_LINE_REF.current = null + canvas.renderAll() + } + if (FOLLOW_LINE_REF.current !== null) { + canvas.remove(FOLLOW_LINE_REF.current) + FOLLOW_LINE_REF.current = null + canvas.renderAll() + } + + roof.drawHelpLine() + initEvent() + closePopup(id) } return { diff --git a/src/hooks/roofcover/useRoofShapeSetting.js b/src/hooks/roofcover/useRoofShapeSetting.js index f1139366..1b4036c0 100644 --- a/src/hooks/roofcover/useRoofShapeSetting.js +++ b/src/hooks/roofcover/useRoofShapeSetting.js @@ -108,6 +108,7 @@ export function useRoofShapeSetting(id) { }) addPitchText(line) + line.bringToFront() } }) canvas.renderAll() diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index e4beea37..788f9d22 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -1770,10 +1770,7 @@ export function useMode() { afterLine.push(line) } }) - wall.lines = afterLine.concat(beforeLine) - wall.baseLines = wall.lines - wall.colorLines = [] //외벽선을 기준으로 polygon을 생성한다. 지붕선의 기준이 됨. const divWallLines = [] @@ -1905,55 +1902,21 @@ export function useMode() { const y2 = Big(line.y2) const lineLength = x1.minus(x2).abs().pow(2).plus(y1.minus(y2).abs().pow(2)).sqrt().times(10).round().toNumber() line.attributes.roofId = roof.id + line.attributes.wallId = wall.id // line.attributes.currentRoofId = roof.lines[index].id line.attributes.planeSize = lineLength line.attributes.actualSize = lineLength - let wallStroke, wallStrokeWidth - switch (line.attributes.type) { - case LINE_TYPE.WALLLINE.EAVES: - wallStroke = '#45CD7D' - wallStrokeWidth = 4 - break - case LINE_TYPE.WALLLINE.HIPANDGABLE: - wallStroke = '#45CD7D' - wallStrokeWidth = 4 - break - case LINE_TYPE.WALLLINE.GABLE: - wallStroke = '#3FBAE6' - wallStrokeWidth = 4 - break - case LINE_TYPE.WALLLINE.JERKINHEAD: - wallStroke = '#3FBAE6' - wallStrokeWidth = 4 - break - case LINE_TYPE.WALLLINE.SHED: - wallStroke = '#000000' - wallStrokeWidth = 4 - break - case LINE_TYPE.WALLLINE.WALL: - wallStroke = '#000000' - wallStrokeWidth = 4 - break - } - - //외벽선의 색깔 표시를 위해 라인을 추가한다. - const wallLine = new fabric.Line([line.x1, line.y1, line.x2, line.y2], { - parentId: wall.id, - name: 'wallLine', - attributes: { - wallId: wall.id, - roofId: roof.id, - type: line.attributes.type, - currentRoofId: line.attributes.currentRoofId, - currentWall: line.id, - }, - stroke: wallStroke, - strokeWidth: wallStrokeWidth, - selectable: false, + const baseLine = new fabric.Line([line.x1, line.y1, line.x2, line.y2], { + visible: false, + attributes: line.attributes, + startPoint: line.startPoint, + endPoint: line.endPoint, + parentId: roof.id, + name: 'baseLine', }) - wall.colorLines.push(wallLine) - canvas.add(wallLine) + roof.wall.baseLines.push(baseLine) + canvas.add(baseLine) }) setRoof(roof) diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index 52551cdd..17b97840 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -740,7 +740,9 @@ export const drawShedRoof = (roofId, canvas, textMode) => { */ export const drawRidgeRoof = (roofId, canvas, textMode) => { const roof = canvas?.getObjects().find((object) => object.id === roofId) + const wall = canvas.getObjects().find((object) => object.name === POLYGON_TYPE.WALL && object.attributes.roofId === roofId) + console.log('wall :', wall) //Math.abs(line.x1 - line.x2) > 1 && Math.abs(line.y1 - line.y2) > 1 const hasNonParallelLines = roof.lines.filter((line) => Big(line.x1).minus(Big(line.x2)).gt(1) && Big(line.y1).minus(Big(line.y2)).gt(1)) if (hasNonParallelLines.length > 0) { @@ -757,10 +759,13 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { /** * 외벽선 */ - const baseLines = roof.wall.baseLines + + const baseLines = wall.baseLines + const wallLines = wall.lines + console.log('wallLines :', wallLines) const baseLinePoints = baseLines.map((line) => ({ x: line.x1, y: line.y1 })) - console.log('지붕 마루 갯수 확인 : ', getMaxRidge(baseLines.length)) + console.log('지붕 마루 갯수 확인 : ', getMaxRidge(baseLines.length), baseLinePoints) /** * baseLine을 기준으로 확인용 polygon 작성 @@ -801,6 +806,8 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { if (eavesType.includes(nextLine.attributes?.type)) { const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint) const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint) + + console.log('prevAngle :', prevAngle, 'nextAngle :', nextAngle) /** * 이전, 다음 라인이 처마일때 라인의 방향이 반대면 ㄷ 모양으로 판단한다. */ @@ -1579,6 +1586,7 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { }) }) + console.log('drawEavesSecondLines : ', drawEavesSecondLines) /** ㄴ 모양 처마에 추녀마루를 그린다. */ drawEavesSecondLines.forEach((current) => { const { currentLine, prevLine, nextLine } = current @@ -1715,25 +1723,27 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { { vertex1: { x: x1, y: y1 }, vertex2: prevEndPoint }, { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }, ) - /*const checkLine = new fabric.Line([x1, y1, prevEndPoint.x, prevEndPoint.y], { + const checkLine = new fabric.Line([x1, y1, prevEndPoint.x, prevEndPoint.y], { stroke: 'red', strokeWidth: 2, parentId: roof.id, + name: 'checkLine', }) canvas.add(checkLine) - canvas.renderAll()*/ + canvas.renderAll() - /* if (intersection) { + if (intersection) { const intersectCircle = new fabric.Circle({ left: intersection.x - 2, top: intersection.y - 2, radius: 4, fill: 'red', parentId: roof.id, + name: 'checkPoint', }) canvas.add(intersectCircle) canvas.renderAll() - }*/ + } if (intersection && !intersection.isIntersectionOutside) { intersectRidgeLine.push({ intersection, @@ -1766,13 +1776,14 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { vertex2: prevEndPoint, } - /*const checkLine = new fabric.Line([hipEdge.vertex1.x, hipEdge.vertex1.y, hipEdge.vertex2.x, hipEdge.vertex2.y], { + const checkLine = new fabric.Line([hipEdge.vertex1.x, hipEdge.vertex1.y, hipEdge.vertex2.x, hipEdge.vertex2.y], { stroke: 'yellow', strokeWidth: 2, parentId: roof.id, + name: 'checkLine', }) canvas.add(checkLine) - canvas.renderAll()*/ + canvas.renderAll() let intersectPoints = [] /** 외벽선에서 추녀 마루가 겹치는 경우에 대한 확인*/ @@ -2279,7 +2290,7 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { }) /** hip을 그리기 위한 기본 길이*/ - let hipSize = 0 + let hipSize = Big(0) if (basePoints.length > 0) { basePoints.sort((a, b) => { const aSize = Big(a.x) @@ -2298,18 +2309,21 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { }) const baseHips = baseHipLines.filter((line) => line.x1 === basePoints[0].x && line.y1 === basePoints[0].y) if (baseHips.length > 0) { - hipSize = Big(baseHips[0].line.attributes.planeSize).div(10) + hipSize = Big(baseHips[0].line.attributes.planeSize) } } + console.log('hipSize : ', hipSize.toNumber()) if (hipSize.eq(0)) { const ridge = current.line - basePoints = baseHipLines.filter((line) => (line.x2 === ridge.x1 && line.y2 === ridge.y1) || (line.x2 === ridge.x2 && line.y2 === ridge.y2)) + basePoints = baseHipLines + .filter((line) => (line.x2 === ridge.x1 && line.y2 === ridge.y1) || (line.x2 === ridge.x2 && line.y2 === ridge.y2)) + .filter((line) => baseLines.filter((baseLine) => baseLine.x1 === line.x1 && baseLine.y1 === line.y1).length > 0) basePoints.sort((a, b) => a.line.attributes.planeSize - b.line.attributes.planeSize) - hipSize = Big(basePoints[0].line.attributes.planeSize).div(10) + hipSize = Big(basePoints[0].line.attributes.planeSize) } - hipSize = hipSize.times(hipSize).div(2).sqrt().round().toNumber() + hipSize = hipSize.pow(2).div(2).sqrt().round().div(10).toNumber() /** 현재 라인을 기준으로 45, 135, 225, 315 방향을 확인하기 위한 좌표, hip은 45도 방향으로만 그린다. */ const checkEdge45 = { vertex1: { x: current.x, y: current.y }, vertex2: { x: current.x + hipSize, y: current.y - hipSize } } @@ -2346,10 +2360,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { notIntersect315 = false } }) - /** baseLine의 각 좌표와 교차하는 경우로 한정*/ - intersectPoints = intersectPoints.filter((is) => baseLinePoints.filter((point) => point.x === is.x && point.y === is.y).length > 0) - /** baseLine과 교차하는 좌표의 경우 이미 그려진 추녀마루가 존재한다면 제외한다. */ - intersectPoints = intersectPoints.filter((point) => baseHipLines.filter((hip) => hip.x1 === point.x && hip.y1 === point.y).length === 0) /** baseLine과 교차하지 않는 포인트를 추가한다.*/ if (notIntersect45) { intersectPoints.push(checkEdge45.vertex2) @@ -2363,6 +2373,13 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { if (notIntersect315) { intersectPoints.push(checkEdge315.vertex2) } + /** baseLine의 각 좌표와 교차하는 경우로 한정*/ + intersectPoints = intersectPoints.filter((is) => baseLinePoints.filter((point) => point.x === is.x && point.y === is.y).length > 0) + /** baseLine과 교차하는 좌표의 경우 이미 그려진 추녀마루가 존재한다면 제외한다. */ + intersectPoints = intersectPoints.filter((point) => baseHipLines.filter((hip) => hip.x1 === point.x && hip.y1 === point.y).length === 0) + /** 중복제거 */ + intersectPoints = intersectPoints.filter((point, index, self) => index === self.findIndex((p) => p.x === point.x && p.y === point.y)) + intersectPoints.forEach((is) => { const points = [current.x, current.y, is.x, is.y] const hipLine = drawHipLine(points, canvas, roof, textMode, null, prevDegree, currentDegree) @@ -2388,9 +2405,46 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { ) noRidgeHipPoints.forEach((current) => { - const orthogonalPoints = noRidgeHipPoints.filter( - (point) => point !== current && ((current.x2 === point.x2 && current.y2 !== point.y2) || (current.x2 !== point.x2 && current.y2 === point.y2)), - ) + const checkPoint = new fabric.Circle({ + left: current.x2, + top: current.y2, + radius: 4, + fill: 'green', + parentId: roofId, + name: 'checkPoint', + }) + canvas.add(checkPoint) + canvas.renderAll() + console.log('noRidgeHipPoints current : ', current.x1, current.y1, current.x2, current.y2) + const orthogonalPoints = noRidgeHipPoints.filter((point) => { + const checkPoint1 = new fabric.Circle({ + left: point.x2, + top: point.y2, + radius: 4, + fill: 'blue', + parentId: roofId, + name: 'checkPoint', + }) + canvas.add(checkPoint1) + canvas.renderAll() + console.log(' orthogonalPoints : ', point.x1, point.y1, point.x2, point.y2) + console.log( + 'same point :', + point !== current, + 'y point 다름', + current.x2 === point.x2 && current.y2 !== point.y2, + 'x point 다름', + current.x2 !== point.x2 && current.y2 === point.y2, + ) + canvas.remove(checkPoint1) + canvas.renderAll() + if (point !== current && ((current.x2 === point.x2 && current.y2 !== point.y2) || (current.x2 !== point.x2 && current.y2 === point.y2))) { + return true + } + }) + + canvas.remove(checkPoint) + canvas.renderAll() /** 직교 하는 포인트가 존재 할때 마루를 그린다. */ if (orthogonalPoints.length > 0) { @@ -2458,6 +2512,7 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { .filter((line) => (line.x1 === point.x && line.y1 === point.y) || (line.x2 === point.x && line.y2 === point.y)) .forEach((line) => unFinishedLines.push(line)) }) + unFinishedLines.forEach((line) => { const xVector = Math.sign(Big(line.x2).minus(Big(line.x1))) const yVector = Math.sign(Big(line.y2).minus(Big(line.y1))) @@ -2492,52 +2547,223 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } }) - intersectPoints.forEach((intersection) => { - const intersectPoint = intersection.intersection - noRidgeHipPoints.forEach((hipPoint) => { - const angle = calculateAngle({ x: intersectPoint.x, y: intersectPoint.y }, { x: hipPoint.x2, y: hipPoint.y2 }) - if (angle === 0 || angle === 180 || angle === 90 || angle === -90) { - baseRidgeCount = baseRidgeCount + 1 - const ridgeLine = drawRidgeLine([intersectPoint.x, intersectPoint.y, hipPoint.x2, hipPoint.y2], canvas, roof, textMode) - baseRidgeLines.push(ridgeLine) - let baseHipLine = baseHipLines.filter((line) => line === intersection.line)[0] - baseHipLine.x2 = intersectPoint.x - baseHipLines.y2 = intersectPoint.y - intersection.line.x2 = intersectPoint.x - intersection.line.y2 = intersectPoint.y - /** 보조선 라인 조정*/ - const hipLine = intersection.line.line - /** 평면길이 */ - const planeSize = calcLinePlaneSize({ - x1: hipLine.x1, - y1: hipLine.y1, - x2: intersectPoint.x, - y2: intersectPoint.y, + /** 마루를 그릴수 있는지 찾는다. */ + noRidgeHipPoints.forEach((hipPoint) => { + const ridgePoints = [] + intersectPoints + .filter( + (intersectPoint) => + (intersectPoint.intersection.x !== hipPoint.x2 && intersectPoint.intersection.y === hipPoint.y2) || + (intersectPoint.intersection.x === hipPoint.x2 && intersectPoint.intersection.y !== hipPoint.y2), + ) + .forEach((intersectPoint) => { + ridgePoints.push({ + intersection: intersectPoint, + distance: Big(intersectPoint.intersection.x) + .minus(Big(hipPoint.x2)) + .abs() + .pow(2) + .plus(Big(intersectPoint.intersection.y).minus(Big(hipPoint.y2)).abs().pow(2)) + .sqrt() + .round(1) + .toNumber(), }) - /** 실제길이 */ - const actualSize = - prevDegree === currentDegree - ? calcLineActualSize( - { - x1: hipLine.x1, - y1: hipLine.y1, - x2: intersectPoint.x, - y2: intersectPoint.y, - }, - currentDegree, - ) - : 0 - hipLine.set({ x2: intersectPoint.x, y2: intersectPoint.y, attributes: { roofId: roof.id, planeSize, actualSize } }) - hipLine.fire('modified') - intersectPoints = intersectPoints.filter((isp) => isp !== intersection) + }) + ridgePoints.sort((a, b) => a.distance - b.distance) + + if (ridgePoints.length > 0) { + const intersection = ridgePoints[0].intersection + const isPoint = intersection.intersection + const points = [hipPoint.x2, hipPoint.y2, isPoint.x, isPoint.y] + const ridgeLine = drawRidgeLine(points, canvas, roof, textMode) + baseRidgeCount = baseRidgeCount + 1 + baseRidgeLines.push(ridgeLine) + + let baseHipLine = baseHipLines.filter((line) => line === intersection.line)[0] + baseHipLine.x2 = isPoint.x + baseHipLines.y2 = isPoint.y + /** 보조선 라인 조정*/ + const hipLine = intersection.line.line + /** 평면길이 */ + const planeSize = calcLinePlaneSize({ + x1: hipLine.x1, + y1: hipLine.y1, + x2: isPoint.x, + y2: isPoint.y, + }) + /** 실제길이 */ + const actualSize = + prevDegree === currentDegree + ? calcLineActualSize( + { + x1: hipLine.x1, + y1: hipLine.y1, + x2: isPoint.x, + y2: isPoint.y, + }, + currentDegree, + ) + : 0 + hipLine.set({ + x2: isPoint.x, + y2: isPoint.y, + attributes: { roofId: roof.id, planeSize, actualSize }, + }) + hipLine.fire('modified') + intersectPoints = intersectPoints.filter((isp) => isp !== intersection) + noRidgeHipPoints = noRidgeHipPoints.filter((hip) => hip !== hipPoint) + } else { + const linePoints = [] + intersectPoints.forEach((intersectPoint) => { + const intersection = intersectPoint.intersection + const xVector = Math.sign(Big(intersection.x).minus(Big(hipPoint.x2))) + const yVector = Math.sign(Big(intersection.y).minus(Big(hipPoint.y2))) + + const checkEdge = { + vertex1: { x: intersection.x, y: intersection.y }, + vertex2: { x: Big(intersection.x).plus(Big(xVector).times(10)).toNumber(), y: Big(intersection.y).plus(Big(yVector).times(10)).toNumber() }, + } + const intersectX = edgesIntersection( + { + vertex1: { x: hipPoint.x2, y: hipPoint.y2 }, + vertex2: { x: Big(hipPoint.x2).plus(Big(xVector).neg().times(10)).toNumber(), y: hipPoint.y2 }, + }, + checkEdge, + ) + const intersectY = edgesIntersection( + { + vertex1: { x: hipPoint.x2, y: hipPoint.y2 }, + vertex2: { x: hipPoint.x2, y: Big(hipPoint.y2).plus(Big(yVector).neg().times(10)).toNumber() }, + }, + checkEdge, + ) + + let distanceX = Infinity, + distanceY = Infinity + + if (intersectX) { + distanceX = Big(intersectX.x) + .minus(Big(intersection.x)) + .abs() + .pow(2) + .plus(Big(intersectX.y).minus(Big(intersection.y)).abs().pow(2)) + .sqrt() + .round(1) + .toNumber() + } + if (intersectY) { + distanceY = Big(intersectY.x) + .minus(Big(intersection.x)) + .abs() + .pow(2) + .plus(Big(intersectY.y).minus(Big(intersection.y)).abs().pow(2)) + .sqrt() + .round(1) + .toNumber() + } + + if (distanceX < distanceY) { + linePoints.push({ intersection: intersectX, intersectPoint }) + } else { + linePoints.push({ intersection: intersectY, intersectPoint }) + } + }) + + const linePoint = linePoints.reduce((prev, current) => { + const prevDistance = Big(prev.intersection.x) + .minus(Big(hipPoint.x2)) + .abs() + .pow(2) + .plus(Big(prev.intersection.y).minus(Big(hipPoint.y2)).abs().pow(2)) + .sqrt() + const currentDistance = Big(current.intersection.x) + .minus(Big(hipPoint.x2)) + .abs() + .pow(2) + .plus(Big(current.intersection.y).minus(Big(hipPoint.y2)).abs().pow(2)) + .sqrt() + if (prevDistance < currentDistance) { + return prev + } else { + return current + } + }, linePoints[0]) + + if (!linePoint) return + const hipStartPoint = [hipPoint.x2, hipPoint.y2, linePoint.intersection.x, linePoint.intersection.y] + console.log('hipStartPoint : ', hipStartPoint) + /** 직선인 경우 마루를 그린다.*/ + if ( + (hipStartPoint[0] === hipStartPoint[2] && hipStartPoint[1] !== hipStartPoint[3]) || + (hipStartPoint[0] !== hipStartPoint[2] && hipStartPoint[1] === hipStartPoint[3]) + ) { + console.log('릿지1') + const ridgeLine = drawRidgeLine(hipStartPoint, canvas, roof, textMode) + baseRidgeCount = baseRidgeCount + 1 + baseRidgeLines.push(ridgeLine) noRidgeHipPoints = noRidgeHipPoints.filter((hip) => hip !== hipPoint) } - }) + console.log( + Big(hipStartPoint[0]).minus(Big(hipStartPoint[2])).abs().toNumber(), + Big(hipStartPoint[1]).minus(Big(hipStartPoint[3])).abs().toNumber(), + Big(hipStartPoint[0]) + .minus(Big(hipStartPoint[2])) + .abs() + .minus(Big(hipStartPoint[1]).minus(Big(hipStartPoint[3])).abs()) + .abs() + .lt(1), + ) + /** 대각선인경우 hip을 그린다. */ + if ( + Big(hipStartPoint[0]) + .minus(Big(hipStartPoint[2])) + .abs() + .minus(Big(hipStartPoint[1]).minus(Big(hipStartPoint[3])).abs()) + .abs() + .lt(1) + ) { + console.log('힙1') + const hipLine = drawHipLine(hipStartPoint, canvas, roof, textMode, null, prevDegree, currentDegree) + baseHipLines.push({ x1: hipStartPoint[0], y1: hipStartPoint[1], x2: hipStartPoint[2], y2: hipStartPoint[3], line: hipLine }) + noRidgeHipPoints = noRidgeHipPoints.filter((hip) => hip !== hipPoint) + } + + const isStartPoint = [ + linePoint.intersection.x, + linePoint.intersection.y, + linePoint.intersectPoint.intersection.x, + linePoint.intersectPoint.intersection.y, + ] + if ( + (isStartPoint[0] === isStartPoint[2] && isStartPoint[1] !== isStartPoint[3]) || + (isStartPoint[0] !== isStartPoint[2] && isStartPoint[1] === isStartPoint[3]) + ) { + const ridgeLine = drawRidgeLine(isStartPoint, canvas, roof, textMode) + baseRidgeCount = baseRidgeCount + 1 + baseRidgeLines.push(ridgeLine) + } + + console.log('isStartPoint : ', isStartPoint) + console.log(Big(isStartPoint[0]).minus(Big(isStartPoint[2])).toNumber()) + if ( + Big(isStartPoint[0]) + .minus(Big(isStartPoint[2])) + .abs() + .minus(Big(isStartPoint[1]).minus(Big(isStartPoint[3])).abs()) + .abs() + .lt(1) + ) { + const hipLine = drawHipLine(isStartPoint, canvas, roof, textMode, null, prevDegree, currentDegree) + baseHipLines.push({ x1: isStartPoint[0], y1: isStartPoint[1], x2: isStartPoint[2], y2: isStartPoint[3], line: hipLine }) + } + } }) const ridgeAllPoints = [] baseRidgeLines.forEach((line) => ridgeAllPoints.push({ x: line.x1, y: line.y1 }, { x: line.x2, y: line.y2 })) + console.log('ridge count', baseRidgeCount, getMaxRidge(baseLines.length)) + ridgeAllPoints.forEach((current) => { ridgeAllPoints .filter((point) => point !== current) @@ -2551,12 +2777,9 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { checkRidgeLine = { x1: current.x, y1: current.y, x2: point.x, y2: point.y } } /** 대각선인 경우 hip확인*/ - if ( - Big(point.x) - .minus(Big(current.x)) - .abs() - .eq(Big(point.y).minus(Big(current.y)).abs()) - ) { + const hipX = Big(current.x).minus(Big(point.x)).abs() + const hipY = Big(current.y).minus(Big(point.y)).abs() + if (hipX.eq(hipY) && hipX.gt(0) && hipY.gt(0)) { checkHipLine = { x1: current.x, y1: current.y, x2: point.x, y2: point.y } } From 46f46d734ae861475081768e43c04c485ee7bca9 Mon Sep 17 00:00:00 2001 From: Jaeyoung Lee Date: Mon, 7 Apr 2025 10:05:55 +0900 Subject: [PATCH 08/46] =?UTF-8?q?=EC=A7=80=EB=B6=95=EB=8D=AE=EA=B0=9C=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=20=EA=B4=80=EB=A0=A8=20=EC=9E=91=EC=97=85?= =?UTF-8?q?=EC=A4=91=20(=EB=A7=88=EB=A3=A8=EC=A7=80=EB=B6=95=201=EC=B0=A8?= =?UTF-8?q?=20=EC=A0=95=EB=A6=AC)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/roofcover/useMovementSetting.js | 15 +- src/hooks/useMode.js | 1 + src/util/qpolygon-utils.js | 468 +++++++++++++++++----- 3 files changed, 389 insertions(+), 95 deletions(-) diff --git a/src/hooks/roofcover/useMovementSetting.js b/src/hooks/roofcover/useMovementSetting.js index 558113ff..fc667ec3 100644 --- a/src/hooks/roofcover/useMovementSetting.js +++ b/src/hooks/roofcover/useMovementSetting.js @@ -7,6 +7,7 @@ import { useEvent } from '@/hooks/useEvent' import { useSwal } from '@/hooks/useSwal' import { LINE_TYPE, POLYGON_TYPE } from '@/common/common' import Big from 'big.js' +import { calcLinePlaneSize } from '@/util/qpolygon-utils' //동선이동 형 올림 내림 export function useMovementSetting(id) { @@ -107,6 +108,7 @@ export function useMovementSetting(id) { addCanvasMouseEventListener('mouse:move', mouseMoveEvent) addCanvasMouseEventListener('mouse:down', mouseDownEvent) return () => { + canvas.discardActiveObject() if (FOLLOW_LINE_REF.current != null) { canvas.remove(FOLLOW_LINE_REF.current) FOLLOW_LINE_REF.current = null @@ -366,7 +368,7 @@ export function useMovementSetting(id) { if (target.y1 === target.y2) { checkPoint = { x: midX.toNumber(), y: midY.plus(10).times(value.s).toNumber() } } else { - checkPoint = { x: midX.plus(10).toNumber().times(value.s), y: midY.toNumber() } + checkPoint = { x: midX.plus(10).times(value.s).toNumber(), y: midY.toNumber() } } const inPolygon = wall.inPolygon(checkPoint) @@ -400,18 +402,29 @@ export function useMovementSetting(id) { startPoint: { x: currentLine.x1 + deltaX, y: currentLine.y1 + deltaY }, endPoint: { x: currentLine.x2 + deltaX, y: currentLine.y2 + deltaY }, }) + const currentSize = calcLinePlaneSize({ x1: currentLine.x1, y1: currentLine.y1, x2: currentLine.x2, y2: currentLine.y2 }) + currentLine.attributes.planeSize = currentSize + currentLine.attributes.actualSize = currentSize + const nextOldActualSize = nextLine.attributes.planeSize nextLine.set({ x1: nextLine.x1 + deltaX, y1: nextLine.y1 + deltaY, startPoint: { x: nextLine.x1 + deltaX, y: nextLine.y1 + deltaY }, }) + const nextSize = calcLinePlaneSize({ x1: nextLine.x1, y1: nextLine.y1, x2: nextLine.x2, y2: nextLine.y2 }) + nextLine.attributes.planeSize = nextSize + nextLine.attributes.actualSize = nextSize + const prevOldActualSize = prevLine.attributes.planeSize prevLine.set({ x2: prevLine.x2 + deltaX, y2: prevLine.y2 + deltaY, endPoint: { x: prevLine.x2 + deltaX, y: prevLine.y2 + deltaY }, }) + const prevSize = calcLinePlaneSize({ x1: prevLine.x1, y1: prevLine.y1, x2: prevLine.x2, y2: prevLine.y2 }) + prevLine.attributes.planeSize = prevSize + prevLine.attributes.actualSize = prevSize canvas.renderAll() }) diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index 788f9d22..8615f7ca 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -1915,6 +1915,7 @@ export function useMode() { parentId: roof.id, name: 'baseLine', }) + baseLine.attributes.originPoint = { x1: line.x1, y1: line.y1, x2: line.x2, y2: line.y2 } roof.wall.baseLines.push(baseLine) canvas.add(baseLine) }) diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index 17b97840..07ca86e2 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -756,20 +756,43 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { // const eaves = roof.lines.filter((line) => eavesType.includes(line.attributes?.type)) // const gables = roof.lines.filter((line) => gableType.includes(line.attributes?.type)) - /** - * 외벽선 - */ - - const baseLines = wall.baseLines - const wallLines = wall.lines - console.log('wallLines :', wallLines) + /** 외벽선 */ + const baseLines = wall.baseLines.filter((line) => line.attributes.planeSize > 0) const baseLinePoints = baseLines.map((line) => ({ x: line.x1, y: line.y1 })) - console.log('지붕 마루 갯수 확인 : ', getMaxRidge(baseLines.length), baseLinePoints) - - /** - * baseLine을 기준으로 확인용 polygon 작성 + /** 모양 판단을 위한 라인 처리. + * 평행한 라인이 나누어져 있는 경우 하나의 선으로 판단 한다. */ + const drawBaseLines = [] + baseLines.forEach((currentLine, index) => { + let nextLine = baseLines[(index + 1) % baseLines.length] + let prevLine = baseLines[(index - 1 + baseLines.length) % baseLines.length] + const currentAngle = calculateAngle(currentLine.startPoint, currentLine.endPoint) + const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint) + const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint) + + let { x1, y1, x2, y2 } = currentLine + + if (currentAngle !== prevAngle) { + if (currentAngle === nextAngle) { + let nextIndex = baseLines.findIndex((line) => line === nextLine) + while (nextIndex !== index) { + const checkNextLine = baseLines[(nextIndex + 1 + baseLines.length) % baseLines.length] + const checkAngle = calculateAngle(checkNextLine.startPoint, checkNextLine.endPoint) + if (currentAngle !== checkAngle) { + x2 = checkNextLine.x1 + y2 = checkNextLine.y1 + break + } else { + nextIndex = nextIndex + 1 + } + } + } + drawBaseLines.push({ x1, y1, x2, y2, line: currentLine, size: calcLinePlaneSize({ x1, y1, x2, y2 }) }) + } + }) + + /** baseLine을 기준으로 확인용 polygon 작성 */ const checkWallPolygon = new QPolygon(baseLinePoints, {}) /** @@ -790,23 +813,24 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const drawEavesFirstLines = [] const drawEavesSecondLines = [] - /** - * 모양을 판단하여 그린다. - */ - baseLines.forEach((currentLine, index) => { - /** - * 현재 라인이 처마유형일 경우 - */ + /** 모양을 판단한다. */ + drawBaseLines.forEach((currentBaseLine, index) => { + let prevBaseLine = drawBaseLines[(index - 1 + drawBaseLines.length) % drawBaseLines.length] + let nextBaseLine = drawBaseLines[(index + 1) % drawBaseLines.length] + console.log('currentLine :', currentBaseLine) + console.log('prevLine :', prevBaseLine) + console.log('nextLine :', nextBaseLine) + const currentLine = currentBaseLine.line + const prevLine = prevBaseLine.line + const nextLine = nextBaseLine.line + /** 현재 라인이 처마유형일 경우 */ if (eavesType.includes(currentLine.attributes?.type)) { - const nextLine = baseLines[(index + 1) % baseLines.length] - const prevLine = baseLines[(index - 1 + baseLines.length) % baseLines.length] - /** - * 현재 라인이 처마인 경우 - */ if (eavesType.includes(nextLine.attributes?.type)) { + /** + * 현재 라인이 처마인 경우 + */ const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint) const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint) - console.log('prevAngle :', prevAngle, 'nextAngle :', nextAngle) /** * 이전, 다음 라인이 처마일때 라인의 방향이 반대면 ㄷ 모양으로 판단한다. @@ -831,26 +855,24 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { y: currentMidY.plus(checkScale.times(Math.sign(yVector.toNumber()))).toNumber(), } - // const checkMidLine = new fabric.Line([currentMidX, currentMidY, checkPoints.x, checkPoints.y], { - // stroke: 'blue', - // strokeWidth: 2, - // }) - // canvas.add(checkMidLine) - // canvas.renderAll() - if (checkWallPolygon.inPolygon(checkPoints)) { - drawEavesFirstLines.push({ currentLine, prevLine, nextLine, size: currentLine.attributes.planeSize }) + drawEavesFirstLines.push({ currentBaseLine, prevBaseLine, nextBaseLine }) } else { - drawEavesSecondLines.push({ currentLine, prevLine, nextLine, size: currentLine.attributes.planeSize }) + drawEavesSecondLines.push({ currentBaseLine, prevBaseLine, nextBaseLine }) } } else { - drawEavesSecondLines.push({ currentLine, prevLine, nextLine, size: currentLine.attributes.planeSize }) + drawEavesSecondLines.push({ currentBaseLine, prevBaseLine, nextBaseLine }) } } } }) - drawEavesFirstLines.sort((a, b) => a.size - b.size) + drawEavesFirstLines.sort((a, b) => a.currentBaseLine.size - b.currentBaseLine.size) + console.log('drawEavesFirstLines :', drawEavesFirstLines) + console.log( + 'drawEavesFirstLines :', + drawEavesFirstLines.map((line) => line.currentBaseLine.size), + ) /** 추녀마루 */ let baseHipLines = [] @@ -862,14 +884,11 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { /** ⨆ 모양 처마에 추녀마루를 그린다. */ drawEavesFirstLines.forEach((current) => { // 확인용 라인, 포인트 제거 - /*canvas - .getObjects() - .filter((obj) => obj.name === 'checkPoint' || obj.name === 'checkLine') - .forEach((obj) => canvas.remove(obj)) - canvas.renderAll() -*/ - const { currentLine, prevLine, nextLine } = current - let { x1, x2, y1, y2 } = currentLine + const { currentBaseLine, prevBaseLine, nextBaseLine } = current + const currentLine = currentBaseLine.line + const prevLine = prevBaseLine.line + const nextLine = nextBaseLine.line + let { x1, x2, y1, y2, size } = currentBaseLine let beforePrevLine, afterNextLine /** 이전 라인의 경사 */ @@ -878,12 +897,12 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const currentDegree = currentLine.attributes.pitch > 0 ? getDegreeByChon(currentLine.attributes.pitch) : currentLine.attributes.degree /** 이전 라인의 전라인, 다음 라인의 다음라인을 찾는다 */ - baseLines.forEach((line, index) => { - if (line === prevLine) { - beforePrevLine = baseLines[(index - 1 + baseLines.length) % baseLines.length] + drawBaseLines.forEach((line, index) => { + if (line === prevBaseLine) { + beforePrevLine = drawBaseLines[(index - 1 + drawBaseLines.length) % drawBaseLines.length] } - if (line === nextLine) { - afterNextLine = baseLines[(index + 1) % baseLines.length] + if (line === nextBaseLine) { + afterNextLine = drawBaseLines[(index + 1) % drawBaseLines.length] } }) @@ -898,16 +917,11 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const currentAngle = calculateAngle(currentLine.startPoint, currentLine.endPoint) const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint) const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint) - const beforePrevAngle = calculateAngle(beforePrevLine.startPoint, beforePrevLine.endPoint) - const afterNextAngle = calculateAngle(afterNextLine.startPoint, afterNextLine.endPoint) + const beforePrevAngle = calculateAngle(beforePrevLine.line.startPoint, beforePrevLine.line.endPoint) + const afterNextAngle = calculateAngle(afterNextLine.line.startPoint, afterNextLine.line.endPoint) /** 현재 라인의 길이 추녀마루 길이의 기준이 된다. */ - let currentSize = Big(x2) - .minus(Big(x1)) - .plus(Big(y2).minus(Big(y1))) - .abs() - - // console.log('currentLine : ', currentLine.attributes.planeSize) + let currentSize = Big(size).div(10) /** 이전 라인과의 사이 추녀마루의 각도를 확인한다, 각도가 지붕안쪽으로 향하지 않을때 반대로 처리한다.*/ const prevCheckPoint = { @@ -934,7 +948,7 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { * 현재 라인에서 2번째 전 라인과 2번째 후 라인의 각도가 같을때 -_- 와 같은 형태로 판단하고 * 맞은 편 외벽선까지의 거리를 확인후 currentSize 를 조정한다. */ - if (currentAngle === beforePrevAngle && currentAngle === afterNextAngle) { + if (beforePrevLine === afterNextLine || (currentAngle === beforePrevAngle && currentAngle === afterNextAngle)) { const xVector = Big(nextLine.x2).minus(Big(nextLine.x1)) const yVector = Big(nextLine.y2).minus(Big(nextLine.y1)) const currentMidX = Big(x1).plus(Big(x2)).div(2) @@ -970,6 +984,8 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { if (currentAngle !== beforePrevAngle && currentAngle !== afterNextAngle && beforePrevLine !== afterNextLine) { console.log('4각 아님') // console.log('currentLine : ', currentLine.attributes.planeSize) + // console.log('beforePrevLine : ', beforePrevLine) + // console.log('afterNextLine : ', afterNextLine) const beforePrevX1 = beforePrevLine.x1, beforePrevY1 = beforePrevLine.y1, beforePrevX2 = beforePrevLine.x2, @@ -988,6 +1004,7 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { ).length > 0 /** 6각 */ + // console.log('isConnect :', isConnect) if (isConnect) { const checkScale = currentSize.pow(2).plus(currentSize.pow(2)).sqrt() const intersectBaseLine = [] @@ -1493,10 +1510,11 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { // console.log('startPoint : ', startPoint, 'currentSize : ', currentSize.toNumber()) /** 이전, 다음 라인중 길이가 짧은 길이*/ - const lineMinSize = + /*const lineMinSize = prevLine.attributes.planeSize < nextLine.attributes.planeSize ? Big(prevLine.attributes.planeSize).div(10) - : Big(nextLine.attributes.planeSize).div(10) + : Big(nextLine.attributes.planeSize).div(10)*/ + const lineMinSize = prevBaseLine.size < nextBaseLine.size ? Big(prevBaseLine.size).div(10) : Big(nextBaseLine.size).div(10) /** 마루의 길이는 이전 다음 라인중 짧은것의 길이와 현재라인부터 맞은편 라인까지의 길이에서 현재 라인의 길이를 뺀 것중 짧은 길이 */ const ridgeSize = Big(Math.min(oppositeSize, lineMinSize)) @@ -1505,7 +1523,7 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { if (ridgeSize.gt(0)) { baseRidgeCount = baseRidgeCount + 1 - console.log('baseRidgeCount : ', baseRidgeCount) + // console.log('baseRidgeCount : ', baseRidgeCount) const points = [ startPoint.x, startPoint.y, @@ -1559,6 +1577,12 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { nextHipLine.fire('modified') } } + + canvas + .getObjects() + .filter((obj) => obj.name === 'checkPoint' || obj.name === 'checkLine') + .forEach((obj) => canvas.remove(obj)) + canvas.renderAll() }) /** 중복제거 */ @@ -1586,12 +1610,14 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { }) }) - console.log('drawEavesSecondLines : ', drawEavesSecondLines) + // console.log('drawEavesSecondLines : ', drawEavesSecondLines) /** ㄴ 모양 처마에 추녀마루를 그린다. */ drawEavesSecondLines.forEach((current) => { - const { currentLine, prevLine, nextLine } = current - let { x1, x2, y1, y2 } = currentLine - let beforePrevLine, afterNextLine + const { currentBaseLine, prevBaseLine, nextBaseLine } = current + const currentLine = currentBaseLine.line + const prevLine = prevBaseLine.line + const nextLine = nextBaseLine.line + let { x1, x2, y1, y2, size } = currentBaseLine /** 이전 라인의 경사 */ const prevDegree = prevLine.attributes.pitch > 0 ? getDegreeByChon(prevLine.attributes.pitch) : prevLine.attributes.degree @@ -1723,16 +1749,16 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { { vertex1: { x: x1, y: y1 }, vertex2: prevEndPoint }, { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }, ) - const checkLine = new fabric.Line([x1, y1, prevEndPoint.x, prevEndPoint.y], { + /*const checkLine = new fabric.Line([x1, y1, prevEndPoint.x, prevEndPoint.y], { stroke: 'red', strokeWidth: 2, parentId: roof.id, name: 'checkLine', }) canvas.add(checkLine) - canvas.renderAll() + canvas.renderAll()*/ - if (intersection) { + /* if (intersection) { const intersectCircle = new fabric.Circle({ left: intersection.x - 2, top: intersection.y - 2, @@ -1743,7 +1769,7 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { }) canvas.add(intersectCircle) canvas.renderAll() - } + }*/ if (intersection && !intersection.isIntersectionOutside) { intersectRidgeLine.push({ intersection, @@ -1776,14 +1802,14 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { vertex2: prevEndPoint, } - const checkLine = new fabric.Line([hipEdge.vertex1.x, hipEdge.vertex1.y, hipEdge.vertex2.x, hipEdge.vertex2.y], { + /* const checkLine = new fabric.Line([hipEdge.vertex1.x, hipEdge.vertex1.y, hipEdge.vertex2.x, hipEdge.vertex2.y], { stroke: 'yellow', strokeWidth: 2, parentId: roof.id, name: 'checkLine', }) canvas.add(checkLine) - canvas.renderAll() + canvas.renderAll()*/ let intersectPoints = [] /** 외벽선에서 추녀 마루가 겹치는 경우에 대한 확인*/ @@ -2313,7 +2339,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } } - console.log('hipSize : ', hipSize.toNumber()) if (hipSize.eq(0)) { const ridge = current.line basePoints = baseHipLines @@ -2324,6 +2349,7 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } hipSize = hipSize.pow(2).div(2).sqrt().round().div(10).toNumber() + // console.log('hipSize : ', hipSize.toNumber()) /** 현재 라인을 기준으로 45, 135, 225, 315 방향을 확인하기 위한 좌표, hip은 45도 방향으로만 그린다. */ const checkEdge45 = { vertex1: { x: current.x, y: current.y }, vertex2: { x: current.x + hipSize, y: current.y - hipSize } } @@ -2382,7 +2408,36 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { intersectPoints.forEach((is) => { const points = [current.x, current.y, is.x, is.y] - const hipLine = drawHipLine(points, canvas, roof, textMode, null, prevDegree, currentDegree) + const pointEdge = { vertex1: { x: points[0], y: points[1] }, vertex2: { x: points[2], y: points[3] } } + const vectorX = Math.sign(Big(points[2]).minus(Big(points[0])).toNumber()) + const vectorY = Math.sign(Big(points[3]).minus(Big(points[1])).toNumber()) + const roofIntersections = [] + roof.lines.forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(pointEdge, lineEdge) + if (intersection) { + const vectorIntersectionX = Math.sign(Big(intersection.x).minus(Big(points[0])).toNumber()) + const vectorIntersectionY = Math.sign(Big(intersection.y).minus(Big(points[1])).toNumber()) + if (vectorIntersectionX === vectorX && vectorIntersectionY === vectorY) { + roofIntersections.push({ + x: intersection.x, + y: intersection.y, + size: calcLinePlaneSize({ x1: points[0], y1: points[1], x2: intersection.x, y2: intersection.y }), + }) + } + } + }) + roofIntersections.sort((a, b) => a.size - b.size) + + const hipLine = drawHipLine( + [points[0], points[1], roofIntersections[0].x, roofIntersections[0].y], + canvas, + roof, + textMode, + null, + prevDegree, + currentDegree, + ) baseHipLines.push({ x1: points[0], y1: points[1], x2: points[2], y2: points[3], line: hipLine }) current.cnt = current.cnt + 1 }) @@ -2405,7 +2460,7 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { ) noRidgeHipPoints.forEach((current) => { - const checkPoint = new fabric.Circle({ + /* const checkPoint = new fabric.Circle({ left: current.x2, top: current.y2, radius: 4, @@ -2415,9 +2470,9 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { }) canvas.add(checkPoint) canvas.renderAll() - console.log('noRidgeHipPoints current : ', current.x1, current.y1, current.x2, current.y2) + console.log('noRidgeHipPoints current : ', current.x1, current.y1, current.x2, current.y2)*/ const orthogonalPoints = noRidgeHipPoints.filter((point) => { - const checkPoint1 = new fabric.Circle({ + /*const checkPoint1 = new fabric.Circle({ left: point.x2, top: point.y2, radius: 4, @@ -2437,14 +2492,14 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { current.x2 !== point.x2 && current.y2 === point.y2, ) canvas.remove(checkPoint1) - canvas.renderAll() + canvas.renderAll()*/ if (point !== current && ((current.x2 === point.x2 && current.y2 !== point.y2) || (current.x2 !== point.x2 && current.y2 === point.y2))) { return true } }) - canvas.remove(checkPoint) - canvas.renderAll() + // canvas.remove(checkPoint) + // canvas.renderAll() /** 직교 하는 포인트가 존재 할때 마루를 그린다. */ if (orthogonalPoints.length > 0) { @@ -2691,28 +2746,28 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { if (!linePoint) return const hipStartPoint = [hipPoint.x2, hipPoint.y2, linePoint.intersection.x, linePoint.intersection.y] - console.log('hipStartPoint : ', hipStartPoint) + // console.log('hipStartPoint : ', hipStartPoint) /** 직선인 경우 마루를 그린다.*/ if ( (hipStartPoint[0] === hipStartPoint[2] && hipStartPoint[1] !== hipStartPoint[3]) || (hipStartPoint[0] !== hipStartPoint[2] && hipStartPoint[1] === hipStartPoint[3]) ) { - console.log('릿지1') + // console.log('릿지1') const ridgeLine = drawRidgeLine(hipStartPoint, canvas, roof, textMode) baseRidgeCount = baseRidgeCount + 1 baseRidgeLines.push(ridgeLine) noRidgeHipPoints = noRidgeHipPoints.filter((hip) => hip !== hipPoint) } - console.log( - Big(hipStartPoint[0]).minus(Big(hipStartPoint[2])).abs().toNumber(), - Big(hipStartPoint[1]).minus(Big(hipStartPoint[3])).abs().toNumber(), - Big(hipStartPoint[0]) - .minus(Big(hipStartPoint[2])) - .abs() - .minus(Big(hipStartPoint[1]).minus(Big(hipStartPoint[3])).abs()) - .abs() - .lt(1), - ) + // console.log( + // Big(hipStartPoint[0]).minus(Big(hipStartPoint[2])).abs().toNumber(), + // Big(hipStartPoint[1]).minus(Big(hipStartPoint[3])).abs().toNumber(), + // Big(hipStartPoint[0]) + // .minus(Big(hipStartPoint[2])) + // .abs() + // .minus(Big(hipStartPoint[1]).minus(Big(hipStartPoint[3])).abs()) + // .abs() + // .lt(1), + // ) /** 대각선인경우 hip을 그린다. */ if ( Big(hipStartPoint[0]) @@ -2722,7 +2777,7 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { .abs() .lt(1) ) { - console.log('힙1') + // console.log('힙1') const hipLine = drawHipLine(hipStartPoint, canvas, roof, textMode, null, prevDegree, currentDegree) baseHipLines.push({ x1: hipStartPoint[0], y1: hipStartPoint[1], x2: hipStartPoint[2], y2: hipStartPoint[3], line: hipLine }) noRidgeHipPoints = noRidgeHipPoints.filter((hip) => hip !== hipPoint) @@ -2743,8 +2798,8 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { baseRidgeLines.push(ridgeLine) } - console.log('isStartPoint : ', isStartPoint) - console.log(Big(isStartPoint[0]).minus(Big(isStartPoint[2])).toNumber()) + // console.log('isStartPoint : ', isStartPoint) + // console.log(Big(isStartPoint[0]).minus(Big(isStartPoint[2])).toNumber()) if ( Big(isStartPoint[0]) .minus(Big(isStartPoint[2])) @@ -2762,7 +2817,227 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const ridgeAllPoints = [] baseRidgeLines.forEach((line) => ridgeAllPoints.push({ x: line.x1, y: line.y1 }, { x: line.x2, y: line.y2 })) - console.log('ridge count', baseRidgeCount, getMaxRidge(baseLines.length)) + // console.log('ridge count', baseRidgeCount, getMaxRidge(baseLines.length)) + + console.log('baseLinePoints : ', baseLinePoints) + /** hip 중에 지붕의 라인과 만나지 않은 선을 확인.*/ + baseHipLines + .filter( + (hip) => baseLinePoints.filter((point) => (point.x === hip.x1 && point.y === hip.y1) || (point.x === hip.x2 && point.y === hip.y2)).length > 0, + ) + .filter((hip) => { + const hipEdge = { vertex1: { x: hip.line.x1, y: hip.line.y1 }, vertex2: { x: hip.line.x2, y: hip.line.y2 } } + const hipVectorX = Math.sign(Big(hip.x1).minus(Big(hip.x2))) + const hipVectorY = Math.sign(Big(hip.y1).minus(Big(hip.y2))) + let isIntersect = false + roof.lines.forEach((line) => { + const edge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(edge, hipEdge) + if (intersection && !intersection.isIntersectionOutside) { + const isVectorX = Math.sign(Big(intersection.x).minus(Big(hip.x2))) + const isVectorY = Math.sign(Big(intersection.y).minus(Big(hip.y2))) + if (isVectorX === hipVectorX && isVectorY === hipVectorY) { + isIntersect = true + } + } + }) + return !isIntersect + }) + .forEach((hip) => { + console.log('hip : ', hip) + const checkLine = new fabric.Line([hip.line.x1, hip.line.y1, hip.line.x2, hip.line.y2], { + stroke: 'red', + strokeWidth: 4, + parentId: roofId, + name: 'checkLine', + }) + canvas.add(checkLine) + canvas.renderAll() + const hipLine = hip.line + if (hipLine) { + const hipVectorX = Big(hipLine.x2).plus(Big(hipLine.attributes.planeSize).times(Big(hipLine.x1).minus(Big(hipLine.x2)).neg().s)) + const hipVectorY = Big(hipLine.y2).plus(Big(hipLine.attributes.planeSize).times(Big(hipLine.y1).minus(Big(hipLine.y2)).neg().s)) + const overlapLineX = roof.lines + .filter((roofLine) => roofLine.x1 !== roofLine.x2 && roofLine.y1 === hipLine.y2 && roofLine.y2 === hipLine.y2) + .filter((roofLine) => { + const minX = Math.min(roofLine.x1, roofLine.x2, hipLine.x2) + const maxX = Math.max(roofLine.x1, roofLine.x2, hipLine.x2) + const checkLineEdge = { vertex1: { x: minX, y: hipLine.y2 }, vertex2: { x: maxX, y: hipLine.y2 } } + let isIntersect = false + baseHipLines.forEach((baseHipLine) => { + const edge = { vertex1: { x: baseHipLine.x1, y: baseHipLine.y1 }, vertex2: { x: baseHipLine.x2, y: baseHipLine.y2 } } + const intersection = edgesIntersection(edge, checkLineEdge) + if (intersection && !intersection.isIntersectionOutside) { + const isVectorX = Math.sign(Big(intersection.x).minus(Big(baseHipLine.x2))) + const isVectorY = Math.sign(Big(intersection.y).minus(Big(baseHipLine.y2))) + if (isVectorX === hipVectorX && isVectorY === hipVectorY) { + isIntersect = true + } + } + }) + baseRidgeLines.forEach((baseRidgeLine) => { + const edge = { vertex1: { x: baseRidgeLine.x1, y: baseRidgeLine.y1 }, vertex2: { x: baseRidgeLine.x2, y: baseRidgeLine.y2 } } + const intersection = edgesIntersection(edge, checkLineEdge) + if (intersection && !intersection.isIntersectionOutside) { + const isVectorX = Math.sign(Big(intersection.x).minus(Big(hipLine.x2))) + const isVectorY = Math.sign(Big(intersection.y).minus(Big(hipLine.y2))) + if (isVectorX === hipVectorX && isVectorY === hipVectorY) { + isIntersect = true + } + } + }) + return !isIntersect + }) + const overlapLineY = roof.lines + .filter((roofLine) => roofLine.y1 !== roofLine.y2 && roofLine.x1 === hipLine.x2 && roofLine.x2 === hipLine.x2) + .filter((roofLine) => { + const minY = Math.min(roofLine.y1, roofLine.y2, hipLine.y2) + const maxY = Math.max(roofLine.y1, roofLine.y2, hipLine.y2) + const checkLineEdge = { vertex1: { x: hipLine.x2, y: minY }, vertex2: { x: hipLine.x2, y: maxY } } + let isIntersect = false + baseHipLines.forEach((baseHipLine) => { + const edge = { vertex1: { x: baseHipLine.x1, y: baseHipLine.y1 }, vertex2: { x: baseHipLine.x2, y: baseHipLine.y2 } } + const intersection = edgesIntersection(edge, checkLineEdge) + if (intersection && !intersection.isIntersectionOutside) { + const isVectorX = Math.sign(Big(intersection.x).minus(Big(hipLine.x2))) + const isVectorY = Math.sign(Big(intersection.y).minus(Big(hipLine.y2))) + if (isVectorX === hipVectorX && isVectorY === hipVectorY) { + isIntersect = true + } + } + }) + baseRidgeLines.forEach((baseRidgeLine) => { + const edge = { vertex1: { x: baseRidgeLine.x1, y: baseRidgeLine.y1 }, vertex2: { x: baseRidgeLine.x2, y: baseRidgeLine.y2 } } + const intersection = edgesIntersection(edge, checkLineEdge) + if (intersection && !intersection.isIntersectionOutside) { + const isVectorX = Math.sign(Big(intersection.x).minus(Big(hipLine.x2))) + const isVectorY = Math.sign(Big(intersection.y).minus(Big(hipLine.y2))) + if (isVectorX === hipVectorX && isVectorY === hipVectorY) { + isIntersect = true + } + } + }) + return !isIntersect + }) + + overlapLineX.reduce((prev, current) => { + const prevDistance = Big(prev.x1) + .minus(Big(hipLine.x2)) + .abs() + .lt(Big(prev.x2).minus(Big(hipLine.x2)).abs()) + ? Big(prev.x1).minus(Big(hipLine.x2)).abs() + : Big(prev.x2).minus(Big(hipLine.x2)).abs() + const currentDistance = Big(current.x1) + .minus(Big(hipLine.x2)) + .abs() + .lt(Big(current.x2).minus(Big(hipLine.x2)).abs()) + ? Big(current.x1).minus(Big(hipLine.x2)).abs() + : Big(current.x2).minus(Big(hipLine.x2)).abs() + + return prevDistance.lt(currentDistance) ? prev : current + }, overlapLineX[0]) + + overlapLineY.reduce((prev, current) => { + const prevDistance = Big(prev.y1) + .minus(Big(hipLine.y2)) + .abs() + .lt(Big(prev.y2).minus(Big(hipLine.y2)).abs()) + ? Big(prev.y1).minus(Big(hipLine.y2)).abs() + : Big(prev.y2).minus(Big(hipLine.y2)).abs() + const currentDistance = Big(current.y1) + .minus(Big(hipLine.y2)) + .abs() + .lt(Big(current.y2).minus(Big(hipLine.y2)).abs()) + ? Big(current.y1).minus(Big(hipLine.y2)).abs() + : Big(current.y2).minus(Big(hipLine.y2)).abs() + return prevDistance.lt(currentDistance) ? prev : current + }, overlapLineY[0]) + + if (overlapLineX.length > 0) { + const overlapLine = overlapLineX[0] + const maxX = Math.max(overlapLine.x1, overlapLine.x2) + const point = [hipLine.x2, hipLine.y2, maxX, hipLine.y2] + const addLine = drawHipLine(point, canvas, roof, textMode, null, prevDegree, currentDegree) + baseHipLines.push({ x1: point[0], y1: point[1], x2: point[2], y2: point[3], line: addLine }) + } + if (overlapLineY.length > 0) { + const overlapLine = overlapLineY[0] + const maxY = Math.max(overlapLine.y1, overlapLine.y2) + const point = [hipLine.x2, hipLine.y2, hipLine.x2, maxY] + const addLine = drawHipLine(point, canvas, roof, textMode, null, prevDegree, currentDegree) + baseHipLines.push({ x1: point[0], y1: point[1], x2: point[2], y2: point[3], line: addLine }) + } + } + + const modifiedBaseLine = baseLines.filter((line) => (hip.x2 === line.x1 && hip.y2 === line.y1) || (hip.x2 === line.x2 && hip.y2 === line.y2)) + console.log('modifiedBaseLine : ', modifiedBaseLine) + if (modifiedBaseLine.length === 0) return + const verticalLine = modifiedBaseLine.find( + (line) => + (hip.x2 === line.attributes.originPoint.x1 && hip.x2 === line.attributes.originPoint.x2) || + (hip.y2 === line.attributes.originPoint.y1 && hip.y2 === line.attributes.originPoint.y2), + ) + const horizonLine = modifiedBaseLine.find((line) => line !== verticalLine) + + console.log('verticalLine', verticalLine) + console.log('horizonLine : ', horizonLine) + const horizonRoof = roof.lines.find((line) => { + const originPoint = horizonLine.attributes.originPoint + if (originPoint.y1 === originPoint.y2) { + return ( + line.y1 === line.y2 && + Math.sign(originPoint.x1 - originPoint.x2) === Math.sign(line.x1 - line.x2) && + Big(originPoint.y1).minus(Big(line.y1)).abs().minus(horizonLine.attributes.offset).abs().lt(1) + ) + } else { + return ( + line.x1 === line.x2 && + Math.sign(originPoint.y1 - originPoint.y2) === Math.sign(line.y1 - line.y2) && + Big(originPoint.x1).minus(Big(line.x1)).abs().minus(horizonLine.attributes.offset).abs().lt(1) + ) + } + }) + + if (horizonRoof) { + let horizonPoint + if (horizonRoof.y1 === horizonRoof.y2) { + const minX = Math.min(horizonRoof.x1, horizonRoof.x2, horizonLine.attributes.originPoint.x1, horizonLine.attributes.originPoint.x2) + const maxX = Math.max(horizonRoof.x1, horizonRoof.x2, horizonLine.attributes.originPoint.x1, horizonLine.attributes.originPoint.x2) + horizonPoint = [minX, horizonRoof.y1, maxX, horizonRoof.y1] + } else { + const minY = Math.min(horizonRoof.y1, horizonRoof.y2, horizonLine.attributes.originPoint.y1, horizonLine.attributes.originPoint.y2) + const maxY = Math.max(horizonRoof.y1, horizonRoof.y2, horizonLine.attributes.originPoint.y1, horizonLine.attributes.originPoint.y2) + horizonPoint = [horizonRoof.x1, minY, horizonRoof.x1, maxY] + } + let addLine + const alreadyHorizonLines = baseHipLines.find( + (hipLine) => + hipLine.x1 === horizonPoint[0] && hipLine.y1 === horizonPoint[1] && hipLine.x2 === horizonPoint[2] && hipLine.y2 === horizonPoint[3], + ) + console.log('alreadyHorizonLines : ', alreadyHorizonLines) + if (!alreadyHorizonLines) { + addLine = drawHipLine(horizonPoint, canvas, roof, textMode, null, prevDegree, currentDegree) + baseHipLines.push({ x1: horizonPoint[0], y1: horizonPoint[1], x2: horizonPoint[2], y2: horizonPoint[3], line: addLine }) + } else { + addLine = alreadyHorizonLines + } + + let verticalPoint + if (addLine.y1 === addLine.y2) { + verticalPoint = [hip.x2, hip.y2, hip.x2, addLine.y1] + } else { + verticalPoint = [hip.x2, hip.y2, addLine.x1, hip.y2] + } + const alreadyVerticalLine = baseHipLines.find( + (hipLine) => + hipLine.x1 === verticalPoint[0] && hipLine.y1 === verticalPoint[1] && hipLine.x2 === verticalPoint[2] && hipLine.y2 === verticalPoint[3], + ) + if (!alreadyVerticalLine) { + addLine = drawHipLine(verticalPoint, canvas, roof, textMode, null, prevDegree, currentDegree) + baseHipLines.push({ x1: verticalPoint[0], y1: verticalPoint[1], x2: verticalPoint[2], y2: verticalPoint[3], line: addLine }) + } + } + }) ridgeAllPoints.forEach((current) => { ridgeAllPoints @@ -2871,6 +3146,11 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { }) }) + /** 중복 제거 */ + baseHipLines.forEach((hipLine) => { + baseHipLines.filter((hipLine2) => hipLine !== hipLine2).forEach((hipLine2) => {}) + }) + roof.innerLines = [...baseRidgeLines, ...baseHipLines.map((line) => line.line)] /** 확인용 라인, 포인트 제거 */ From 95e6f4c0a4cd3aabddd0bd5a6cc20e9ad0d4071a Mon Sep 17 00:00:00 2001 From: Jaeyoung Lee Date: Mon, 28 Apr 2025 18:06:55 +0900 Subject: [PATCH 09/46] =?UTF-8?q?=EB=B0=95=EA=B3=B5=EC=A7=80=EB=B6=95=206?= =?UTF-8?q?=EA=B0=81=20=EB=8C=80=EC=9D=91=20=EC=B6=94=EA=B0=80=20=ED=9B=84?= =?UTF-8?q?=20=EC=A0=95=EB=A6=AC.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/fabric/QPolygon.js | 7 +- src/hooks/roofcover/useMovementSetting.js | 22 +- src/util/qpolygon-utils.js | 1773 ++++++++++++++++++++- 3 files changed, 1731 insertions(+), 71 deletions(-) diff --git a/src/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js index 65fe9d11..fc2ba2ab 100644 --- a/src/components/fabric/QPolygon.js +++ b/src/components/fabric/QPolygon.js @@ -2,7 +2,7 @@ import { fabric } from 'fabric' import { v4 as uuidv4 } from 'uuid' import { QLine } from '@/components/fabric/QLine' import { distanceBetweenPoints, findTopTwoIndexesByDistance, getDirectionByPoint, sortedPointLessEightPoint, sortedPoints } from '@/util/canvas-util' -import { calculateAngle, drawGabledRoof, drawRidgeRoof, drawShedRoof, toGeoJSON } from '@/util/qpolygon-utils' +import { calculateAngle, drawRidgeRoof, drawShedRoof, toGeoJSON } from '@/util/qpolygon-utils' import * as turf from '@turf/turf' import { LINE_TYPE, POLYGON_TYPE } from '@/common/common' import Big from 'big.js' @@ -247,12 +247,13 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { const hasShed = types.includes(LINE_TYPE.WALLLINE.SHED) // A형, B형 박공 지붕 - if ( + /* if ( (gableOdd.every((type) => type === LINE_TYPE.WALLLINE.EAVES) && gableEven.every((type) => gableType.includes(type))) || (gableEven.every((type) => type === LINE_TYPE.WALLLINE.EAVES) && gableOdd.every((type) => gableType.includes(type))) ) { drawGabledRoof(this.id, this.canvas, textMode) - } else if (hasShed) { + } else*/ + if (hasShed) { const sheds = this.lines.filter((line) => line.attributes !== undefined && line.attributes.type === LINE_TYPE.WALLLINE.SHED) const areLinesParallel = function (line1, line2) { const angle1 = calculateAngle(line1.startPoint, line1.endPoint) diff --git a/src/hooks/roofcover/useMovementSetting.js b/src/hooks/roofcover/useMovementSetting.js index fc667ec3..d7680150 100644 --- a/src/hooks/roofcover/useMovementSetting.js +++ b/src/hooks/roofcover/useMovementSetting.js @@ -299,6 +299,17 @@ export function useMovementSetting(id) { } const handleSave = () => { + if (CONFIRM_LINE_REF.current !== null) { + canvas.remove(CONFIRM_LINE_REF.current) + CONFIRM_LINE_REF.current = null + canvas.renderAll() + } + if (FOLLOW_LINE_REF.current !== null) { + canvas.remove(FOLLOW_LINE_REF.current) + FOLLOW_LINE_REF.current = null + canvas.renderAll() + } + const target = selectedObject.current !== null ? selectedObject.current : CONFIRM_LINE_REF.current?.target if (!target) return @@ -429,17 +440,6 @@ export function useMovementSetting(id) { canvas.renderAll() }) - if (CONFIRM_LINE_REF.current !== null) { - canvas.remove(CONFIRM_LINE_REF.current) - CONFIRM_LINE_REF.current = null - canvas.renderAll() - } - if (FOLLOW_LINE_REF.current !== null) { - canvas.remove(FOLLOW_LINE_REF.current) - FOLLOW_LINE_REF.current = null - canvas.renderAll() - } - roof.drawHelpLine() initEvent() closePopup(id) diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index 07ca86e2..9fbfc6a3 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -743,6 +743,7 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const wall = canvas.getObjects().find((object) => object.name === POLYGON_TYPE.WALL && object.attributes.roofId === roofId) console.log('wall :', wall) + console.log('roof.lines :', roof.lines) //Math.abs(line.x1 - line.x2) > 1 && Math.abs(line.y1 - line.y2) > 1 const hasNonParallelLines = roof.lines.filter((line) => Big(line.x1).minus(Big(line.x2)).gt(1) && Big(line.y1).minus(Big(line.y2)).gt(1)) if (hasNonParallelLines.length > 0) { @@ -813,25 +814,40 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const drawEavesFirstLines = [] const drawEavesSecondLines = [] + const drawGableRidgeFirst = [] + const drawGableRidgeSecond = [] + const drawGableFirstLines = [] + const drawGableSecondLines = [] + const drawGablePolygonFirst = [] + const drawGablePolygonSecond = [] /** 모양을 판단한다. */ drawBaseLines.forEach((currentBaseLine, index) => { let prevBaseLine = drawBaseLines[(index - 1 + drawBaseLines.length) % drawBaseLines.length] let nextBaseLine = drawBaseLines[(index + 1) % drawBaseLines.length] - console.log('currentLine :', currentBaseLine) - console.log('prevLine :', prevBaseLine) - console.log('nextLine :', nextBaseLine) + // console.log('currentLine :', currentBaseLine) + // console.log('prevLine :', prevBaseLine) + // console.log('nextLine :', nextBaseLine) const currentLine = currentBaseLine.line const prevLine = prevBaseLine.line const nextLine = nextBaseLine.line + const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint) + const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint) + + const checkScale = Big(10) + const xVector = Big(nextLine.x2).minus(Big(nextLine.x1)) + const yVector = Big(nextLine.y2).minus(Big(nextLine.y1)) + const currentMidX = Big(currentLine.x1).plus(Big(currentLine.x2)).div(2) + const currentMidY = Big(currentLine.y1).plus(Big(currentLine.y2)).div(2) + + const checkPoints = { + x: currentMidX.plus(checkScale.times(Math.sign(xVector.toNumber()))).toNumber(), + y: currentMidY.plus(checkScale.times(Math.sign(yVector.toNumber()))).toNumber(), + } + + // console.log('prevAngle :', prevAngle, 'nextAngle :', nextAngle) /** 현재 라인이 처마유형일 경우 */ if (eavesType.includes(currentLine.attributes?.type)) { if (eavesType.includes(nextLine.attributes?.type)) { - /** - * 현재 라인이 처마인 경우 - */ - const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint) - const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint) - console.log('prevAngle :', prevAngle, 'nextAngle :', nextAngle) /** * 이전, 다음 라인이 처마일때 라인의 방향이 반대면 ㄷ 모양으로 판단한다. */ @@ -840,21 +856,10 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { eavesType.includes(nextLine.attributes?.type) && Big(prevAngle).minus(Big(nextAngle)).abs().eq(180) ) { - const checkScale = Big(10) - const xVector = Big(nextLine.x2).minus(Big(nextLine.x1)) - const yVector = Big(nextLine.y2).minus(Big(nextLine.y1)) - const currentMidX = Big(currentLine.x1).plus(Big(currentLine.x2)).div(2) - const currentMidY = Big(currentLine.y1).plus(Big(currentLine.y2)).div(2) - /** * 다음라인 방향에 포인트를 확인해서 역방향 ㄷ 모양인지 판단한다. * @type {{x: *, y: *}} */ - const checkPoints = { - x: currentMidX.plus(checkScale.times(Math.sign(xVector.toNumber()))).toNumber(), - y: currentMidY.plus(checkScale.times(Math.sign(yVector.toNumber()))).toNumber(), - } - if (checkWallPolygon.inPolygon(checkPoints)) { drawEavesFirstLines.push({ currentBaseLine, prevBaseLine, nextBaseLine }) } else { @@ -863,24 +868,1513 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } else { drawEavesSecondLines.push({ currentBaseLine, prevBaseLine, nextBaseLine }) } + } else if (gableType.includes(nextLine.attributes?.type) && gableType.includes(prevLine.attributes?.type)) { + console.log('currentLine :', currentBaseLine.size, 'prevAngle :', prevAngle, 'nextAngle :', nextAngle) + if (Big(prevAngle).minus(Big(nextAngle)).abs().eq(180)) { + const checkPoints = { + x: currentMidX.plus(checkScale.times(Math.sign(xVector.toNumber()))).toNumber(), + y: currentMidY.plus(checkScale.times(Math.sign(yVector.toNumber()))).toNumber(), + } + + if (checkWallPolygon.inPolygon(checkPoints)) { + drawGablePolygonFirst.push({ currentBaseLine, prevBaseLine, nextBaseLine }) + } + // if (!checkWallPolygon.inPolygon(checkPoints)) { + drawGableRidgeSecond.push({ currentBaseLine, prevBaseLine, nextBaseLine }) + // } + } else { + drawGablePolygonSecond.push({ currentBaseLine, prevBaseLine, nextBaseLine }) + } + } + } + + if (gableType.includes(currentLine.attributes?.type)) { + if (eavesType.includes(nextLine.attributes?.type)) { + if ( + eavesType.includes(prevLine.attributes?.type) && + eavesType.includes(nextLine.attributes?.type) && + Big(prevAngle).minus(Big(nextAngle)).abs().eq(180) + ) { + if (checkWallPolygon.inPolygon(checkPoints)) { + drawGableRidgeFirst.push({ currentBaseLine, prevBaseLine, nextBaseLine }) + drawGableFirstLines.push({ currentBaseLine, prevBaseLine, nextBaseLine }) + } else { + drawGableRidgeSecond.push({ currentBaseLine, prevBaseLine, nextBaseLine }) + } + } else { + drawGableSecondLines.push({ currentBaseLine, prevBaseLine, nextBaseLine }) + } } } }) drawEavesFirstLines.sort((a, b) => a.currentBaseLine.size - b.currentBaseLine.size) - console.log('drawEavesFirstLines :', drawEavesFirstLines) - console.log( - 'drawEavesFirstLines :', - drawEavesFirstLines.map((line) => line.currentBaseLine.size), - ) + drawGableRidgeFirst.sort((a, b) => a.currentBaseLine.size - b.currentBaseLine.size) + drawGableRidgeSecond.sort((a, b) => a.currentBaseLine.size - b.currentBaseLine.size) /** 추녀마루 */ let baseHipLines = [] /** 용마루 */ let baseRidgeLines = [] + /** 박공지붕 마루*/ + let baseGableRidgeLines = [] + + /** 박공지붕 라인*/ + let baseGableLines = [] /** 용마루의 갯수*/ let baseRidgeCount = 0 + // console.log('drawGableFirstLines :', drawGableFirstLines) + // console.log('drawGableSecondLines :', drawGableSecondLines) + console.log('drawEavesFirstLines :', drawEavesFirstLines) + console.log('drawEavesSecondLines :', drawEavesSecondLines) + console.log('drawGableRidgeFirst : ', drawGableRidgeFirst) + console.log('drawGableRidgeSecond : ', drawGableRidgeSecond) + console.log('drawGablePolygonFirst :', drawGablePolygonFirst) + console.log('drawGablePolygonSecond :', drawGablePolygonSecond) + + /** 박공지붕에서 파생되는 마루를 그린다. ㄷ 형태*/ + drawGableRidgeFirst.forEach((current) => { + const { currentBaseLine, prevBaseLine, nextBaseLine } = current + const currentLine = currentBaseLine.line + const prevLine = prevBaseLine.line + const nextLine = nextBaseLine.line + + let { x1, x2, y1, y2, size } = currentBaseLine + let beforePrevBaseLine, afterNextBaseLine + + /** 이전 라인의 경사 */ + const prevDegree = prevLine.attributes.pitch > 0 ? getDegreeByChon(prevLine.attributes.pitch) : prevLine.attributes.degree + /** 다음 라인의 경사 */ + const nextDegree = nextLine.attributes.pitch > 0 ? getDegreeByChon(nextLine.attributes.pitch) : nextLine.attributes.degree + /** 현재 라인의 경사 */ + const currentDegree = currentLine.attributes.pitch > 0 ? getDegreeByChon(currentLine.attributes.pitch) : currentLine.attributes.degree + + /** 이전 라인의 전라인, 다음 라인의 다음라인을 찾는다 */ + drawBaseLines.forEach((line, index) => { + if (line === prevBaseLine) { + beforePrevBaseLine = drawBaseLines[(index - 1 + drawBaseLines.length) % drawBaseLines.length] + } + if (line === nextBaseLine) { + afterNextBaseLine = drawBaseLines[(index + 1) % drawBaseLines.length] + } + }) + + const beforePrevLine = beforePrevBaseLine?.line + const afterNextLine = afterNextBaseLine?.line + + /** 각 라인의 흐름 방향을 확인한다. */ + const currentAngle = calculateAngle(currentLine.startPoint, currentLine.endPoint) + const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint) + const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint) + const beforePrevAngle = calculateAngle(beforePrevLine.startPoint, beforePrevLine.endPoint) + const afterNextAngle = calculateAngle(afterNextLine.startPoint, afterNextLine.endPoint) + + /** 현재라인의 vector*/ + const currentVectorX = Math.sign(Big(x2).minus(Big(x1)).toNumber()) + const currentVectorY = Math.sign(Big(y2).minus(Big(y1)).toNumber()) + + /** 이전라인의 vector*/ + const prevVectorX = Math.sign(Big(prevLine.x2).minus(Big(prevLine.x1))) + const prevVectorY = Math.sign(Big(prevLine.y2).minus(Big(prevLine.y1))) + + /** 다음라인의 vector*/ + const nextVectorX = Math.sign(Big(nextLine.x2).minus(Big(nextLine.x1))) + const nextVectorY = Math.sign(Big(nextLine.y2).minus(Big(nextLine.y1))) + + /** 현재라인의 기준점*/ + const currentMidX = Big(x1).plus(Big(x2)).div(2).plus(Big(prevVectorX).times(currentLine.attributes.offset)) + const currentMidY = Big(y1).plus(Big(y2)).div(2).plus(Big(prevVectorY).times(currentLine.attributes.offset)) + + if (beforePrevBaseLine === afterNextBaseLine) { + const afterNextMidX = Big(afterNextLine.x1).plus(Big(afterNextLine.x2)).div(2) + const afterNextMidY = Big(afterNextLine.y1).plus(Big(afterNextLine.y2)).div(2) + const vectorMidX = Math.sign(currentMidX.minus(afterNextMidX)) + const vectorMidY = Math.sign(currentMidY.minus(afterNextMidY)) + + let oppositeMidX, oppositeMidY + if (eavesType.includes(afterNextLine.attributes?.type)) { + const checkSize = currentMidX + .minus(afterNextMidX) + .pow(2) + .plus(currentMidY.minus(afterNextMidY).pow(2)) + .sqrt() + .minus(Big(afterNextLine.attributes.planeSize).div(20)) + .round(1) + oppositeMidX = currentMidX.plus(checkSize.times(vectorMidX).neg()) + oppositeMidY = currentMidY.plus(checkSize.times(vectorMidY).neg()) + + const xVector1 = Math.sign(Big(oppositeMidX).minus(Big(afterNextLine.x1)).neg().toNumber()) + const yVector1 = Math.sign(Big(oppositeMidY).minus(Big(afterNextLine.y1)).neg().toNumber()) + const xVector2 = Math.sign(Big(oppositeMidX).minus(Big(afterNextLine.x2)).neg().toNumber()) + const yVector2 = Math.sign(Big(oppositeMidY).minus(Big(afterNextLine.y2)).neg().toNumber()) + + let addOppositeX1 = 0, + addOppositeY1 = 0, + addOppositeX2 = 0, + addOppositeY2 = 0 + + if (!checkWallPolygon.inPolygon({ x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() })) { + const checkScale = currentMidX.minus(oppositeMidX).pow(2).plus(currentMidY.minus(oppositeMidY).pow(2)).sqrt() + addOppositeX1 = checkScale.times(xVector1).toNumber() + addOppositeY1 = checkScale.times(yVector1).toNumber() + addOppositeX2 = checkScale.times(xVector2).toNumber() + addOppositeY2 = checkScale.times(yVector2).toNumber() + } + + let scale1 = Big(afterNextLine.attributes.offset).pow(2).plus(Big(nextLine.attributes.offset).pow(2)).sqrt() + scale1 = scale1.eq(0) ? Big(1) : scale1 + let scale2 = Big(afterNextLine.attributes.offset).pow(2).plus(Big(prevLine.attributes.offset).pow(2)).sqrt() + scale2 = scale2.eq(0) ? Big(1) : scale2 + + const checkHip1 = { + x1: Big(afterNextLine.x1).plus(scale1.times(xVector1)).toNumber(), + y1: Big(afterNextLine.y1).plus(scale1.times(yVector1)).toNumber(), + x2: oppositeMidX.plus(addOppositeX1).toNumber(), + y2: oppositeMidY.plus(addOppositeY1).toNumber(), + } + + const checkHip2 = { + x1: Big(afterNextLine.x2).plus(scale2.times(xVector2)).toNumber(), + y1: Big(afterNextLine.y2).plus(scale2.times(yVector2)).toNumber(), + x2: oppositeMidX.plus(addOppositeX2).toNumber(), + y2: oppositeMidY.plus(addOppositeY2).toNumber(), + } + + const intersection1 = findRoofIntersection(roof, checkHip1, { x: oppositeMidX.plus(addOppositeX1), y: oppositeMidY.plus(addOppositeY1) }) + const intersection2 = findRoofIntersection(roof, checkHip2, { x: oppositeMidX.plus(addOppositeX2), y: oppositeMidY.plus(addOppositeY2) }) + + const afterNextDegree = afterNextLine.attributes.pitch > 0 ? getDegreeByChon(afterNextLine.attributes.pitch) : afterNextLine.attributes.degree + + if (intersection1) { + const hipLine = drawHipLine( + [intersection1.intersection.x, intersection1.intersection.y, oppositeMidX.plus(addOppositeX1), oppositeMidY.plus(addOppositeY1)], + canvas, + roof, + textMode, + null, + nextDegree, + afterNextDegree, + ) + baseHipLines.push({ + x1: afterNextLine.x1, + y1: afterNextLine.y1, + x2: oppositeMidX.plus(addOppositeX1).toNumber(), + y2: oppositeMidY.plus(addOppositeY1).toNumber(), + line: hipLine, + }) + } + if (intersection2) { + const hipLine = drawHipLine( + [intersection2.intersection.x, intersection2.intersection.y, oppositeMidX.plus(addOppositeX2), oppositeMidY.plus(addOppositeY2)], + canvas, + roof, + textMode, + null, + prevDegree, + afterNextDegree, + ) + baseHipLines.push({ + x1: afterNextLine.x2, + y1: afterNextLine.y2, + x2: oppositeMidX.plus(addOppositeX2).toNumber(), + y2: oppositeMidY.plus(addOppositeY2).toNumber(), + line: hipLine, + }) + } + } else { + oppositeMidX = Big(afterNextLine.x1).plus(Big(afterNextLine.x2)).div(2).plus(Big(prevVectorX).neg().times(afterNextLine.attributes.offset)) + oppositeMidY = Big(afterNextLine.y1).plus(Big(afterNextLine.y2)).div(2).plus(Big(prevVectorY).neg().times(afterNextLine.attributes.offset)) + } + + const vectorOppositeX = Math.sign(currentMidX.minus(oppositeMidX)) + const vectorOppositeY = Math.sign(currentMidY.minus(oppositeMidY)) + + if (vectorMidX === vectorOppositeX && vectorMidY === vectorOppositeY && baseRidgeCount < getMaxRidge(baseLines.length)) { + const ridge = drawRidgeLine( + [currentMidX.toNumber(), currentMidY.toNumber(), oppositeMidX.toNumber(), oppositeMidY.toNumber()], + canvas, + roof, + textMode, + ) + baseGableRidgeLines.push(ridge) + baseRidgeCount++ + } + } else { + const vectorMidX = Math.sign(Big(nextLine.x2).minus(nextLine.x1)) + const vectorMidY = Math.sign(Big(nextLine.y2).minus(nextLine.y1)) + let oppositeMidX = currentMidX, + oppositeMidY = currentMidY + let prevOppositeMidX, prevOppositeMidY, nextOppositeMidX, nextOppositeMidY + const beforePrevOffset = + currentAngle === beforePrevAngle + ? Big(beforePrevLine.attributes.offset) + : Big(beforePrevLine.attributes.offset).plus(currentLine.attributes.offset) + const afterNextOffset = + currentAngle === afterNextAngle + ? Big(afterNextLine.attributes.offset) + : Big(afterNextLine.attributes.offset).plus(currentLine.attributes.offset) + const prevSize = Big(prevLine.attributes.planeSize).div(10) + const nextSize = Big(nextLine.attributes.planeSize).div(10) + + let prevHipCoords, nextHipCoords + + /** 다음 라인이 그 다음 라인과의 사이에 추녀마루가 존재 하는지 확인. 처마-처마 인 경우 추녀마루*/ + if (eavesType.includes(afterNextLine.attributes?.type)) { + /** 현재 라인의 길이를 기준으로 추녀 마루의 길이를 삼각함수를 사용하여 판단한다.*/ + let hipLength = Big(size).div(10).div(2).pow(2).plus(Big(size).div(10).div(2).pow(2)).sqrt() + + const nextHalfVector = getHalfAngleVector(nextLine, afterNextLine) + let nextHipVector = { x: nextHalfVector.x, y: nextHalfVector.y } + + /** 이전 라인과의 사이 추녀마루의 각도를 확인한다, 각도가 지붕안쪽으로 향하지 않을때 반대로 처리한다.*/ + const nextCheckPoint = { + x: Big(nextLine.x2).plus(Big(nextHalfVector.x).times(10)), + y: Big(nextLine.y2).plus(Big(nextHalfVector.y).times(10)), + } + if (!checkWallPolygon.inPolygon(nextCheckPoint)) { + nextHipVector = { x: Big(nextHipVector.x).neg().toNumber(), y: Big(nextHipVector.y).neg().toNumber() } + } + + const nextEndPoint = { + x: Big(nextLine.x2).plus(Big(nextHipVector.x).times(hipLength)), + y: Big(nextLine.y2).plus(Big(nextHipVector.y).times(hipLength)), + } + + let ridgeEdge = { + vertex1: { x: currentMidX.toNumber(), y: currentMidY.toNumber() }, + vertex2: { + x: currentMidX.plus(Big(nextVectorX).times(nextBaseLine.size)).toNumber(), + y: currentMidY.plus(Big(nextVectorY).times(nextBaseLine.size)).toNumber(), + }, + } + let hipEdge = { vertex1: { x: nextLine.x2, y: nextLine.y2 }, vertex2: { x: nextEndPoint.x, y: nextEndPoint.y } } + let intersection = edgesIntersection(ridgeEdge, hipEdge) + if (intersection) { + nextHipCoords = { x1: nextLine.x2, y1: nextLine.y2, x2: intersection.x, y2: intersection.y } + nextOppositeMidY = Big(intersection.y) + nextOppositeMidX = Big(intersection.x) + } + } else { + if (vectorMidX === 0) { + nextOppositeMidY = currentMidY.plus(nextSize.plus(afterNextOffset).times(vectorMidY)) + nextOppositeMidX = currentMidX + } else { + nextOppositeMidX = currentMidX.plus(nextSize.plus(afterNextOffset).times(vectorMidX)) + nextOppositeMidY = currentMidY + } + } + + /** 이전 라인이 그 이전 라인과의 사이에 추녀마루가 존재 하는지 확인. 처마-처마 인 경우 추녀마루*/ + if (eavesType.includes(beforePrevLine.attributes?.type)) { + /** 현재 라인의 길이를 기준으로 추녀 마루의 길이를 삼각함수를 사용하여 판단한다.*/ + let hipLength = Big(size).div(10).div(2).pow(2).plus(Big(size).div(10).div(2).pow(2)).sqrt() + + const prevHalfVector = getHalfAngleVector(prevLine, beforePrevLine) + let prevHipVector = { x: prevHalfVector.x, y: prevHalfVector.y } + + /** 이전 라인과의 사이 추녀마루의 각도를 확인한다, 각도가 지붕안쪽으로 향하지 않을때 반대로 처리한다.*/ + const prevCheckPoint = { + x: Big(prevLine.x1).plus(Big(prevHalfVector.x).times(10)), + y: Big(prevLine.y1).plus(Big(prevHalfVector.y).times(10)), + } + + if (!checkWallPolygon.inPolygon(prevCheckPoint)) { + prevHipVector = { x: Big(prevHipVector.x).neg().toNumber(), y: Big(prevHipVector.y).neg().toNumber() } + } + + const prevEndPoint = { + x: Big(prevLine.x1).plus(Big(prevHipVector.x).times(hipLength)), + y: Big(prevLine.y1).plus(Big(prevHipVector.y).times(hipLength)), + } + + let ridgeEdge = { + vertex1: { x: currentMidX.toNumber(), y: currentMidY.toNumber() }, + vertex2: { + x: currentMidX.plus(Big(prevVectorX).times(prevBaseLine.size)).toNumber(), + y: currentMidY.plus(Big(prevVectorY).times(prevBaseLine.size)).toNumber(), + }, + } + let hipEdge = { vertex1: { x: prevLine.x1, y: prevLine.y1 }, vertex2: { x: prevEndPoint.x, y: prevEndPoint.y } } + let intersection = edgesIntersection(ridgeEdge, hipEdge) + if (intersection) { + prevHipCoords = { x1: prevLine.x1, y1: prevLine.y1, x2: intersection.x, y2: intersection.y } + prevOppositeMidY = Big(intersection.y) + prevOppositeMidX = Big(intersection.x) + } + } else { + if (vectorMidX === 0) { + prevOppositeMidY = currentMidY.plus(prevSize.plus(beforePrevOffset).times(vectorMidY)) + prevOppositeMidX = currentMidX + } else { + prevOppositeMidX = currentMidX.plus(prevSize.plus(beforePrevOffset).times(vectorMidX)) + prevOppositeMidY = currentMidY + } + } + const checkPrevSize = currentMidX.minus(prevOppositeMidX).pow(2).plus(currentMidY.minus(prevOppositeMidY).pow(2)).sqrt() + const checkNextSize = currentMidX.minus(nextOppositeMidX).pow(2).plus(currentMidY.minus(nextOppositeMidY).pow(2)).sqrt() + + /** 두 포인트 중에 current와 가까운 포인트를 사용*/ + if (checkPrevSize.gt(checkNextSize)) { + if (nextHipCoords) { + let intersectPoints = [] + const hipEdge = { vertex1: { x: nextHipCoords.x2, y: nextHipCoords.y2 }, vertex2: { x: nextHipCoords.x1, y: nextHipCoords.y1 } } + + /** 외벽선에서 라인 겹치는 경우에 대한 확인*/ + roof.lines.forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(hipEdge, lineEdge) + if ( + intersection && + Math.sign(nextHipCoords.x2 - nextHipCoords.x1) === Math.sign(nextHipCoords.x2 - intersection.x) && + Math.sign(nextHipCoords.y2 - nextHipCoords.y1) === Math.sign(nextHipCoords.y2 - intersection.y) + ) { + const intersectEdge = { vertex1: { x: nextHipCoords.x2, y: nextHipCoords.y2 }, vertex2: { x: intersection.x, y: intersection.y } } + const is = edgesIntersection(intersectEdge, lineEdge) + if (!is.isIntersectionOutside) { + const intersectSize = Big(nextHipCoords.x2) + .minus(Big(intersection.x)) + .pow(2) + .plus(Big(nextHipCoords.y2).minus(Big(intersection.y)).pow(2)) + .abs() + .sqrt() + .toNumber() + + intersectPoints.push({ + intersection, + size: intersectSize, + line, + }) + } + } + }) + const intersect = intersectPoints.sort((a, b) => a.size - b.size)[0] + if (intersect) { + const degree = intersect.line.attributes.pitch > 0 ? getDegreeByChon(intersect.line.attributes.pitch) : intersect.line.attributes.degree + const hipLine = drawHipLine( + [intersect.intersection.x, intersect.intersection.y, nextOppositeMidX.toNumber(), nextOppositeMidY.toNumber()], + canvas, + roof, + textMode, + null, + degree, + degree, + ) + baseHipLines.push({ + x1: nextHipCoords.x1, + y1: nextHipCoords.y1, + x2: nextHipCoords.x2, + y2: nextHipCoords.y2, + line: hipLine, + }) + } + } + oppositeMidY = nextOppositeMidY + oppositeMidX = nextOppositeMidX + } else { + if (prevHipCoords) { + let intersectPoints = [] + const hipEdge = { vertex1: { x: prevHipCoords.x2, y: prevHipCoords.y2 }, vertex2: { x: prevHipCoords.x1, y: prevHipCoords.y1 } } + + /** 외벽선에서 라인 겹치는 경우에 대한 확인*/ + roof.lines.forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(hipEdge, lineEdge) + if ( + intersection && + Math.sign(prevHipCoords.x2 - prevHipCoords.x1) === Math.sign(prevHipCoords.x2 - intersection.x) && + Math.sign(prevHipCoords.y2 - prevHipCoords.y1) === Math.sign(prevHipCoords.y2 - intersection.y) + ) { + const intersectEdge = { vertex1: { x: prevHipCoords.x2, y: prevHipCoords.y2 }, vertex2: { x: intersection.x, y: intersection.y } } + const is = edgesIntersection(intersectEdge, lineEdge) + if (!is.isIntersectionOutside) { + const intersectSize = Big(prevHipCoords.x2) + .minus(Big(intersection.x)) + .pow(2) + .plus(Big(prevHipCoords.y2).minus(Big(intersection.y)).pow(2)) + .abs() + .sqrt() + .toNumber() + + intersectPoints.push({ + intersection, + size: intersectSize, + line, + }) + } + } + }) + const intersect = intersectPoints.sort((a, b) => a.size - b.size)[0] + + if (intersect) { + const degree = intersect.line.attributes.pitch > 0 ? getDegreeByChon(intersect.line.attributes.pitch) : intersect.line.attributes.degree + const hipLine = drawHipLine( + [intersect.intersection.x, intersect.intersection.y, prevOppositeMidX.toNumber(), prevOppositeMidY.toNumber()], + canvas, + roof, + textMode, + null, + degree, + degree, + ) + baseHipLines.push({ + x1: prevHipCoords.x1, + y1: prevHipCoords.y1, + x2: prevHipCoords.x2, + y2: prevHipCoords.y2, + line: hipLine, + }) + } + } + oppositeMidY = prevOppositeMidY + oppositeMidX = prevOppositeMidX + } + + if (baseRidgeCount < getMaxRidge(baseLines.length)) { + /** 마루가 맞은편 외벽선에 닿는 경우 해당 부분까지로 한정한다. */ + const ridgeEdge = { + vertex1: { x: currentMidX.toNumber(), y: currentMidY.toNumber() }, + vertex2: { x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() }, + } + const ridgeVectorX = Math.sign(currentMidX.minus(oppositeMidX).toNumber()) + const ridgeVectorY = Math.sign(currentMidY.minus(oppositeMidY).toNumber()) + + roof.lines + .filter((line) => { + const lineVectorX = Math.sign(Big(line.x2).minus(Big(line.x1)).toNumber()) + const lineVectorY = Math.sign(Big(line.y2).minus(Big(line.y1)).toNumber()) + return ( + (lineVectorX === currentVectorX && lineVectorY !== currentVectorY) || (lineVectorX !== currentVectorX && lineVectorY === currentVectorY) + ) + }) + .forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(ridgeEdge, lineEdge) + if (intersection && !intersection.isIntersectionOutside) { + const isVectorX = Math.sign(Big(currentMidX).minus(intersection.x).toNumber()) + const isVectorY = Math.sign(Big(currentMidY).minus(intersection.y).toNumber()) + if (isVectorX === ridgeVectorX && isVectorY === ridgeVectorY) { + oppositeMidX = Big(intersection.x) + oppositeMidY = Big(intersection.y) + } + } + }) + + const ridgeLine = drawRidgeLine([currentMidX, currentMidY, oppositeMidX, oppositeMidY], canvas, roof, textMode) + baseGableRidgeLines.push(ridgeLine) + baseRidgeCount++ + } + } + }) + + /** 박공지붕에서 파생되는 마루를 그린다. 첫번째에서 처리 하지 못한 라인이 있는 경우 */ + drawGableRidgeSecond.forEach((current) => { + const { currentBaseLine, prevBaseLine, nextBaseLine } = current + const currentLine = currentBaseLine.line + const prevLine = prevBaseLine.line + const nextLine = nextBaseLine.line + + let { x1, x2, y1, y2, size } = currentBaseLine + let beforePrevBaseLine, afterNextBaseLine + + /** 이전 라인의 경사 */ + const prevDegree = prevLine.attributes.pitch > 0 ? getDegreeByChon(prevLine.attributes.pitch) : prevLine.attributes.degree + /** 다음 라인의 경사 */ + const nextDegree = nextLine.attributes.pitch > 0 ? getDegreeByChon(nextLine.attributes.pitch) : nextLine.attributes.degree + /** 현재 라인의 경사 */ + const currentDegree = currentLine.attributes.pitch > 0 ? getDegreeByChon(currentLine.attributes.pitch) : currentLine.attributes.degree + + const currentAngle = calculateAngle(currentLine.startPoint, currentLine.endPoint) + const currentVectorX = Big(currentLine.x2).minus(currentLine.x1) + const currentVectorY = Big(currentLine.y2).minus(currentLine.y1) + const checkVectorX = Big(nextLine.x2).minus(Big(nextLine.x1)) + const checkVectorY = Big(nextLine.y2).minus(Big(nextLine.y1)) + const currentMidX = Big(currentLine.x1).plus(Big(currentLine.x2)).div(2) + const currentMidY = Big(currentLine.y1).plus(Big(currentLine.y2)).div(2) + const checkSize = Big(10) + + const checkPoints = { + x: currentMidX.plus(checkSize.times(Math.sign(checkVectorX.toNumber()))).toNumber(), + y: currentMidY.plus(checkSize.times(Math.sign(checkVectorY.toNumber()))).toNumber(), + } + if (!checkWallPolygon.inPolygon(checkPoints)) { + const currentMidEdge = { + vertex1: { x: currentMidX.toNumber(), y: currentMidY.toNumber() }, + vertex2: { + x: currentMidX.plus(checkSize.times(Math.sign(checkVectorX.neg().toNumber()))).toNumber(), + y: currentMidY.plus(checkSize.times(Math.sign(checkVectorY.neg().toNumber()))).toNumber(), + }, + } + + let oppositeLines = [] + baseLines + .filter((line, index) => { + let nextLine = baseLines[(index + 1) % baseLines.length] + let prevLine = baseLines[(index - 1 + baseLines.length) % baseLines.length] + if ( + (gableType.includes(nextLine.attributes.type) && gableType.includes(prevLine.attributes.type)) || + (eavesType.includes(nextLine.attributes.type) && eavesType.includes(prevLine.attributes.type)) + ) { + const angle = calculateAngle(line.startPoint, line.endPoint) + switch (currentAngle) { + case 90: + return angle === -90 + case -90: + return angle === 90 + case 0: + return angle === 180 + case 180: + return angle === 0 + } + } + return false + }) + .forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(currentMidEdge, lineEdge) + if (intersection) { + oppositeLines.push({ + line, + intersection, + size: Big(intersection.x).minus(currentMidX).abs().pow(2).plus(Big(intersection.y).minus(currentMidY).abs().pow(2)).sqrt(), + }) + } + }) + + if (oppositeLines.length === 0) { + return + } + const oppositeLine = oppositeLines.sort((a, b) => a.size - b.size)[0] + + let points = [] + if (eavesType.includes(oppositeLine.line.attributes.type)) { + const oppositeCurrentLine = oppositeLine.line + let oppositePrevLine, oppositeNextLine + baseLines.forEach((line, index) => { + if (line === oppositeCurrentLine) { + oppositePrevLine = baseLines[(index - 1 + baseLines.length) % baseLines.length] + oppositeNextLine = baseLines[(index + 1) % baseLines.length] + } + }) + if (gableType.includes(oppositeNextLine.attributes.type) && gableType.includes(oppositePrevLine.attributes.type)) { + if (currentVectorX.eq(0)) { + const centerX = currentMidX.plus(oppositeLine.intersection.x).div(2).toNumber() + points = [centerX, currentLine.y1, centerX, currentLine.y2] + } else { + const centerY = currentMidY.plus(oppositeLine.intersection.y).div(2).toNumber() + points = [currentLine.x1, centerY, currentLine.x2, centerY] + } + } + if (eavesType.includes(oppositeNextLine.attributes.type) && eavesType.includes(oppositePrevLine.attributes.type)) { + /** 이전, 다음라인의 사잇각의 vector를 구한다. */ + let prevVector = getHalfAngleVector(oppositePrevLine, oppositeCurrentLine) + let nextVector = getHalfAngleVector(oppositeCurrentLine, oppositeNextLine) + + let prevHipVector = { x: Big(prevVector.x), y: Big(prevVector.y) } + let nextHipVector = { x: Big(nextVector.x), y: Big(nextVector.y) } + + /** 이전 라인과의 사이 추녀마루의 각도를 확인한다, 각도가 지붕안쪽으로 향하지 않을때 반대로 처리한다.*/ + const prevCheckPoint = { + x: Big(oppositeCurrentLine.x1).plus(Big(prevHipVector.x).times(10)), + y: Big(oppositeCurrentLine.y1).plus(Big(prevHipVector.y).times(10)), + } + if (!checkWallPolygon.inPolygon(prevCheckPoint)) { + prevHipVector = { x: Big(prevHipVector.x).neg(), y: Big(prevHipVector.y).neg() } + } + + /** 다음 라인과의 사이 추녀마루의 각도를 확인한다, 각도가 지붕안쪽으로 향하지 않을때 반대로 처리한다.*/ + const nextCheckPoint = { + x: Big(oppositeCurrentLine.x2).plus(Big(nextHipVector.x).times(10)), + y: Big(oppositeCurrentLine.y2).plus(Big(nextHipVector.y).times(10)), + } + if (!checkWallPolygon.inPolygon(nextCheckPoint)) { + nextHipVector = { x: Big(nextHipVector.x).neg(), y: Big(nextHipVector.y).neg() } + } + + /** 현재 라인의 길이를 기준으로 추녀 마루의 길이를 삼각함수를 사용하여 판단한다.*/ + let hipLength = Big(oppositeCurrentLine.attributes.planeSize) + .div(2) + .pow(2) + .plus(Big(oppositeCurrentLine.attributes.planeSize).div(2).pow(2)) + .sqrt() + .div(10) + .round(2) + + const ridgeEndPoint = { + x: Big(oppositeCurrentLine.x1).plus(hipLength.times(prevHipVector.x)).round(1), + y: Big(oppositeCurrentLine.y1).plus(hipLength.times(prevHipVector.y)).round(1), + } + + const prevHypotenuse = Big(oppositePrevLine.attributes.offset).pow(2).plus(Big(oppositeCurrentLine.attributes.offset).pow(2)).sqrt() + const prevHipPoints = { + x1: Big(oppositeCurrentLine.x1).plus(prevHypotenuse.times(prevHipVector.x.neg())).round(1).toNumber(), + y1: Big(oppositeCurrentLine.y1).plus(prevHypotenuse.times(prevHipVector.y.neg())).round(1).toNumber(), + x2: ridgeEndPoint.x.toNumber(), + y2: ridgeEndPoint.y.toNumber(), + } + + const nextHypotenuse = Big(oppositeNextLine.attributes.offset).pow(2).plus(Big(oppositeCurrentLine.attributes.offset).pow(2)).sqrt() + const nextHipPoints = { + x1: Big(oppositeCurrentLine.x2).plus(nextHypotenuse.times(nextHipVector.x.neg())).round(1).toNumber(), + y1: Big(oppositeCurrentLine.y2).plus(nextHypotenuse.times(nextHipVector.y.neg())).round(1).toNumber(), + x2: ridgeEndPoint.x.toNumber(), + y2: ridgeEndPoint.y.toNumber(), + } + + const prevIntersection = findRoofIntersection(roof, prevHipPoints, ridgeEndPoint) + const nextIntersection = findRoofIntersection(roof, nextHipPoints, ridgeEndPoint) + + if (prevIntersection) { + const prevHip = drawHipLine( + [prevIntersection.intersection.x, prevIntersection.intersection.y, ridgeEndPoint.x.toNumber(), ridgeEndPoint.y.toNumber()], + canvas, + roof, + textMode, + null, + prevDegree, + prevDegree, + ) + baseHipLines.push({ + x1: oppositeCurrentLine.x1, + y1: oppositeCurrentLine.y1, + x2: ridgeEndPoint.x, + y2: ridgeEndPoint.y, + line: prevHip, + }) + } + if (nextIntersection) { + const nextHip = drawHipLine( + [nextIntersection.intersection.x, nextIntersection.intersection.y, ridgeEndPoint.x.toNumber(), ridgeEndPoint.y.toNumber()], + canvas, + roof, + textMode, + null, + nextDegree, + nextDegree, + ) + baseHipLines.push({ + x1: ridgeEndPoint.x, + y1: ridgeEndPoint.y, + x2: oppositeCurrentLine.x2, + y2: oppositeCurrentLine.y2, + line: nextHip, + }) + } + + const ridgeVectorX = Math.sign(currentMidX.minus(ridgeEndPoint.x).toNumber()) + const ridgeVectorY = Math.sign(currentMidY.minus(ridgeEndPoint.y).toNumber()) + const ridgePoints = { + x1: currentMidX.plus(Big(currentLine.attributes.offset).times(ridgeVectorX)).toNumber(), + y1: currentMidY.plus(Big(currentLine.attributes.offset).times(ridgeVectorY)).toNumber(), + x2: ridgeEndPoint.x.toNumber(), + y2: ridgeEndPoint.y.toNumber(), + } + const ridgeIntersection = findRoofIntersection(roof, ridgePoints, { x: Big(ridgePoints.x2), y: Big(ridgePoints.y2) }) + if (ridgeIntersection) { + points = [ridgeIntersection.intersection.x, ridgeIntersection.intersection.y, ridgeEndPoint.x, ridgeEndPoint.y] + } + } + } else { + if (currentVectorX.eq(0)) { + points = [oppositeLine.intersection.x, currentLine.y1, oppositeLine.intersection.x, currentLine.y2] + } else { + points = [currentLine.x1, oppositeLine.intersection.y, currentLine.x2, oppositeLine.intersection.y] + } + } + + if (baseRidgeCount < getMaxRidge(baseLines.length)) { + const ridgeLine = drawRidgeLine(points, canvas, roof, textMode) + baseGableRidgeLines.push(ridgeLine) + baseRidgeCount++ + } + + canvas + .getObjects() + .filter((obj) => obj.name === 'checkLine' || obj.name === 'checkPoint') + .forEach((obj) => canvas.remove(obj)) + canvas.renderAll() + } else { + const oppositeLines = baseLines.filter((line) => { + const lineAngle = calculateAngle(line.startPoint, line.endPoint) + switch (currentAngle) { + case 90: + return lineAngle === -90 + case -90: + return lineAngle === 90 + case 0: + return lineAngle === 180 + case 180: + return lineAngle === 0 + } + }) + + if (oppositeLines.length > 0 && baseRidgeCount < getMaxRidge(baseLines.length)) { + let ridgePoints = [] + const oppositeLine = oppositeLines.sort((a, b) => { + let diffCurrentA, diffCurrentB + if (Math.sign(currentVectorX) === 0) { + diffCurrentA = currentMidY.minus(a.y1).abs() + diffCurrentB = currentMidY.minus(b.y1).abs() + } else { + diffCurrentA = currentMidX.minus(a.x1).abs() + diffCurrentB = currentMidX.minus(b.x1).abs() + } + return diffCurrentA.minus(diffCurrentB).toNumber() + })[0] + + const prevOffset = prevLine.attributes.offset + const nextOffset = nextLine.attributes.offset + if (Math.sign(currentVectorX) === 0) { + const prevY = Big(currentLine.y1) + .plus(Big(Math.sign(currentVectorY)).neg().times(prevOffset)) + .toNumber() + const nextY = Big(currentLine.y2) + .plus(Big(Math.sign(currentVectorY)).times(nextOffset)) + .toNumber() + const midX = Big(currentLine.x1).plus(oppositeLine.x1).div(2).toNumber() + ridgePoints = [midX, prevY, midX, nextY] + } else { + const prevX = Big(currentLine.x1) + .plus(Big(Math.sign(currentVectorX)).neg().times(prevOffset)) + .toNumber() + const nextX = Big(currentLine.x2) + .plus(Big(Math.sign(currentVectorX)).times(nextOffset)) + .toNumber() + const midY = Big(currentLine.y1).plus(oppositeLine.y1).div(2).toNumber() + ridgePoints = [prevX, midY, nextX, midY] + } + const ridge = drawRidgeLine(ridgePoints, canvas, roof, textMode) + baseGableRidgeLines.push(ridge) + baseRidgeCount++ + } + } + canvas + .getObjects() + .filter((obj) => obj.name === 'checkLine' || obj.name === 'checkPoint') + .forEach((obj) => canvas.remove(obj)) + canvas.renderAll() + }) + + /** 케라바 지붕으로 생성된 마루의 지붕선을 그린다.*/ + baseGableRidgeLines.forEach((ridge) => { + return + const { x1, x2, y1, y2 } = ridge + const checkLine = new fabric.Line([x1, y1, x2, y2], { + stroke: 'red', + strokeWidth: 4, + parentId: roofId, + name: 'checkLine', + }) + canvas.add(checkLine) + canvas.renderAll() + + const ridgeVectorX = Math.sign(Big(x1).minus(x2).toNumber()) + const ridgeVectorY = Math.sign(Big(y1).minus(y2).toNumber()) + const firstPoint = { x: x1, y: y1 } + const secondPoint = { x: x2, y: y2 } + let firstRoofLine, secondRoofLine + roof.lines + .filter((line) => { + if (ridgeVectorX === 0) { + return line.x1 !== line.x2 + } else { + return line.y1 !== line.y2 + } + }) + .forEach((line) => { + if (ridgeVectorX === 0) { + if (line.y1 === firstPoint.y) { + firstRoofLine = line + } + if (line.y1 === secondPoint.y) { + secondRoofLine = line + } + } else { + if (line.x1 === firstPoint.x) { + firstRoofLine = line + } + if (line.x1 === secondPoint.x) { + secondRoofLine = line + } + } + }) + /** 마루 1개에 (위,아래), (좌,우) 로 두가지의 지붕면이 생길 수 있다.*/ + let firstCheckPoints = [] + let secondCheckPoints = [] + + /** + * @param startPoint + * @param nextPoint + * @param endPoint + */ + const findPointToPolygon = (startPoint, nextPoint, endPoint) => { + let findPoint = nextPoint + const points = [startPoint, nextPoint] + let index = 1 + while (!(points[points.length - 1].x === endPoint.x && points[points.length - 1].y === endPoint.y)) { + const prevVector = { x: Math.sign(points[index - 1].x - points[index].x), y: Math.sign(points[index - 1].y - points[index].y) } + const currentPoint = points[index] + + const hipLine = baseHipLines.find( + (line) => + (line.line.x1 === currentPoint.x && line.line.y1 === currentPoint.y) || + (line.line.x2 === currentPoint.x && line.line.y2 === currentPoint.y), + ) + if (hipLine) { + if (hipLine.line.x1 === currentPoint.x && hipLine.line.y1 === currentPoint.y) { + points.push({ x: hipLine.line.x2, y: hipLine.line.y2 }) + } else { + points.push({ x: hipLine.line.x1, y: hipLine.line.y1 }) + } + } else { + const nextRoofLine = roof.lines.find((line) => { + if (prevVector.x !== 0) { + return ( + line.y1 !== line.y2 && + ((line.x1 <= currentPoint.x && currentPoint.x <= line.x2) || (line.x2 <= currentPoint.x && currentPoint.x <= line.x1)) + ) + } else { + return ( + line.x1 !== line.x2 && + ((line.y1 <= currentPoint.y && currentPoint.y <= line.y2) || (line.y2 <= currentPoint.y && currentPoint.y <= line.y1)) + ) + } + }) + const checkLine = new fabric.Line([nextRoofLine.x1, nextRoofLine.y1, nextRoofLine.x2, nextRoofLine.y2], { + stroke: 'yellow', + strokeWidth: 4, + parentId: roofId, + name: 'checkLine', + }) + canvas.add(checkLine) + canvas.renderAll() + + const lineEdge = { vertex1: { x: nextRoofLine.x1, y: nextRoofLine.y1 }, vertex2: { x: nextRoofLine.x2, y: nextRoofLine.y2 } } + let ridgeIntersection + baseGableRidgeLines.forEach((ridgeLine) => { + const ridgeEdge = { vertex1: { x: ridgeLine.x1, y: ridgeLine.y1 }, vertex2: { x: ridgeLine.x2, y: ridgeLine.y2 } } + const intersection = edgesIntersection(lineEdge, ridgeEdge) + console.log('intersection', intersection) + if (intersection && !intersection.isIntersectionOutside) { + ridgeIntersection = { x: intersection.x, y: intersection.y } + } + }) + if (ridgeIntersection) { + points.push(ridgeIntersection) + } else { + if (nextRoofLine.x1 === currentPoint.x && nextRoofLine.y1 === currentPoint.y) { + points.push({ x: nextRoofLine.x2, y: nextRoofLine.y2 }) + } else { + points.push({ x: nextRoofLine.x1, y: nextRoofLine.y1 }) + } + } + } + console.log('points', points, points[points.length - 1], endPoint) + index = index + 1 + } + + canvas + .getObjects() + .filter((obj) => obj.name === 'checkLine' || obj.name === 'checkPoint') + .forEach((obj) => canvas.remove(obj)) + canvas.renderAll() + return points + } + + let startVector + console.log('!firstRoofLine && !secondRoofLine', !firstRoofLine && !secondRoofLine) + let firstPoints, secondPoints + if (!firstRoofLine && !secondRoofLine) { + } else { + if (firstRoofLine) { + firstPoints = findPointToPolygon(firstPoint, { x: firstRoofLine.x1, y: firstRoofLine.y1 }, secondPoint) + secondPoints = findPointToPolygon(firstPoint, { x: firstRoofLine.x2, y: firstRoofLine.y2 }, secondPoint) + } else { + firstPoints = findPointToPolygon(secondPoint, { x: secondRoofLine.x1, y: secondRoofLine.y1 }, firstPoint) + secondPoints = findPointToPolygon(secondPoint, { x: secondRoofLine.x2, y: secondRoofLine.y2 }, firstPoint) + } + } + const firstPolygonPoints = getSortedPoint(firstPoints) + const secondPolygonPoints = getSortedPoint(secondPoints) + + firstPolygonPoints.forEach((point, index) => { + let endPoint + if (index === firstPolygonPoints.length - 1) { + endPoint = firstPolygonPoints[0] + } else { + endPoint = firstPolygonPoints[index + 1] + } + const hipLine = drawHipLine([point.x, point.y, endPoint.x, endPoint.y], canvas, roof, textMode, null, currentDegree, currentDegree) + baseHipLines.push({ x1: point.x, y1: point.y, x2: endPoint.x, y2: endPoint.y, line: hipLine }) + + const checkLine = new fabric.Line([point.x, point.y, endPoint.x, endPoint.y], { + stroke: 'red', + strokeWidth: 4, + parentId: roofId, + name: 'checkLine', + }) + canvas.add(checkLine) + canvas.renderAll() + }) + secondPolygonPoints.forEach((point, index) => { + let endPoint + if (index === secondPolygonPoints.length - 1) { + endPoint = secondPolygonPoints[0] + } else { + endPoint = secondPolygonPoints[index + 1] + } + const hipLine = drawHipLine([point.x, point.y, endPoint.x, endPoint.y], canvas, roof, textMode, null, currentDegree, currentDegree) + baseHipLines.push({ x1: point.x, y1: point.y, x2: endPoint.x, y2: endPoint.y, line: hipLine }) + + const checkLine = new fabric.Line([point.x, point.y, endPoint.x, endPoint.y], { + stroke: 'red', + strokeWidth: 4, + parentId: roofId, + name: 'checkLine', + }) + canvas.add(checkLine) + canvas.renderAll() + }) + }) + + drawGableFirstLines.forEach((current) => { + return + const { currentBaseLine, prevBaseLine, nextBaseLine } = current + const currentLine = currentBaseLine.line + const prevLine = prevBaseLine.line + const nextLine = nextBaseLine.line + let { x1, x2, y1, y2, size } = currentBaseLine + let beforePrevBaseLine, afterNextBaseLine + + /** 이전 라인의 경사 */ + const prevDegree = prevLine.attributes.pitch > 0 ? getDegreeByChon(prevLine.attributes.pitch) : prevLine.attributes.degree + /** 다음 라인의 경사 */ + const nextDegree = nextLine.attributes.pitch > 0 ? getDegreeByChon(nextLine.attributes.pitch) : nextLine.attributes.degree + /** 현재 라인의 경사 */ + const currentDegree = currentLine.attributes.pitch > 0 ? getDegreeByChon(currentLine.attributes.pitch) : currentLine.attributes.degree + + /** 이전 라인의 전라인, 다음 라인의 다음라인을 찾는다 */ + drawBaseLines.forEach((line, index) => { + if (line === prevBaseLine) { + beforePrevBaseLine = drawBaseLines[(index - 1 + drawBaseLines.length) % drawBaseLines.length] + } + if (line === nextBaseLine) { + afterNextBaseLine = drawBaseLines[(index + 1) % drawBaseLines.length] + } + }) + + const beforePrevLine = beforePrevBaseLine?.line + const afterNextLine = afterNextBaseLine?.line + + /** 각 라인의 흐름 방향을 확인한다. */ + const currentAngle = calculateAngle(currentLine.startPoint, currentLine.endPoint) + const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint) + const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint) + const beforePrevAngle = calculateAngle(beforePrevLine.startPoint, beforePrevLine.endPoint) + const afterNextAngle = calculateAngle(afterNextLine.startPoint, afterNextLine.endPoint) + + /** 이전라인의 vector*/ + const prevVectorX = Math.sign(Big(prevLine.x2).minus(Big(prevLine.x1))) + const prevVectorY = Math.sign(Big(prevLine.y2).minus(Big(prevLine.y1))) + + /** 현재라인의 기준점*/ + const currentMidX = Big(x1).plus(Big(x2)).div(2).plus(Big(prevVectorX).times(currentLine.attributes.offset)) + const currentMidY = Big(y1).plus(Big(y2)).div(2).plus(Big(prevVectorY).times(currentLine.attributes.offset)) + + if (beforePrevBaseLine === afterNextBaseLine) { + console.log('박공지붕 사각') + + const afterNextMidX = Big(afterNextLine.x1).plus(Big(afterNextLine.x2)).div(2) + const afterNextMidY = Big(afterNextLine.y1).plus(Big(afterNextLine.y2)).div(2) + const vectorMidX = Math.sign(currentMidX.minus(afterNextMidX)) + const vectorMidY = Math.sign(currentMidY.minus(afterNextMidY)) + + let oppositeMidX, oppositeMidY + if (eavesType.includes(afterNextLine.attributes?.type)) { + const checkSize = currentMidX + .minus(afterNextMidX) + .pow(2) + .plus(currentMidY.minus(afterNextMidY).pow(2)) + .sqrt() + .minus(Big(afterNextLine.attributes.planeSize).div(20)) + .round(1) + oppositeMidX = currentMidX.plus(checkSize.times(vectorMidX).neg()) + oppositeMidY = currentMidY.plus(checkSize.times(vectorMidY).neg()) + + const xVector1 = Math.sign(Big(oppositeMidX).minus(Big(afterNextLine.x1)).neg().toNumber()) + const yVector1 = Math.sign(Big(oppositeMidY).minus(Big(afterNextLine.y1)).neg().toNumber()) + const xVector2 = Math.sign(Big(oppositeMidX).minus(Big(afterNextLine.x2)).neg().toNumber()) + const yVector2 = Math.sign(Big(oppositeMidY).minus(Big(afterNextLine.y2)).neg().toNumber()) + + let addOppositeX1 = 0, + addOppositeY1 = 0, + addOppositeX2 = 0, + addOppositeY2 = 0 + + if (!checkWallPolygon.inPolygon({ x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() })) { + const checkScale = currentMidX.minus(oppositeMidX).pow(2).plus(currentMidY.minus(oppositeMidY).pow(2)).sqrt() + addOppositeX1 = checkScale.times(xVector1).toNumber() + addOppositeY1 = checkScale.times(yVector1).toNumber() + addOppositeX2 = checkScale.times(xVector2).toNumber() + addOppositeY2 = checkScale.times(yVector2).toNumber() + } + + let scale1 = Big(afterNextLine.attributes.offset).pow(2).plus(Big(nextLine.attributes.offset).pow(2)).sqrt() + scale1 = scale1.eq(0) ? Big(1) : scale1 + let scale2 = Big(afterNextLine.attributes.offset).pow(2).plus(Big(prevLine.attributes.offset).pow(2)).sqrt() + scale2 = scale2.eq(0) ? Big(1) : scale2 + + const checkHip1 = { + x1: Big(afterNextLine.x1).plus(scale1.times(xVector1)).toNumber(), + y1: Big(afterNextLine.y1).plus(scale1.times(yVector1)).toNumber(), + x2: oppositeMidX.plus(addOppositeX1).toNumber(), + y2: oppositeMidY.plus(addOppositeY1).toNumber(), + } + + const checkHip2 = { + x1: Big(afterNextLine.x2).plus(scale2.times(xVector2)).toNumber(), + y1: Big(afterNextLine.y2).plus(scale2.times(yVector2)).toNumber(), + x2: oppositeMidX.plus(addOppositeX2).toNumber(), + y2: oppositeMidY.plus(addOppositeY2).toNumber(), + } + + const intersection1 = findRoofIntersection(roof, checkHip1, { x: oppositeMidX.plus(addOppositeX1), y: oppositeMidY.plus(addOppositeY1) }) + const intersection2 = findRoofIntersection(roof, checkHip2, { x: oppositeMidX.plus(addOppositeX2), y: oppositeMidY.plus(addOppositeY2) }) + + const afterNextDegree = afterNextLine.attributes.pitch > 0 ? getDegreeByChon(afterNextLine.attributes.pitch) : afterNextLine.attributes.degree + + if (intersection1) { + const hipLine = drawHipLine( + [intersection1.intersection.x, intersection1.intersection.y, oppositeMidX.plus(addOppositeX1), oppositeMidY.plus(addOppositeY1)], + canvas, + roof, + textMode, + null, + nextDegree, + afterNextDegree, + ) + baseHipLines.push({ + x1: afterNextLine.x1, + y1: afterNextLine.y1, + x2: oppositeMidX.plus(addOppositeX1).toNumber(), + y2: oppositeMidY.plus(addOppositeY1).toNumber(), + line: hipLine, + }) + } + if (intersection2) { + const hipLine = drawHipLine( + [intersection2.intersection.x, intersection2.intersection.y, oppositeMidX.plus(addOppositeX2), oppositeMidY.plus(addOppositeY2)], + canvas, + roof, + textMode, + null, + prevDegree, + afterNextDegree, + ) + baseHipLines.push({ + x1: afterNextLine.x2, + y1: afterNextLine.y2, + x2: oppositeMidX.plus(addOppositeX2).toNumber(), + y2: oppositeMidY.plus(addOppositeY2).toNumber(), + line: hipLine, + }) + } + } else { + oppositeMidX = Big(afterNextLine.x1).plus(Big(afterNextLine.x2)).div(2).plus(Big(prevVectorX).neg().times(afterNextLine.attributes.offset)) + oppositeMidY = Big(afterNextLine.y1).plus(Big(afterNextLine.y2)).div(2).plus(Big(prevVectorY).neg().times(afterNextLine.attributes.offset)) + } + + const vectorOppositeX = Math.sign(currentMidX.minus(oppositeMidX)) + const vectorOppositeY = Math.sign(currentMidY.minus(oppositeMidY)) + + if (vectorMidX === vectorOppositeX && vectorMidY === vectorOppositeY && baseRidgeCount < getMaxRidge(baseLines.length)) { + const ridge = drawRidgeLine( + [currentMidX.toNumber(), currentMidY.toNumber(), oppositeMidX.toNumber(), oppositeMidY.toNumber()], + canvas, + roof, + textMode, + ) + baseRidgeLines.push(ridge) + baseRidgeCount++ + } + } else { + console.log('4각 아님') + const vectorMidX = Math.sign(Big(nextLine.x2).minus(nextLine.x1)) + const vectorMidY = Math.sign(Big(nextLine.y2).minus(nextLine.y1)) + let oppositeMidX = currentMidX, + oppositeMidY = currentMidY + let prevOppositeMidX, prevOppositeMidY, nextOppositeMidX, nextOppositeMidY + const beforePrevOffset = + currentAngle === beforePrevAngle + ? Big(beforePrevLine.attributes.offset) + : Big(beforePrevLine.attributes.offset).plus(currentLine.attributes.offset) + const afterNextOffset = + currentAngle === afterNextAngle + ? Big(afterNextLine.attributes.offset) + : Big(afterNextLine.attributes.offset).plus(currentLine.attributes.offset) + const prevSize = Big(prevLine.attributes.planeSize).div(10) + const nextSize = Big(nextLine.attributes.planeSize).div(10) + + /** 다음 라인이 그 다음 라인과의 사이에 추녀마루가 존재 하는지 확인. 처마-처마 인 경우 추녀마루*/ + if (eavesType.includes(afterNextLine.attributes?.type)) { + } else { + if (vectorMidX === 0) { + prevOppositeMidY = currentMidY.plus(prevSize.plus(beforePrevOffset).times(vectorMidY)) + prevOppositeMidX = currentMidX + } else { + prevOppositeMidX = currentMidX.plus(prevSize.plus(beforePrevOffset).times(vectorMidX)) + prevOppositeMidY = currentMidY + } + } + + /** 이전 라인이 그 이전 라인과의 사이에 추녀마루가 존재 하는지 확인. 처마-처마 인 경우 추녀마루*/ + if (eavesType.includes(beforePrevLine.attributes?.type)) { + } else { + if (vectorMidX === 0) { + nextOppositeMidY = currentMidY.plus(nextSize.plus(afterNextOffset).times(vectorMidY)) + nextOppositeMidX = currentMidX + } else { + nextOppositeMidX = currentMidX.plus(nextSize.plus(afterNextOffset).times(vectorMidX)) + nextOppositeMidY = currentMidY + } + } + + const checkPrevSize = currentMidX.minus(prevOppositeMidX).pow(2).plus(currentMidY.minus(prevOppositeMidY).pow(2)).sqrt() + const checkNextSize = currentMidX.minus(nextOppositeMidX).pow(2).plus(currentMidY.minus(nextOppositeMidY).pow(2)).sqrt() + + /** 두 포인트 중에 current와 가까운 포인트를 사용*/ + if (checkPrevSize.gt(checkNextSize)) { + oppositeMidY = nextOppositeMidY + oppositeMidX = nextOppositeMidX + } else { + oppositeMidY = prevOppositeMidY + oppositeMidX = prevOppositeMidX + } + + if (baseRidgeCount < getMaxRidge(baseLines.length)) { + const ridgeLine = drawRidgeLine([currentMidX, currentMidY, oppositeMidX, oppositeMidY], canvas, roof, textMode) + baseRidgeLines.push(ridgeLine) + baseRidgeCount++ + } + } + }) + + /** 박공지붕 polygon 생성 */ + drawGablePolygonFirst.forEach((current) => { + const { currentBaseLine, prevBaseLine, nextBaseLine } = current + const currentLine = currentBaseLine.line + const prevLine = prevBaseLine.line + const nextLine = nextBaseLine.line + let { x1, x2, y1, y2 } = currentBaseLine + let prevLineRidges = [], + nextLineRidges = [] + + const currentVectorX = Math.sign(currentLine.x2 - currentLine.x1) + const currentVectorY = Math.sign(currentLine.y2 - currentLine.y1) + const prevVectorX = Math.sign(prevLine.x2 - prevLine.x1) + const prevVectorY = Math.sign(prevLine.y2 - prevLine.y1) + const nextVectorX = Math.sign(nextLine.x1 - nextLine.x2) + const nextVectorY = Math.sign(nextLine.y1 - nextLine.y2) + const currentDegree = currentLine.attributes.pitch > 0 ? getDegreeByChon(currentLine.attributes.pitch) : currentLine.attributes.degree + const currentOffset = currentLine.attributes.offset + const prevOffset = prevLine.attributes.offset + const nextOffset = nextLine.attributes.offset + + let roofX1, roofY1, roofX2, roofY2 + if (currentVectorX === 0) { + roofX1 = Big(x1).plus(Big(currentLine.attributes.offset).times(prevVectorX)) + roofX2 = roofX1 + roofY1 = Big(y1).plus(Big(prevLine.attributes.offset).times(currentVectorY * -1)) + roofY2 = Big(y2).plus(Big(nextLine.attributes.offset).times(currentVectorY)) + } else { + roofX1 = Big(x1).plus(Big(prevLine.attributes.offset).times(currentVectorX * -1)) + roofX2 = Big(x2).plus(Big(nextLine.attributes.offset).times(currentVectorX)) + roofY1 = Big(y1).plus(Big(currentLine.attributes.offset).times(prevVectorY)) + roofY2 = roofY1 + } + + const prevRoofLine = roof.lines.find( + (line) => currentVectorX !== Math.sign(line.x2 - line.x1) && line.x2 === roofX1.toNumber() && line.y2 === roofY1.toNumber(), + ) + const nextRoofLine = roof.lines.find( + (line) => currentVectorX !== Math.sign(line.x2 - line.x1) && line.x1 === roofX2.toNumber() && line.y1 === roofY2.toNumber(), + ) + + const prevRoofEdge = { vertex1: { x: prevRoofLine.x1, y: prevRoofLine.y1 }, vertex2: { x: prevRoofLine.x2, y: prevRoofLine.y2 } } + const nextRoofEdge = { vertex1: { x: nextRoofLine.x1, y: nextRoofLine.y1 }, vertex2: { x: nextRoofLine.x2, y: nextRoofLine.y2 } } + + baseGableRidgeLines.forEach((ridge) => { + const ridgeEdge = { vertex1: { x: ridge.x1, y: ridge.y1 }, vertex2: { x: ridge.x2, y: ridge.y2 } } + const prevIs = edgesIntersection(prevRoofEdge, ridgeEdge) + const nextIs = edgesIntersection(nextRoofEdge, ridgeEdge) + if (prevIs) { + prevLineRidges.push({ + ridge, + size: calcLinePlaneSize({ x1: ridgeEdge.vertex1.x, y1: ridgeEdge.vertex1.y, x2: prevIs.x, y2: prevIs.y }), + }) + } + if (nextIs) { + nextLineRidges.push({ + ridge, + size: calcLinePlaneSize({ x1: ridgeEdge.vertex1.x, y1: ridgeEdge.vertex1.y, x2: nextIs.x, y2: nextIs.y }), + }) + } + }) + + const polygonPoints = [ + { x: roofX1.toNumber(), y: roofY1.toNumber() }, + { x: roofX2.toNumber(), y: roofY2.toNumber() }, + ] + + let prevLineRidge, nextLineRidge + if (prevLineRidges.length > 0) { + prevLineRidges = prevLineRidges + .filter((line) => { + const ridge = line.ridge + if (currentVectorX === 0) { + return ridge.y1 === roofY1.toNumber() || ridge.y2 === roofY1.toNumber() + } else { + return ridge.x1 === roofX1.toNumber() || ridge.x2 === roofX1.toNumber() + } + }) + .sort((a, b) => a.size - b.size) + prevLineRidge = prevLineRidges[0].ridge + } + if (nextLineRidges.length > 0) { + nextLineRidges = nextLineRidges + .filter((line) => { + const ridge = line.ridge + if (currentVectorX === 0) { + return ridge.y1 === roofY2.toNumber() || ridge.y2 === roofY2.toNumber() + } else { + return ridge.x1 === roofX2.toNumber() || ridge.x2 === roofX2.toNumber() + } + }) + .sort((a, b) => a.size - b.size) + nextLineRidge = nextLineRidges[0].ridge + } + + if (prevLineRidge === nextLineRidge) { + polygonPoints.push({ x: prevLineRidge.x1, y: prevLineRidge.y1 }, { x: prevLineRidge.x2, y: prevLineRidge.y2 }) + } else { + let isOverLap = + currentVectorX === 0 + ? (prevLineRidge.y1 <= nextLineRidge.y1 && prevLineRidge.y2 >= nextLineRidge.y1) || + (prevLineRidge.y1 >= nextLineRidge.y1 && prevLineRidge.y2 <= nextLineRidge.y1) || + (prevLineRidge.y1 <= nextLineRidge.y2 && prevLineRidge.y2 >= nextLineRidge.y2) || + (prevLineRidge.y1 >= nextLineRidge.y2 && prevLineRidge.y2 <= nextLineRidge.y2) + : (prevLineRidge.x1 <= nextLineRidge.x1 && prevLineRidge.x2 >= nextLineRidge.x1) || + (prevLineRidge.x1 >= nextLineRidge.x1 && prevLineRidge.x2 <= nextLineRidge.x1) || + (prevLineRidge.x1 <= nextLineRidge.x2 && prevLineRidge.x2 >= nextLineRidge.x2) || + (prevLineRidge.x1 >= nextLineRidge.x2 && prevLineRidge.x2 <= nextLineRidge.x2) + console.log('isOverLap : ', isOverLap) + if (isOverLap) { + const prevDistance = currentVectorX === 0 ? Math.abs(prevLineRidge.x1 - roofX1.toNumber()) : Math.abs(prevLineRidge.y1 - roofY1.toNumber()) + const nextDistance = currentVectorX === 0 ? Math.abs(nextLineRidge.x1 - roofX1.toNumber()) : Math.abs(nextLineRidge.y1 - roofY1.toNumber()) + /** 현재 지붕 라인과 먼 라인의 포인트를 온전히 사용한다. */ + if (prevDistance < nextDistance) { + polygonPoints.push({ x: nextLineRidge.x1, y: nextLineRidge.y1 }, { x: nextLineRidge.x2, y: nextLineRidge.y2 }) + /** 이전라인과 교차한 마루의 포인트*/ + const prevRidgePoint1 = + currentVectorX === 0 + ? roofY1.toNumber() === prevLineRidge.y1 + ? { x: prevLineRidge.x1, y: prevLineRidge.y1 } + : { x: prevLineRidge.x2, y: prevLineRidge.y2 } + : roofX1.toNumber() === prevLineRidge.x1 + ? { x: prevLineRidge.x1, y: prevLineRidge.y1 } + : { x: prevLineRidge.x2, y: prevLineRidge.y2 } + + polygonPoints.push(prevRidgePoint1) + + /** 다음 라인과 교차한 마루의 포인트 중 라인과 접하지 않은 포인트*/ + const checkRidgePoint = + currentVectorX === 0 + ? roofY2.toNumber() !== nextLineRidge.y1 + ? { x: nextLineRidge.x1, y: nextLineRidge.y1 } + : { x: nextLineRidge.x2, y: nextLineRidge.y2 } + : roofX2.toNumber() !== nextLineRidge.x1 + ? { x: nextLineRidge.x1, y: nextLineRidge.y1 } + : { x: nextLineRidge.x2, y: nextLineRidge.y2 } + + const prevRidgePoint2 = + currentVectorX === 0 ? { x: prevRidgePoint1.x, y: checkRidgePoint.y } : { x: checkRidgePoint.x, y: prevRidgePoint1.y } + polygonPoints.push(prevRidgePoint2) + } else { + polygonPoints.push({ x: prevLineRidge.x1, y: prevLineRidge.y1 }, { x: prevLineRidge.x2, y: prevLineRidge.y2 }) + + /** 다음라인과 교차한 마루의 포인트*/ + const nextRidgePoint1 = + currentVectorX === 0 + ? roofY2.toNumber() === nextLineRidge.y1 + ? { x: nextLineRidge.x1, y: nextLineRidge.y1 } + : { x: nextLineRidge.x2, y: nextLineRidge.y2 } + : roofX2.toNumber() === nextLineRidge.x1 + ? { x: nextLineRidge.x1, y: nextLineRidge.y1 } + : { x: nextLineRidge.x2, y: nextLineRidge.y2 } + polygonPoints.push(nextRidgePoint1) + + /** 이전 라인과 교차한 마루의 포인트 중 라인과 접하지 않은 포인트*/ + const checkRidgePoint = + currentVectorX === 0 + ? roofY1.toNumber() !== prevLineRidge.y1 + ? { x: prevLineRidge.x1, y: prevLineRidge.y1 } + : { x: prevLineRidge.x2, y: prevLineRidge.y2 } + : roofX1.toNumber() !== prevLineRidge.x1 + ? { x: prevLineRidge.x1, y: prevLineRidge.y1 } + : { x: prevLineRidge.x2, y: prevLineRidge.y2 } + const nextRidgePoint2 = + currentVectorX === 0 ? { x: nextRidgePoint1.x, y: checkRidgePoint.y } : { x: checkRidgePoint.x, y: nextRidgePoint1.y } + polygonPoints.push(nextRidgePoint2) + } + } else { + } + } + + const sortedPolygonPoints = getSortedPoint(polygonPoints) + + sortedPolygonPoints.forEach((startPoint, index) => { + let endPoint + if (index === sortedPolygonPoints.length - 1) { + endPoint = sortedPolygonPoints[0] + } else { + endPoint = sortedPolygonPoints[index + 1] + } + const hipLine = drawHipLine([startPoint.x, startPoint.y, endPoint.x, endPoint.y], canvas, roof, textMode, null, currentDegree, currentDegree) + if (currentVectorX === 0) { + if (Math.sign(startPoint.x - endPoint.x) === 0) { + hipLine.attributes.actualSize = hipLine.attributes.planeSize + } + } else { + if (Math.sign(startPoint.y - endPoint.y) === 0) { + hipLine.attributes.actualSize = hipLine.attributes.planeSize + } + } + baseHipLines.push({ x1: hipLine.x1, y1: hipLine.y1, x2: hipLine.x2, y2: hipLine.y2, line: hipLine }) + }) + + canvas + .getObjects() + .filter((obj) => obj.name === 'checkLine' || obj.name === 'checkRoofLine') + .forEach((obj) => canvas.remove(obj)) + canvas.renderAll() + }) + + drawGablePolygonSecond.forEach((current) => { + const { currentBaseLine, prevBaseLine, nextBaseLine } = current + const currentLine = currentBaseLine.line + const prevLine = prevBaseLine.line + const nextLine = nextBaseLine.line + let { x1, x2, y1, y2 } = currentBaseLine + let prevLineRidges = [], + nextLineRidges = [] + + const currentVectorX = Math.sign(currentLine.x2 - currentLine.x1) + const currentVectorY = Math.sign(currentLine.y2 - currentLine.y1) + const prevVectorX = Math.sign(prevLine.x2 - prevLine.x1) + const prevVectorY = Math.sign(prevLine.y2 - prevLine.y1) + const nextVectorX = Math.sign(nextLine.x1 - nextLine.x2) + const nextVectorY = Math.sign(nextLine.y1 - nextLine.y2) + const currentDegree = currentLine.attributes.pitch > 0 ? getDegreeByChon(currentLine.attributes.pitch) : currentLine.attributes.degree + const currentOffset = currentLine.attributes.offset + const prevOffset = prevLine.attributes.offset + const nextOffset = nextLine.attributes.offset + + const prevEdge = { vertex1: { x: prevLine.x1, y: prevLine.y1 }, vertex2: { x: prevLine.x2, y: prevLine.y2 } } + const prevRidge = baseGableRidgeLines.find((ridge) => { + const ridgeEdge = { vertex1: { x: ridge.x1, y: ridge.y1 }, vertex2: { x: ridge.x2, y: ridge.y2 } } + const is = edgesIntersection(prevEdge, ridgeEdge) + if (is && !is.isIntersectionOutside) { + return ridge + } + }) + + const nextEdge = { vertex1: { x: nextLine.x1, y: nextLine.y1 }, vertex2: { x: nextLine.x2, y: nextLine.y2 } } + const nextRidge = baseGableRidgeLines.find((ridge) => { + const ridgeEdge = { vertex1: { x: ridge.x1, y: ridge.y1 }, vertex2: { x: ridge.x2, y: ridge.y2 } } + const is = edgesIntersection(nextEdge, ridgeEdge) + if (is && !is.isIntersectionOutside) { + return ridge + } + }) + + let currentRidge + + if (prevRidge) { + if ( + currentVectorX === 0 && + ((prevRidge.y1 <= currentLine.y1 && prevRidge.y2 >= currentLine.y1 && prevRidge.y1 <= currentLine.y2 && prevRidge.y2 >= currentLine.y2) || + (prevRidge.y1 >= currentLine.y1 && prevRidge.y2 <= currentLine.y1 && prevRidge.y1 >= currentLine.y2 && prevRidge.y2 <= currentLine.y2)) + ) { + currentRidge = prevRidge + } + if ( + currentVectorY === 0 && + ((prevRidge.x1 <= currentLine.x1 && prevRidge.x2 >= currentLine.x1 && prevRidge.x1 <= currentLine.x2 && prevRidge.x2 >= currentLine.x2) || + (prevRidge.x1 >= currentLine.x1 && prevRidge.x2 <= currentLine.x1 && prevRidge.x1 >= currentLine.x2 && prevRidge.x2 <= currentLine.x2)) + ) { + currentRidge = prevRidge + } + } + if (nextRidge) { + if ( + currentVectorX === 0 && + ((nextRidge.y1 <= currentLine.y1 && nextRidge.y2 >= currentLine.y1 && nextRidge.y1 <= currentLine.y2 && nextRidge.y2 >= currentLine.y2) || + (nextRidge.y1 >= currentLine.y1 && nextRidge.y2 <= currentLine.y1 && nextRidge.y1 >= currentLine.y2 && nextRidge.y2 <= currentLine.y2)) + ) { + currentRidge = nextRidge + } + if ( + currentVectorY === 0 && + ((nextRidge.x1 <= currentLine.x1 && nextRidge.x2 >= currentLine.x1 && nextRidge.x1 <= currentLine.x2 && nextRidge.x2 >= currentLine.x2) || + (nextRidge.x1 >= currentLine.x1 && nextRidge.x2 <= currentLine.x1 && nextRidge.x1 >= currentLine.x2 && nextRidge.x2 <= currentLine.x2)) + ) { + currentRidge = nextRidge + } + } + if (currentRidge) { + const vectorX = currentVectorX === 0 ? Math.sign(currentLine.x1 - currentRidge.x1) : 0 + const vectorY = currentVectorY === 0 ? Math.sign(currentLine.y1 - currentRidge.y1) : 0 + + const polygonPoints = [ + { x: currentRidge.x1, y: currentRidge.y1 }, + { x: currentRidge.x2, y: currentRidge.y2 }, + ] + if (currentVectorX === 0) { + polygonPoints.push( + { x: Big(currentLine.x1).plus(Big(currentOffset).times(vectorX)).toNumber(), y: currentRidge.y1 }, + { x: Big(currentLine.x2).plus(Big(currentOffset).times(vectorX)).toNumber(), y: currentRidge.y2 }, + ) + } else { + polygonPoints.push( + { x: currentRidge.x1, y: Big(currentLine.y1).plus(Big(currentOffset).times(vectorY)).toNumber() }, + { x: currentRidge.x2, y: Big(currentLine.y2).plus(Big(currentOffset).times(vectorY)).toNumber() }, + ) + } + + const sortedPolygonPoints = getSortedPoint(polygonPoints) + + sortedPolygonPoints.forEach((startPoint, index) => { + let endPoint + if (index === sortedPolygonPoints.length - 1) { + endPoint = sortedPolygonPoints[0] + } else { + endPoint = sortedPolygonPoints[index + 1] + } + const hipLine = drawHipLine([startPoint.x, startPoint.y, endPoint.x, endPoint.y], canvas, roof, textMode, null, currentDegree, currentDegree) + if (currentVectorX === 0) { + if (Math.sign(startPoint.x - endPoint.x) === 0) { + hipLine.attributes.actualSize = hipLine.attributes.planeSize + } + } else { + if (Math.sign(startPoint.y - endPoint.y) === 0) { + hipLine.attributes.actualSize = hipLine.attributes.planeSize + } + } + baseHipLines.push({ x1: hipLine.x1, y1: hipLine.y1, x2: hipLine.x2, y2: hipLine.y2, line: hipLine }) + }) + } + }) + /** ⨆ 모양 처마에 추녀마루를 그린다. */ drawEavesFirstLines.forEach((current) => { // 확인용 라인, 포인트 제거 @@ -1408,13 +2902,14 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { }, } - /*const checkLine = new fabric.Line([checkEdges.vertex1.x, checkEdges.vertex1.y, checkEdges.vertex2.x, checkEdges.vertex2.y], { + const checkLine = new fabric.Line([checkEdges.vertex1.x, checkEdges.vertex1.y, checkEdges.vertex2.x, checkEdges.vertex2.y], { stroke: 'purple', strokeWidth: 2, parentId: roof.id, + name: 'checkLine', }) canvas.add(checkLine) - canvas.renderAll()*/ + canvas.renderAll() /** 맞은편 벽 까지의 길이 판단을 위한 교차되는 line*/ const intersectBaseLine = [] @@ -1464,7 +2959,7 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } const intersection = edgesIntersection(checkEdges, lineEdge) if (intersection) { - /*const intersectPoint = new fabric.Circle({ + /* const intersectPoint = new fabric.Circle({ left: intersection.x - 2, top: intersection.y - 2, radius: 4, @@ -1494,7 +2989,7 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { return prevDistance < currentDistance ? prev : current }, intersectBaseLine[0]) - // console.log('oppositeLine : ', oppositeLine) + console.log('oppositeLine : ', oppositeLine) /** 맞은편 라인까지의 길이 = 전체 길이 - 현재라인의 길이 */ const oppositeSize = oppositeLine @@ -1521,8 +3016,7 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { // console.log('oppositeSize : ', oppositeSize, 'lineMinSize : ', lineMinSize.toNumber(), 'ridgeSize : ', ridgeSize.toNumber()) - if (ridgeSize.gt(0)) { - baseRidgeCount = baseRidgeCount + 1 + if (ridgeSize.gt(0) && baseRidgeCount < getMaxRidge(baseLines.length)) { // console.log('baseRidgeCount : ', baseRidgeCount) const points = [ startPoint.x, @@ -1557,10 +3051,9 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { canvas.add(checkMidLine) canvas.renderAll()*/ // console.log('points : ', points) - - /** 마루 생성 */ const ridgeLine = drawRidgeLine(points, canvas, roof, textMode) baseRidgeLines.push(ridgeLine) + baseRidgeCount = baseRidgeCount + 1 /** 포인트 조정*/ baseHipLines @@ -1619,6 +3112,16 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const nextLine = nextBaseLine.line let { x1, x2, y1, y2, size } = currentBaseLine + // const checkLine = new fabric.Line([x1, y1, x2, y2], { + // stroke: 'yellow', + // strokeWidth: 4, + // parentId: roof.id, + // name: 'checkLine', + // }) + // canvas.add(checkLine) + // canvas.renderAll() + // checkLine.bringToFront() + /** 이전 라인의 경사 */ const prevDegree = prevLine.attributes.pitch > 0 ? getDegreeByChon(prevLine.attributes.pitch) : prevLine.attributes.degree /** 다음 라인의 경사 */ @@ -1675,7 +3178,7 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { let prevHipLine, nextHipLine /** 이전라인과의 연결지점에 추녀마루를 그린다. */ // console.log('이전라인 : ', baseHipLines.filter((line) => line.x1 === x1 && line.y1 === y1).length === 0) - if (baseHipLines.filter((line) => line.x1 === x1 && line.y1 === y1).length === 0) { + if (baseHipLines.filter((line) => line.x1 === x1 && line.y1 === y1).length === 0 && eavesType.includes(prevLine.attributes.type)) { let prevEndPoint = { x: Big(x1).plus(Big(prevHipVector.x).times(hipLength)).round(2), y: Big(y1).plus(Big(prevHipVector.y).times(hipLength)).round(2), @@ -1869,7 +3372,7 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } /** 다음라인과의 연결지점에 추녀마루를 그린다. */ // console.log('다음라인 : ', baseHipLines.filter((line) => line.x1 === x2 && line.y1 === y2).length === 0) - if (baseHipLines.filter((line) => line.x1 === x2 && line.y1 === y2).length === 0) { + if (baseHipLines.filter((line) => line.x1 === x2 && line.y1 === y2).length === 0 && eavesType.includes(nextLine.attributes.type)) { let nextEndPoint = { x: Big(x2).plus(Big(nextHipVector.x).times(hipLength)).round(2), y: Big(y2).plus(Big(nextHipVector.y).times(hipLength)).round(2), @@ -2097,7 +3600,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } } }) - intersectRidgePoints.sort((prev, current) => prev.size.minus(current.size).toNumber()) if (intersectRidgePoints.length > 0) { const oldPlaneSize = hipLine.line.attributes.planeSize @@ -2116,15 +3618,15 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } }) - // console.log('baseHipLines : ', baseHipLines) - // console.log('baseRidgeLines : ', baseRidgeLines) + console.log('baseHipLines : ', baseHipLines) + console.log('baseRidgeLines : ', baseRidgeLines) /** 지붕선에 맞닫지 않은 부분을 확인하여 처리 한다.*/ /** 1. 그려진 마루 중 추녀마루가 부재하는 경우를 판단. 부재는 연결된 라인이 홀수인 경우로 판단한다.*/ let unFinishedRidge = [] baseRidgeLines.forEach((current) => { let checkPoint = [ - { x: current.x1, y: current.y1, line: current, cnt: 0 }, - { x: current.x2, y: current.y2, line: current, cnt: 0 }, + { x: current.x1, y: current.y1, line: current, cnt: 0, onRoofLine: false }, + { x: current.x2, y: current.y2, line: current, cnt: 0, onRoofLine: false }, ] baseHipLines.forEach((line) => { if ((line.x1 === current.x1 && line.y1 === current.y1) || (line.x2 === current.x1 && line.y2 === current.y1)) { @@ -2134,11 +3636,43 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { checkPoint[1].cnt = checkPoint[1].cnt + 1 } }) + + /** 마루의 포인트가 지붕선 위에 있는경우는 제외 (케라바 등)*/ + roof.lines.forEach((line) => { + if ( + line.x1 === line.x2 && + checkPoint[0].x === line.x1 && + ((line.y1 <= checkPoint[0].y && line.y2 >= checkPoint[0].y) || (line.y1 >= checkPoint[0].y && line.y2 <= checkPoint[0].y)) + ) { + checkPoint[0].onRoofLine = true + } + if ( + line.y1 === line.y2 && + checkPoint[0].y === line.y1 && + ((line.x1 <= checkPoint[0].x && line.x2 >= checkPoint[0].x) || (line.x1 >= checkPoint[0].x && line.x2 <= checkPoint[0].x)) + ) { + checkPoint[0].onRoofLine = true + } + if ( + line.x1 === line.x2 && + checkPoint[1].x === line.x1 && + ((line.y1 <= checkPoint[1].y && line.y2 >= checkPoint[1].y) || (line.y1 >= checkPoint[1].y && line.y2 <= checkPoint[1].y)) + ) { + checkPoint[1].onRoofLine = true + } + if ( + line.y1 === line.y2 && + checkPoint[1].y === line.y1 && + ((line.x1 <= checkPoint[1].x && line.x2 >= checkPoint[1].x) || (line.x1 >= checkPoint[1].x && line.x2 <= checkPoint[1].x)) + ) { + checkPoint[1].onRoofLine = true + } + }) // console.log('checkPoint : ', checkPoint) - if (checkPoint[0].cnt === 0 || checkPoint[0].cnt % 2 !== 0) { + if ((checkPoint[0].cnt === 0 || checkPoint[0].cnt % 2 !== 0) && !checkPoint[0].onRoofLine) { unFinishedRidge.push(checkPoint[0]) } - if (checkPoint[1].cnt === 0 || checkPoint[1].cnt % 2 !== 0) { + if ((checkPoint[1].cnt === 0 || checkPoint[1].cnt % 2 !== 0) && !checkPoint[1].onRoofLine) { unFinishedRidge.push(checkPoint[1]) } }) @@ -2185,19 +3719,25 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { /**3. 라인이 부재인 마루의 모자란 라인을 찾는다. 라인은 그려진 추녀마루 중에 완성되지 않은 추녀마루와 확인한다.*/ /**3-1 라인을 그릴때 각도가 필요하기 때문에 각도를 구한다. 각도는 전체 지부의 각도가 같다면 하나로 처리 */ let degreeAllLine = [] - baseLines.forEach((line) => { - const pitch = line.attributes.pitch - const degree = line.attributes.degree - degreeAllLine.push(pitch > 0 ? getDegreeByChon(pitch) : degree) - }) + baseLines + .filter((line) => eavesType.includes(line.attributes.type)) + .forEach((line) => { + const pitch = line.attributes.pitch + const degree = line.attributes.degree + degreeAllLine.push(pitch > 0 ? getDegreeByChon(pitch) : degree) + }) let currentDegree, prevDegree degreeAllLine = [...new Set(degreeAllLine)] currentDegree = degreeAllLine[0] if (degreeAllLine.length === 1) { prevDegree = currentDegree + } else { + prevDegree = degreeAllLine[1] } + console.log('unFinishedRidge : ', unFinishedRidge) + console.log('unFinishedPoint : ', unFinishedPoint) /** 라인이 부재한 마루에 라인을 찾아 그린다.*/ unFinishedRidge.forEach((current) => { let checkPoints = [] @@ -2270,6 +3810,13 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { hipBasePoint = { x1: line.x1, y1: line.y1, x2: line.x2, y2: line.y2 } point = [mergePoint[0].x, mergePoint[0].y, mergePoint[3].x, mergePoint[3].y] + const theta = Big(Math.acos(Big(line.line.attributes.planeSize).div(line.line.attributes.actualSize))) + .times(180) + .div(Math.PI) + .round(1) + console.log('theta : ', theta.toNumber()) + prevDegree = theta.toNumber() + currentDegree = theta.toNumber() canvas.remove(line.line) baseHipLines = baseHipLines.filter((baseLine) => baseLine.line !== line.line) } @@ -2408,6 +3955,16 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { intersectPoints.forEach((is) => { const points = [current.x, current.y, is.x, is.y] + /** 추녀마루의 연결점이 처마라인이 아닌경우 return */ + let hasGable = false + baseLines + .filter((line) => (line.x1 === points[2] && line.y1 === points[3]) || (line.x2 === points[2] && line.y2 === points[3])) + .forEach((line) => { + if (!eavesType.includes(line.attributes.type)) { + hasGable = true + } + }) + if (hasGable) return const pointEdge = { vertex1: { x: points[0], y: points[1] }, vertex2: { x: points[2], y: points[3] } } const vectorX = Math.sign(Big(points[2]).minus(Big(points[0])).toNumber()) const vectorY = Math.sign(Big(points[3]).minus(Big(points[1])).toNumber()) @@ -2502,7 +4059,7 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { // canvas.renderAll() /** 직교 하는 포인트가 존재 할때 마루를 그린다. */ - if (orthogonalPoints.length > 0) { + if (orthogonalPoints.length > 0 && baseRidgeCount < getMaxRidge(baseLines.length)) { baseRidgeCount = baseRidgeCount + 1 const points = [current.x2, current.y2, orthogonalPoints[0].x2, orthogonalPoints[0].y2] const ridgeLine = drawRidgeLine(points, canvas, roof, textMode) @@ -2626,7 +4183,7 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { }) ridgePoints.sort((a, b) => a.distance - b.distance) - if (ridgePoints.length > 0) { + if (ridgePoints.length > 0 && baseRidgeCount < getMaxRidge(baseLines.length)) { const intersection = ridgePoints[0].intersection const isPoint = intersection.intersection const points = [hipPoint.x2, hipPoint.y2, isPoint.x, isPoint.y] @@ -2749,8 +4306,9 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { // console.log('hipStartPoint : ', hipStartPoint) /** 직선인 경우 마루를 그린다.*/ if ( - (hipStartPoint[0] === hipStartPoint[2] && hipStartPoint[1] !== hipStartPoint[3]) || - (hipStartPoint[0] !== hipStartPoint[2] && hipStartPoint[1] === hipStartPoint[3]) + ((hipStartPoint[0] === hipStartPoint[2] && hipStartPoint[1] !== hipStartPoint[3]) || + (hipStartPoint[0] !== hipStartPoint[2] && hipStartPoint[1] === hipStartPoint[3])) && + baseRidgeCount < getMaxRidge(baseLines.length) ) { // console.log('릿지1') const ridgeLine = drawRidgeLine(hipStartPoint, canvas, roof, textMode) @@ -2790,8 +4348,9 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { linePoint.intersectPoint.intersection.y, ] if ( - (isStartPoint[0] === isStartPoint[2] && isStartPoint[1] !== isStartPoint[3]) || - (isStartPoint[0] !== isStartPoint[2] && isStartPoint[1] === isStartPoint[3]) + ((isStartPoint[0] === isStartPoint[2] && isStartPoint[1] !== isStartPoint[3]) || + (isStartPoint[0] !== isStartPoint[2] && isStartPoint[1] === isStartPoint[3])) && + baseRidgeCount < getMaxRidge(baseLines.length) ) { const ridgeLine = drawRidgeLine(isStartPoint, canvas, roof, textMode) baseRidgeCount = baseRidgeCount + 1 @@ -3095,7 +4654,12 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { (line.x1 === ridgePoints[2] && line.y1 === ridgePoints[3] && line.x2 === ridgePoints[0] && line.y2 === ridgePoints[1]), ) - if (!baseIntersection && alreadyRidges.length === 0 && otherRidgeInterSection.length === 0) { + if ( + !baseIntersection && + alreadyRidges.length === 0 && + otherRidgeInterSection.length === 0 && + baseRidgeCount < getMaxRidge(baseLines.length) + ) { baseRidgeCount = baseRidgeCount + 1 const ridgeLine = drawRidgeLine(ridgePoints, canvas, roof, textMode) baseRidgeLines.push(ridgeLine) @@ -3109,7 +4673,7 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const hipEdge = { vertex1: { x: hipPoints[0], y: hipPoints[1] }, vertex2: { x: hipPoints[2], y: hipPoints[3] } } baseLines.forEach((line) => { const intersection = edgesIntersection(hipEdge, { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }) - if (intersection && !intersection.isIntersectionOutside) { + if (intersection && !intersection.isIntersectionOutside && eavesType.includes(line.attributes.type)) { baseIntersection = true } }) @@ -3151,7 +4715,25 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { baseHipLines.filter((hipLine2) => hipLine !== hipLine2).forEach((hipLine2) => {}) }) - roof.innerLines = [...baseRidgeLines, ...baseHipLines.map((line) => line.line)] + const innerLines = [...baseRidgeLines, ...baseGableRidgeLines, ...baseHipLines.map((line) => line.line)] + const uniqueInnerLines = [] + + innerLines.forEach((currentLine) => { + const sameLines = uniqueInnerLines.filter( + (line) => + line !== currentLine && + ((line.x1 === currentLine.x1 && line.y1 === currentLine.y1 && line.x2 === currentLine.x2 && line.y2 === currentLine.y2) || + (line.x1 === currentLine.x2 && line.y1 === currentLine.y2 && line.x2 === currentLine.x1 && line.y2 === currentLine.y1)), + ) + + if (sameLines.length === 0) { + uniqueInnerLines.push(currentLine) + } else { + sameLines.forEach((line) => canvas.remove(line)) + } + }) + canvas.renderAll() + roof.innerLines = uniqueInnerLines /** 확인용 라인, 포인트 제거 */ canvas @@ -3210,10 +4792,57 @@ const drawHipLine = (points, canvas, roof, textMode, currentRoof, prevDegree, cu }) canvas.add(hip) + hip.bringToFront() canvas.renderAll() return hip } +/** + * 라인의 흐름 방향에서 마주치는 지붕선의 포인트를 찾는다. + * @param roof + * @param baseLine + * @param endPoint + * @returns {*} + */ +const findRoofIntersection = (roof, baseLine, endPoint) => { + let intersectPoints = [] + const { x1, y1, x2, y2 } = baseLine + + const baseEdge = { + vertex1: { x: x1, y: y1 }, + vertex2: { x: x2, y: y2 }, + } + + /** 외벽선에서 라인 겹치는 경우에 대한 확인*/ + roof.lines.forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(baseEdge, lineEdge) + if ( + intersection && + !intersection.isIntersectionOutside && + Math.sign(endPoint.x - baseLine.x1) === Math.sign(endPoint.x - intersection.x) && + Math.sign(endPoint.y - baseLine.y1) === Math.sign(endPoint.y - intersection.y) + ) { + const intersectSize = endPoint.x + .minus(Big(intersection.x)) + .pow(2) + .plus(endPoint.y.minus(Big(intersection.y)).pow(2)) + .abs() + .sqrt() + .toNumber() + + intersectPoints.push({ + intersection, + size: intersectSize, + }) + } + }) + + return intersectPoints.reduce((prev, current) => { + return prev.size < current.size ? prev : current + }, intersectPoints[0]) +} + /** * 마루를 그린다. * @param points @@ -3247,6 +4876,7 @@ const drawRidgeLine = (points, canvas, roof, textMode) => { }, }) canvas.add(ridge) + ridge.bringToFront() canvas.renderAll() return ridge @@ -6068,7 +7698,7 @@ export const calcLinePlaneSize = (points) => { export const calcLineActualSize = (points, degree) => { const planeSize = calcLinePlaneSize(points) const theta = Big(Math.cos(Big(degree).times(Math.PI).div(180))) - return Big(planeSize).div(theta).round(1).toNumber() + return Big(planeSize).div(theta).round().toNumber() } export const createLinesFromPolygon = (points) => { @@ -6085,3 +7715,32 @@ export const createLinesFromPolygon = (points) => { } return lines } + +/** 포인트 정렬 가장왼쪽, 가장위 부터 */ +const getSortedPoint = (points) => { + const startPoint = points + .filter((point) => point.x === Math.min(...points.map((point) => point.x))) + .reduce((prev, curr) => { + return prev.y < curr.y ? prev : curr + }) + const sortedPoints = [] + sortedPoints.push(startPoint) + + let prevPoint = startPoint + + for (let i = 0; i < points.length - 1; i++) { + points + .filter((point) => !sortedPoints.includes(point)) + .forEach((point) => { + if (i % 2 === 1 && prevPoint.y === point.y) { + sortedPoints.push(point) + prevPoint = point + } + if (i % 2 === 0 && prevPoint.x === point.x) { + sortedPoints.push(point) + prevPoint = point + } + }) + } + return sortedPoints +} From 5f648632dded73953c8c5e7ef935fcebebcd248a Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Mon, 28 Apr 2025 18:07:54 +0900 Subject: [PATCH 10/46] =?UTF-8?q?=EC=B5=9C=EB=8B=A8=EA=B1=B0=EB=A6=AC=20ro?= =?UTF-8?q?of=20=EB=82=98=EB=88=84=EA=B8=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/usePolygon.js | 280 ++++++++++++++++++++++++++-------------- 1 file changed, 180 insertions(+), 100 deletions(-) diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js index fd5b5a69..9a5f39f8 100644 --- a/src/hooks/usePolygon.js +++ b/src/hooks/usePolygon.js @@ -1,7 +1,7 @@ import { ANGLE_TYPE, canvasState, currentAngleTypeSelector, pitchTextSelector } from '@/store/canvasAtom' import { useRecoilValue } from 'recoil' import { fabric } from 'fabric' -import { findAndRemoveClosestPoint, getDegreeByChon, getDegreeInOrientation, isPointOnLine } from '@/util/canvas-util' +import { findAndRemoveClosestPoint, getDegreeByChon, getDegreeInOrientation, isPointOnLine, toFixedWithoutRounding } from '@/util/canvas-util' import { QPolygon } from '@/components/fabric/QPolygon' import { isSamePoint, removeDuplicatePolygons } from '@/util/qpolygon-utils' import { flowDisplaySelector } from '@/store/settingAtom' @@ -757,7 +757,7 @@ export const usePolygon = () => { polygon.set({ visible: false }) let innerLines = [...polygon.innerLines] - // innerLine이 세팅이 안되어있는경우 찾아서 세팅한다. + /*// innerLine이 세팅이 안되어있는경우 찾아서 세팅한다. if (!innerLines || innerLines.length === 0) { let innerLineTypes = Object.keys(LINE_TYPE.SUBLINE).map((key, value) => LINE_TYPE.SUBLINE[key]) polygon.innerLines = canvas @@ -771,8 +771,7 @@ export const usePolygon = () => { ) innerLines = [...polygon.innerLines] - } - + }*/ canvas.renderAll() let polygonLines = [...polygon.lines] const roofs = [] @@ -830,8 +829,14 @@ export const usePolygon = () => { canvas.renderAll() polygonLines.forEach((line) => { + /*const originStroke = line.stroke + line.set({ stroke: 'red' }) + canvas.renderAll()*/ const intersections = [] innerLines.forEach((innerLine) => { + /*const originInnerStroke = innerLine.stroke + innerLine.set({ stroke: 'red' }) + canvas.renderAll()*/ if (isPointOnLine(line, innerLine.startPoint)) { if (isSamePoint(line.startPoint, innerLine.startPoint) || isSamePoint(line.endPoint, innerLine.startPoint)) { return @@ -844,8 +849,13 @@ export const usePolygon = () => { } intersections.push(innerLine.endPoint) } + /*innerLine.set({ stroke: originInnerStroke }) + canvas.renderAll()*/ }) line.set({ intersections }) + + /*line.set({ stroke: originStroke }) + canvas.renderAll()*/ }) const divideLines = polygonLines.filter((line) => line.intersections.length > 0) @@ -924,10 +934,52 @@ export const usePolygon = () => { }) //polygonLines에서 divideLines를 제거하고 newLines를 추가한다. - polygonLines = polygonLines.filter((line) => !divideLines.includes(line)) + polygonLines = polygonLines.filter((line) => line.intersections?.length === 0) + polygonLines = [...polygonLines, ...newLines] - const allLines = [...polygonLines, ...innerLines] + let allLines = [...polygonLines, ...innerLines] + + /*allLines.forEach((line) => { + const originColor = line.stroke + + line.set('stroke', 'red') + canvas.renderAll() + + line.set('stroke', originColor) + canvas.renderAll() + })*/ + + const allPoints = [] + + // test용 좌표 + const polygonLinesPoints = polygonLines.map((line) => { + return { startPoint: line.startPoint, endPoint: line.endPoint } + }) + + const innerLinesPoints = innerLines.map((line) => { + return { startPoint: line.startPoint, endPoint: line.endPoint } + }) + + polygonLinesPoints.forEach(({ startPoint, endPoint }) => { + allPoints.push(startPoint) + allPoints.push(endPoint) + }) + + innerLinesPoints.forEach(({ startPoint, endPoint }) => { + allPoints.push(startPoint) + allPoints.push(endPoint) + }) + + // allPoints에서 중복을 제거한다. + const uniquePoints = allPoints.filter((point, index, self) => { + return ( + index === + self.findIndex((p) => { + return isSamePoint(p, point) + }) + ) + }) // 2025-02-19 대각선은 케라바, 직선은 용마루로 세팅 innerLines.forEach((innerLine) => { @@ -978,102 +1030,23 @@ export const usePolygon = () => { line.endPoint = endPoint }) - // polygon line에서 각각 출발한다. - polygonLines.forEach((line) => { - /*line.set({ strokeWidth: 5, stroke: 'green' }) - canvas.add(line) - canvas.renderAll()*/ - const startPoint = { ...line.startPoint } // 시작점 - let arrivalPoint = { ...line.endPoint } // 도착점 + // polygonLines에서 시작점 혹은 끝점이 innerLines와 연결된 line만 가져온다. + let startLines = polygonLines.filter((line) => { + const startPoint = line.startPoint + const endPoint = line.endPoint - let currentPoint = startPoint - let roofPoints = [startPoint] - - let startLine = line - let visitPoints = [startPoint] - let visitLines = [startLine] - let notVisitedLines = [] - let cnt = 0 - - while (!isSamePoint(currentPoint, arrivalPoint)) { - //현재 점으로 부터 갈 수 있는 다른 라인을 찾는다. - let nextLines = allLines.filter( - (line2) => - (isSamePoint(line2.startPoint, currentPoint) || isSamePoint(line2.endPoint, currentPoint)) && - line !== line2 && - innerLines.includes(line2) && - !visitLines.includes(line2), + return innerLines.some((innerLine) => { + return ( + isSamePoint(innerLine.startPoint, startPoint) || + isSamePoint(innerLine.endPoint, startPoint) || + isSamePoint(innerLine.startPoint, endPoint) || + isSamePoint(innerLine.endPoint, endPoint) ) - - if (nextLines.length === 0) { - nextLines = allLines.filter( - (line2) => - (isSamePoint(line2.startPoint, currentPoint) || isSamePoint(line2.endPoint, currentPoint)) && - line !== line2 && - !visitLines.includes(line2), - ) - } - - if (nextLines.length === 0) { - //아직 안갔던 line중 0번째를 선택한다. - if (notVisitedLines.length === 0) { - break - } else { - let notVisitedLine = notVisitedLines.shift() - roofPoints = [...notVisitedLine.roofPoints] - currentPoint = { ...notVisitedLine.currentPoint } - continue - } - } - - let comparisonPoints = [] - - nextLines.forEach((nextLine) => { - if (isSamePoint(nextLine.startPoint, currentPoint)) { - comparisonPoints.push(nextLine.endPoint) - } else { - comparisonPoints.push(nextLine.startPoint) - } - }) - - comparisonPoints = comparisonPoints.filter((point) => !visitPoints.some((visitPoint) => isSamePoint(visitPoint, point))) - comparisonPoints = comparisonPoints.filter((point) => !isSamePoint(point, currentPoint)) - - const minDistancePoint = comparisonPoints.reduce((prev, current) => { - const prevDistance = Math.sqrt(Math.pow(prev.x - arrivalPoint.x, 2) + Math.pow(prev.y - arrivalPoint.y, 2)) - const currentDistance = Math.sqrt(Math.pow(current.x - arrivalPoint.x, 2) + Math.pow(current.y - arrivalPoint.y, 2)) - - return prevDistance < currentDistance ? prev : current - }, comparisonPoints[0]) - - nextLines.forEach((nextLine) => { - if (isSamePoint(nextLine.startPoint, minDistancePoint) || isSamePoint(nextLine.endPoint, minDistancePoint)) { - visitLines.push(nextLine) - } else { - notVisitedLines.push({ - line: nextLine, - endPoint: nextLine.endPoint, - startPoint: nextLine.startPoint, - currentPoint: { ...currentPoint }, - roofPoints: [...roofPoints], - }) - } - }) - - currentPoint = { ...minDistancePoint } - roofPoints.push(currentPoint) - cnt++ - - if (cnt > 100) { - break - } - } - roofs.push(roofPoints) - canvas.remove(line) - canvas.renderAll() + }) }) - const newRoofs = removeDuplicatePolygons(roofs.filter((roof) => roof.length < 100)) + // 나눠서 중복 제거된 roof return + const newRoofs = getSplitRoofsPoints(startLines, allLines, innerLines, uniquePoints) newRoofs.forEach((roofPoint, index) => { let defense, pitch @@ -1104,7 +1077,7 @@ export const usePolygon = () => { }) // blue로 생성된 것들은 대표라인이 될 수 없음. - representLines = representLines.filter((line) => line.stroke !== 'blue') + // representLines = representLines.filter((line) => line.stroke !== 'blue') // representLines중 가장 긴 line을 찾는다. representLines.forEach((line) => { if (!representLine) { @@ -1115,7 +1088,7 @@ export const usePolygon = () => { } } }) - const direction = polygon.direction ?? representLine.direction + const direction = polygon.direction ?? representLine?.direction const polygonDirection = polygon.direction switch (direction) { @@ -1174,6 +1147,113 @@ export const usePolygon = () => { }) } + const getSplitRoofsPoints = (startLines, allLines, innerLines, uniquePoints) => { + // 거리 계산 + function calcDistance(p1, p2) { + return Math.hypot(p2.x - p1.x, p2.y - p1.y) + } + + // 바로 연결 체크 + function isDirectlyConnected(start, end, graph) { + const startKey = `${start.x},${start.y}` + if (!graph[startKey]) return false + return graph[startKey].some((neighbor) => neighbor.point.x === end.x && neighbor.point.y === end.y) + } + + // Dijkstra 최단 경로 + function findShortestPath(start, end, graph) { + const startKey = `${start.x},${start.y}` + const endKey = `${end.x},${end.y}` + + const distances = {} + const previous = {} + const visited = new Set() + const queue = [{ key: startKey, dist: 0 }] + + for (const key in graph) { + distances[key] = Infinity + } + distances[startKey] = 0 + + while (queue.length > 0) { + queue.sort((a, b) => a.dist - b.dist) + const { key } = queue.shift() + if (visited.has(key)) continue + visited.add(key) + + for (const neighbor of graph[key]) { + const neighborKey = `${neighbor.point.x},${neighbor.point.y}` + const alt = distances[key] + neighbor.distance + if (alt < distances[neighborKey]) { + distances[neighborKey] = alt + previous[neighborKey] = key + queue.push({ key: neighborKey, dist: alt }) + } + } + } + + // 경로 복구 + const path = [] + let currentKey = endKey + + if (!previous[currentKey]) { + return null // 경로 없음 + } + + while (currentKey !== startKey) { + const [x, y] = currentKey.split(',').map(Number) + path.unshift({ x, y }) + currentKey = previous[currentKey] + } + path.unshift(start) + + return path + } + + // 최종 함수 + function getPath(start, end, graph) { + if (isDirectlyConnected(start, end, graph)) { + return null + } else { + const path = findShortestPath(start, end, graph) + if (!path || path.length < 3) { + return null + } + return path + } + } + + const roofs = [] + + startLines.forEach((line) => { + // 그래프 생성 + const graph = {} + const edges = allLines + .filter((line2) => line !== line2) + .map((line) => { + return [line.startPoint, line.endPoint] + }) + + for (const [p1, p2] of edges) { + const key1 = `${p1.x},${p1.y}` + const key2 = `${p2.x},${p2.y}` + const distance = calcDistance(p1, p2) + + if (!graph[key1]) graph[key1] = [] + if (!graph[key2]) graph[key2] = [] + + graph[key1].push({ point: p2, distance }) + graph[key2].push({ point: p1, distance }) + } + + const startPoint = { ...line.startPoint } // 시작점 + let arrivalPoint = { ...line.endPoint } // 도착점 + roofs.push(getPath(startPoint, arrivalPoint, graph)) + }) + + return removeDuplicatePolygons(roofs.filter((roof) => roof.length < 100)) + } + const splitPolygonWithSeparate = (separates) => { separates.forEach((separate) => { const points = separate.lines.map((line) => { From a45bba3a7bc0e2dfad959d7c5d363be81dc310ab Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Tue, 29 Apr 2025 14:26:21 +0900 Subject: [PATCH 11/46] =?UTF-8?q?=EC=98=A4=EC=B0=A8=EB=B2=94=EC=9C=84=20?= =?UTF-8?q?=EA=B3=84=EC=82=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/usePolygon.js | 82 +++++++++++++++++++++++++---------------- 1 file changed, 50 insertions(+), 32 deletions(-) diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js index 9a5f39f8..43a78eae 100644 --- a/src/hooks/usePolygon.js +++ b/src/hooks/usePolygon.js @@ -822,10 +822,10 @@ export const usePolygon = () => { line.endPoint = endPoint }) - polygonLines.forEach((line) => { + /*polygonLines.forEach((line) => { line.set({ strokeWidth: 10 }) canvas.add(line) - }) + })*/ canvas.renderAll() polygonLines.forEach((line) => { @@ -1148,31 +1148,47 @@ export const usePolygon = () => { } const getSplitRoofsPoints = (startLines, allLines, innerLines, uniquePoints) => { + // ==== Utility functions ==== + + function isSamePoint(p1, p2, epsilon = 1) { + return Math.abs(p1.x - p2.x) <= epsilon && Math.abs(p1.y - p2.y) <= epsilon + } + + function normalizePoint(p, epsilon = 1) { + return { + x: Math.round(p.x / epsilon) * epsilon, + y: Math.round(p.y / epsilon) * epsilon, + } + } + + function pointToKey(p, epsilon = 1) { + const norm = normalizePoint(p, epsilon) + return `${norm.x},${norm.y}` + } + // 거리 계산 function calcDistance(p1, p2) { return Math.hypot(p2.x - p1.x, p2.y - p1.y) } - // 바로 연결 체크 - function isDirectlyConnected(start, end, graph) { - const startKey = `${start.x},${start.y}` - if (!graph[startKey]) return false - return graph[startKey].some((neighbor) => neighbor.point.x === end.x && neighbor.point.y === end.y) - } + // ==== Direct edge check ==== - // Dijkstra 최단 경로 - function findShortestPath(start, end, graph) { - const startKey = `${start.x},${start.y}` - const endKey = `${end.x},${end.y}` + function isDirectlyConnected(start, end, graph, epsilon = 1) { + const startKey = pointToKey(start, epsilon) + return (graph[startKey] || []).some((neighbor) => isSamePoint(neighbor.point, end, epsilon)) + } + // ==== Dijkstra pathfinding ==== + + function findShortestPath(start, end, graph, epsilon = 1) { + const startKey = pointToKey(start, epsilon) + const endKey = pointToKey(end, epsilon) const distances = {} const previous = {} const visited = new Set() const queue = [{ key: startKey, dist: 0 }] - for (const key in graph) { - distances[key] = Infinity - } + for (const key in graph) distances[key] = Infinity distances[startKey] = 0 while (queue.length > 0) { @@ -1181,8 +1197,8 @@ export const usePolygon = () => { if (visited.has(key)) continue visited.add(key) - for (const neighbor of graph[key]) { - const neighborKey = `${neighbor.point.x},${neighbor.point.y}` + for (const neighbor of graph[key] || []) { + const neighborKey = pointToKey(neighbor.point, epsilon) const alt = distances[key] + neighbor.distance if (alt < distances[neighborKey]) { distances[neighborKey] = alt @@ -1192,35 +1208,37 @@ export const usePolygon = () => { } } - // 경로 복구 const path = [] let currentKey = endKey - if (!previous[currentKey]) { - return null // 경로 없음 - } + if (!previous[currentKey]) return null while (currentKey !== startKey) { const [x, y] = currentKey.split(',').map(Number) path.unshift({ x, y }) currentKey = previous[currentKey] } - path.unshift(start) + + const [sx, sy] = startKey.split(',').map(Number) + path.unshift({ x: sx, y: sy }) return path } // 최종 함수 - function getPath(start, end, graph) { - if (isDirectlyConnected(start, end, graph)) { + function getPath(start, end, graph, epsilon = 1) { + if (isDirectlyConnected(start, end, graph, epsilon)) { + console.log('직선 연결 있음. 무시.') return null - } else { - const path = findShortestPath(start, end, graph) - if (!path || path.length < 3) { - return null - } - return path } + + const path = findShortestPath(start, end, graph, epsilon) + if (!path || path.length < 3) { + console.log('경로 존재하나 3개 미만 좌표. 무시.') + return null + } + + return path } const roofs = [] @@ -1235,8 +1253,8 @@ export const usePolygon = () => { }) for (const [p1, p2] of edges) { - const key1 = `${p1.x},${p1.y}` - const key2 = `${p2.x},${p2.y}` + const key1 = pointToKey(p1) + const key2 = pointToKey(p2) const distance = calcDistance(p1, p2) if (!graph[key1]) graph[key1] = [] From 9eda4aa834cf597cc2d51ce6ba1de66476ef5f75 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Tue, 29 Apr 2025 17:48:38 +0900 Subject: [PATCH 12/46] =?UTF-8?q?=EC=A0=9C=EB=8C=80=EB=A1=9C=20=EC=95=88?= =?UTF-8?q?=EB=82=98=EB=88=A0=EC=A7=80=EB=8A=94=20=ED=98=84=EC=83=81=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/usePolygon.js | 9 +++++++-- src/util/canvas-util.js | 15 ++++++++++----- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js index 43a78eae..93659a08 100644 --- a/src/hooks/usePolygon.js +++ b/src/hooks/usePolygon.js @@ -754,7 +754,7 @@ export const usePolygon = () => { } const splitPolygonWithLines = (polygon) => { - polygon.set({ visible: false }) + // polygon.set({ visible: false }) let innerLines = [...polygon.innerLines] /*// innerLine이 세팅이 안되어있는경우 찾아서 세팅한다. @@ -934,8 +934,9 @@ export const usePolygon = () => { }) //polygonLines에서 divideLines를 제거하고 newLines를 추가한다. + newLines = newLines.filter((line) => !(Math.abs(line.startPoint.x - line.endPoint.x) < 1 && Math.abs(line.startPoint.y - line.endPoint.y) < 1)) polygonLines = polygonLines.filter((line) => line.intersections?.length === 0) - + const originPolygonLines = [...polygonLines] polygonLines = [...polygonLines, ...newLines] let allLines = [...polygonLines, ...innerLines] @@ -1045,6 +1046,10 @@ export const usePolygon = () => { }) }) + if (startLines.length === 0) { + startLines = originPolygonLines + } + // 나눠서 중복 제거된 roof return const newRoofs = getSplitRoofsPoints(startLines, allLines, innerLines, uniquePoints) diff --git a/src/util/canvas-util.js b/src/util/canvas-util.js index 1146afa7..9eb6e0e5 100644 --- a/src/util/canvas-util.js +++ b/src/util/canvas-util.js @@ -515,26 +515,31 @@ export const sortedPointLessEightPoint = (points) => { * @param line * @param point * @returns {boolean} + * @param epsilon */ // 직선의 방정식. // 방정식은 ax + by + c = 0이며, 점의 좌표를 대입하여 계산된 값은 직선과 점 사이의 관계를 나타낸다. -export function isPointOnLine({ x1, y1, x2, y2 }, { x, y }) { - /*const a = line.y2 - line.y1 +export function isPointOnLine({ x1, y1, x2, y2 }, { x, y }, epsilon = 2) { + const a = y2 - y1 + const b = x1 - x2 + const c = x2 * y1 - x1 * y2 + return Math.abs(a * x + b * y + c) < 1000 + /*/!*const a = line.y2 - line.y1 const b = line.x1 - line.x2 const c = line.x2 * line.y1 - line.x1 * line.y2 const result = Math.abs(a * point.x + b * point.y + c) / 100 // 점이 선 위에 있는지 확인 - return result <= 10*/ + return result <= 10*!/ // 직선 방정식 만족 여부 확인 const crossProduct = (y - y1) * (x2 - x1) - (x - x1) * (y2 - y1) - if (Math.abs(crossProduct) > 5) return false // 작은 오차 허용 + if (Math.abs(crossProduct) > 10) return false // 작은 오차 허용 // 점이 선분의 범위 내에 있는지 확인 const withinXRange = Math.min(x1, x2) <= x && x <= Math.max(x1, x2) const withinYRange = Math.min(y1, y2) <= y && y <= Math.max(y1, y2) - return withinXRange && withinYRange + return withinXRange && withinYRange*/ } /** * 점과 가까운 line 찾기 From 9f88880ac2a93c25edff47632a9f070e070ec29d Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Tue, 29 Apr 2025 17:50:36 +0900 Subject: [PATCH 13/46] =?UTF-8?q?=EC=A3=BC=EC=84=9D=20=EB=82=B4=EC=9A=A9?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/usePolygon.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js index 93659a08..8fc150e7 100644 --- a/src/hooks/usePolygon.js +++ b/src/hooks/usePolygon.js @@ -754,7 +754,7 @@ export const usePolygon = () => { } const splitPolygonWithLines = (polygon) => { - // polygon.set({ visible: false }) + polygon.set({ visible: false }) let innerLines = [...polygon.innerLines] /*// innerLine이 세팅이 안되어있는경우 찾아서 세팅한다. From 8412462a8b7ef229fa834e3780573477e4882040 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Wed, 30 Apr 2025 13:08:58 +0900 Subject: [PATCH 14/46] =?UTF-8?q?=EB=9D=BC=EC=9D=B8=20=EC=9C=84=EC=97=90?= =?UTF-8?q?=20=EC=9E=88=EB=8A=94=EC=A7=80=20=ED=99=95=EC=9D=B8=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=EC=88=98=EC=A0=95=20=EC=A7=80=EB=B6=95=EC=9E=AC=20?= =?UTF-8?q?=ED=95=A0=EB=8B=B9=20=EC=8B=9C=20=EC=98=A4=EB=A5=98=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/common/useRoofFn.js | 250 +++++++++++++++++----------------- src/hooks/usePolygon.js | 4 +- src/util/canvas-util.js | 8 +- 3 files changed, 133 insertions(+), 129 deletions(-) diff --git a/src/hooks/common/useRoofFn.js b/src/hooks/common/useRoofFn.js index c49128e7..38246273 100644 --- a/src/hooks/common/useRoofFn.js +++ b/src/hooks/common/useRoofFn.js @@ -23,102 +23,65 @@ export function useRoofFn() { //면형상 선택 클릭시 지붕 패턴 입히기 function setSurfaceShapePattern(polygon, mode = 'onlyBorder', trestleMode = false, roofMaterial, isForceChange = false, isDisplay = false) { - if (!polygon) { - return - } - if (polygon.points.length < 3) { - return - } - if (isForceChange && !isDisplay) { - /*if (polygon.roofMaterial) { + try { + if (!polygon) { + return + } + if (polygon.points.length < 3) { + return + } + if (isForceChange && !isDisplay) { + /*if (polygon.roofMaterial) { polygon.roofMaterial = null }*/ - } - if (!roofMaterial) { - roofMaterial = polygon.roofMaterial ?? selectedRoofMaterial - } - - const ratio = window.devicePixelRatio || 1 - const layout = roofMaterial.layout - - let width = (roofMaterial.width || 226) / 10 - let height = (roofMaterial.length || 158) / 10 - const index = roofMaterial.index ?? 0 - let roofStyle = 2 - const inputPatternSize = { width: width, height: height } //임시 사이즈 - const patternSize = { ...inputPatternSize } // 입력된 값을 뒤집기 위해 - - if (polygon.direction === 'east' || polygon.direction === 'west') { - //세로형이면 width height를 바꿈 - ;[patternSize.width, patternSize.height] = [inputPatternSize.height, patternSize.width] - } - - // 패턴 소스를 위한 임시 캔버스 생성 - const patternSourceCanvas = document.createElement('canvas') - patternSourceCanvas.width = polygon.width * ratio - patternSourceCanvas.height = polygon.height * ratio - const ctx = patternSourceCanvas.getContext('2d') - let offset = roofStyle === 1 ? 0 : patternSize.width / 2 - - const rows = Math.floor(patternSourceCanvas.height / patternSize.height) - const cols = Math.floor(patternSourceCanvas.width / patternSize.width) - - ctx.strokeStyle = mode === 'allPainted' ? 'black' : ROOF_COLOR[index] - ctx.lineWidth = 2 - ctx.fillStyle = mode === 'allPainted' ? 'rgba(0, 159, 64, 0.7)' : 'white' - - if (trestleMode) { - ctx.strokeStyle = 'black' - ctx.lineWidth = 0.2 - ctx.fillStyle = 'rgba(0, 0, 0, 0.1)' - } else { - ctx.fillStyle = 'rgba(255, 255, 255, 1)' - } - - if (polygon.direction === 'east' || polygon.direction === 'west') { - offset = roofStyle === 1 ? 0 : patternSize.height / 2 - for (let col = 0; col <= cols; col++) { - const x = col * patternSize.width - const yStart = 0 - const yEnd = patternSourceCanvas.height - ctx.beginPath() - ctx.moveTo(x, yStart) // 선 시작점 - ctx.lineTo(x, yEnd) // 선 끝점 - ctx.stroke() - if (mode === 'allPainted' || trestleMode) { - ctx.fillRect(x, yStart, patternSize.width, yEnd - yStart) - } - - for (let row = 0; row <= rows; row++) { - const y = layout === ROOF_MATERIAL_LAYOUT.STAIRS ? row * patternSize.height + (col % 2 === 0 ? 0 : offset) : row * patternSize.height - const xStart = col * patternSize.width - const xEnd = xStart + patternSize.width - ctx.beginPath() - ctx.moveTo(xStart, y) // 선 시작점 - ctx.lineTo(xEnd, y) // 선 끝점 - ctx.stroke() - if (mode === 'allPainted' || trestleMode) { - ctx.fillRect(xStart, y, xEnd - xStart, patternSize.height) - } - } } - } else { - for (let row = 0; row <= rows; row++) { - const y = row * patternSize.height + if (!roofMaterial) { + roofMaterial = polygon.roofMaterial ?? selectedRoofMaterial + } - ctx.beginPath() - ctx.moveTo(0, y) // 선 시작점 - ctx.lineTo(patternSourceCanvas.width, y) // 선 끝점 - ctx.stroke() - if (mode === 'allPainted' || trestleMode) { - ctx.fillRect(0, y, patternSourceCanvas.width, patternSize.height) - } + const ratio = window.devicePixelRatio || 1 + const layout = roofMaterial.layout + let width = (roofMaterial.width || 226) / 10 + let height = (roofMaterial.length || 158) / 10 + const index = roofMaterial.index ?? 0 + let roofStyle = 2 + const inputPatternSize = { width: width, height: height } //임시 사이즈 + const patternSize = { ...inputPatternSize } // 입력된 값을 뒤집기 위해 + + if (polygon.direction === 'east' || polygon.direction === 'west') { + //세로형이면 width height를 바꿈 + ;[patternSize.width, patternSize.height] = [inputPatternSize.height, patternSize.width] + } + + // 패턴 소스를 위한 임시 캔버스 생성 + const patternSourceCanvas = document.createElement('canvas') + patternSourceCanvas.width = polygon.width * ratio + patternSourceCanvas.height = polygon.height * ratio + const ctx = patternSourceCanvas.getContext('2d') + let offset = roofStyle === 1 ? 0 : patternSize.width / 2 + + const rows = Math.floor(patternSourceCanvas.height / patternSize.height) + const cols = Math.floor(patternSourceCanvas.width / patternSize.width) + + ctx.strokeStyle = mode === 'allPainted' ? 'black' : ROOF_COLOR[index] + ctx.lineWidth = 2 + ctx.fillStyle = mode === 'allPainted' ? 'rgba(0, 159, 64, 0.7)' : 'white' + + if (trestleMode) { + ctx.strokeStyle = 'black' + ctx.lineWidth = 0.2 + ctx.fillStyle = 'rgba(0, 0, 0, 0.1)' + } else { + ctx.fillStyle = 'rgba(255, 255, 255, 1)' + } + + if (polygon.direction === 'east' || polygon.direction === 'west') { + offset = roofStyle === 1 ? 0 : patternSize.height / 2 for (let col = 0; col <= cols; col++) { - const x = layout === ROOF_MATERIAL_LAYOUT.STAIRS ? col * patternSize.width + (row % 2 === 0 ? 0 : offset) : col * patternSize.width - const yStart = row * patternSize.height - const yEnd = yStart + patternSize.height - + const x = col * patternSize.width + const yStart = 0 + const yEnd = patternSourceCanvas.height ctx.beginPath() ctx.moveTo(x, yStart) // 선 시작점 ctx.lineTo(x, yEnd) // 선 끝점 @@ -126,49 +89,90 @@ export function useRoofFn() { if (mode === 'allPainted' || trestleMode) { ctx.fillRect(x, yStart, patternSize.width, yEnd - yStart) } + + for (let row = 0; row <= rows; row++) { + const y = layout === ROOF_MATERIAL_LAYOUT.STAIRS ? row * patternSize.height + (col % 2 === 0 ? 0 : offset) : row * patternSize.height + const xStart = col * patternSize.width + const xEnd = xStart + patternSize.width + ctx.beginPath() + ctx.moveTo(xStart, y) // 선 시작점 + ctx.lineTo(xEnd, y) // 선 끝점 + ctx.stroke() + if (mode === 'allPainted' || trestleMode) { + ctx.fillRect(xStart, y, xEnd - xStart, patternSize.height) + } + } + } + } else { + for (let row = 0; row <= rows; row++) { + const y = row * patternSize.height + + ctx.beginPath() + ctx.moveTo(0, y) // 선 시작점 + ctx.lineTo(patternSourceCanvas.width, y) // 선 끝점 + ctx.stroke() + if (mode === 'allPainted' || trestleMode) { + ctx.fillRect(0, y, patternSourceCanvas.width, patternSize.height) + } + + for (let col = 0; col <= cols; col++) { + const x = layout === ROOF_MATERIAL_LAYOUT.STAIRS ? col * patternSize.width + (row % 2 === 0 ? 0 : offset) : col * patternSize.width + const yStart = row * patternSize.height + const yEnd = yStart + patternSize.height + + ctx.beginPath() + ctx.moveTo(x, yStart) // 선 시작점 + ctx.lineTo(x, yEnd) // 선 끝점 + ctx.stroke() + if (mode === 'allPainted' || trestleMode) { + ctx.fillRect(x, yStart, patternSize.width, yEnd - yStart) + } + } } } - } - const hachingPatternSourceCanvas = document.createElement('canvas') + const hachingPatternSourceCanvas = document.createElement('canvas') - if (mode === 'lineHatch') { - hachingPatternSourceCanvas.width = polygon.width * ratio - hachingPatternSourceCanvas.height = polygon.height * ratio + if (mode === 'lineHatch') { + hachingPatternSourceCanvas.width = polygon.width * ratio + hachingPatternSourceCanvas.height = polygon.height * ratio - const ctx1 = hachingPatternSourceCanvas.getContext('2d') + const ctx1 = hachingPatternSourceCanvas.getContext('2d') - const gap = 10 + const gap = 10 - ctx1.strokeStyle = 'green' // 선 색상 - ctx1.lineWidth = 0.3 // 선 두께 + ctx1.strokeStyle = 'green' // 선 색상 + ctx1.lineWidth = 0.3 // 선 두께 - for (let x = 0; x < hachingPatternSourceCanvas.width + hachingPatternSourceCanvas.height; x += gap) { - ctx1.beginPath() - ctx1.moveTo(x, 0) // 선 시작점 - ctx1.lineTo(0, x) // 선 끝점 - ctx1.stroke() + for (let x = 0; x < hachingPatternSourceCanvas.width + hachingPatternSourceCanvas.height; x += gap) { + ctx1.beginPath() + ctx1.moveTo(x, 0) // 선 시작점 + ctx1.lineTo(0, x) // 선 끝점 + ctx1.stroke() + } } + + const combinedPatternCanvas = document.createElement('canvas') + combinedPatternCanvas.width = polygon.width * ratio + combinedPatternCanvas.height = polygon.height * ratio + const combinedCtx = combinedPatternCanvas.getContext('2d') + // 첫 번째 패턴을 그린 후 두 번째 패턴을 덧입힘 + combinedCtx.drawImage(patternSourceCanvas, 0, 0) + combinedCtx.drawImage(hachingPatternSourceCanvas, 0, 0) + + // 패턴 생성 + const pattern = new fabric.Pattern({ + source: combinedPatternCanvas, + repeat: 'repeat', + }) + + polygon.set('fill', null) + polygon.set('fill', pattern) + polygon.roofMaterial = roofMaterial + polygon.canvas?.renderAll() + } catch (e) { + console.log(e) } - - const combinedPatternCanvas = document.createElement('canvas') - combinedPatternCanvas.width = polygon.width * ratio - combinedPatternCanvas.height = polygon.height * ratio - const combinedCtx = combinedPatternCanvas.getContext('2d') - // 첫 번째 패턴을 그린 후 두 번째 패턴을 덧입힘 - combinedCtx.drawImage(patternSourceCanvas, 0, 0) - combinedCtx.drawImage(hachingPatternSourceCanvas, 0, 0) - - // 패턴 생성 - const pattern = new fabric.Pattern({ - source: combinedPatternCanvas, - repeat: 'repeat', - }) - - polygon.set('fill', null) - polygon.set('fill', pattern) - polygon.roofMaterial = roofMaterial - polygon.canvas?.renderAll() } function removeRoofMaterial(roof = currentObject) { diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js index 8fc150e7..49472b08 100644 --- a/src/hooks/usePolygon.js +++ b/src/hooks/usePolygon.js @@ -1234,13 +1234,13 @@ export const usePolygon = () => { function getPath(start, end, graph, epsilon = 1) { if (isDirectlyConnected(start, end, graph, epsilon)) { console.log('직선 연결 있음. 무시.') - return null + return [] } const path = findShortestPath(start, end, graph, epsilon) if (!path || path.length < 3) { console.log('경로 존재하나 3개 미만 좌표. 무시.') - return null + return [] } return path diff --git a/src/util/canvas-util.js b/src/util/canvas-util.js index 9eb6e0e5..74c5c2a4 100644 --- a/src/util/canvas-util.js +++ b/src/util/canvas-util.js @@ -520,17 +520,17 @@ export const sortedPointLessEightPoint = (points) => { // 직선의 방정식. // 방정식은 ax + by + c = 0이며, 점의 좌표를 대입하여 계산된 값은 직선과 점 사이의 관계를 나타낸다. export function isPointOnLine({ x1, y1, x2, y2 }, { x, y }, epsilon = 2) { - const a = y2 - y1 + /*const a = y2 - y1 const b = x1 - x2 const c = x2 * y1 - x1 * y2 - return Math.abs(a * x + b * y + c) < 1000 + return Math.abs(a * x + b * y + c) < 1000*/ /*/!*const a = line.y2 - line.y1 const b = line.x1 - line.x2 const c = line.x2 * line.y1 - line.x1 * line.y2 const result = Math.abs(a * point.x + b * point.y + c) / 100 // 점이 선 위에 있는지 확인 - return result <= 10*!/ + return result <= 10*!*/ // 직선 방정식 만족 여부 확인 const crossProduct = (y - y1) * (x2 - x1) - (x - x1) * (y2 - y1) if (Math.abs(crossProduct) > 10) return false // 작은 오차 허용 @@ -539,7 +539,7 @@ export function isPointOnLine({ x1, y1, x2, y2 }, { x, y }, epsilon = 2) { const withinXRange = Math.min(x1, x2) <= x && x <= Math.max(x1, x2) const withinYRange = Math.min(y1, y2) <= y && y <= Math.max(y1, y2) - return withinXRange && withinYRange*/ + return withinXRange && withinYRange } /** * 점과 가까운 line 찾기 From adc0c41a672ca990d3c30e3cbbe3abb25f7566b4 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Wed, 30 Apr 2025 13:18:48 +0900 Subject: [PATCH 15/46] =?UTF-8?q?=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useLine.js | 8 ++++---- src/hooks/usePolygon.js | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/hooks/useLine.js b/src/hooks/useLine.js index b447d4f0..bdbd5d6f 100644 --- a/src/hooks/useLine.js +++ b/src/hooks/useLine.js @@ -38,12 +38,12 @@ export const useLine = () => { line.set({ visible: false, }) - canvas - ?.getObjects() - .find((obj) => obj.parentId === line.id) - .set({ + const obj = canvas?.getObjects().find((obj) => obj.parentId === line.id) + if (obj) { + obj.set({ visible: false, }) + } canvas?.renderAll() } diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js index 49472b08..89f1b40b 100644 --- a/src/hooks/usePolygon.js +++ b/src/hooks/usePolygon.js @@ -1110,7 +1110,7 @@ export const usePolygon = () => { defense = 'north' break } - pitch = polygon.lines[index].attributes?.pitch ?? 0 + pitch = polygon.lines[index]?.attributes?.pitch ?? 0 const roof = new QPolygon(roofPoint, { fontSize: polygon.fontSize, From 937f2eb3feadaa95ad1742ddb84c9db352ed61c6 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Wed, 30 Apr 2025 13:26:05 +0900 Subject: [PATCH 16/46] =?UTF-8?q?x=EA=B0=80=20=EC=A0=84=EB=B6=80=20?= =?UTF-8?q?=EA=B0=99=EA=B1=B0=EB=82=98,=20y=EA=B0=80=20=EC=A0=84=EB=B6=80?= =?UTF-8?q?=20=EA=B0=99=EC=9D=80=EA=B2=BD=EC=9A=B0=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/qpolygon-utils.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index 9fbfc6a3..131e9232 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -303,7 +303,13 @@ export function removeDuplicatePolygons(polygons) { } }) - return uniquePolygons + // x가 전부 같거나, y가 전부 같은 경우 제거 + return uniquePolygons.filter((polygon) => { + const xValues = polygon.map((point) => point.x) + const yValues = polygon.map((point) => point.y) + + return !(xValues.every((x) => x === xValues[0]) || yValues.every((y) => y === yValues[0])) + }) } export const isSamePoint = (a, b) => { From 902d13b2739e2a757fe50a0de58d46b4ae3f362a Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Wed, 30 Apr 2025 16:17:03 +0900 Subject: [PATCH 17/46] =?UTF-8?q?=EC=98=A4=EC=B0=A8=20=EB=B2=94=EC=9C=84?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/canvas-util.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/canvas-util.js b/src/util/canvas-util.js index 74c5c2a4..6eef894e 100644 --- a/src/util/canvas-util.js +++ b/src/util/canvas-util.js @@ -533,7 +533,7 @@ export function isPointOnLine({ x1, y1, x2, y2 }, { x, y }, epsilon = 2) { return result <= 10*!*/ // 직선 방정식 만족 여부 확인 const crossProduct = (y - y1) * (x2 - x1) - (x - x1) * (y2 - y1) - if (Math.abs(crossProduct) > 10) return false // 작은 오차 허용 + if (Math.abs(crossProduct) > 500) return false // 작은 오차 허용 // 점이 선분의 범위 내에 있는지 확인 const withinXRange = Math.min(x1, x2) <= x && x <= Math.max(x1, x2) From a7eb2f7598212932adca274bf433d193ae9439e1 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Wed, 30 Apr 2025 18:05:25 +0900 Subject: [PATCH 18/46] =?UTF-8?q?=EC=A7=80=EB=B6=95=EC=9E=AC=20=ED=95=A0?= =?UTF-8?q?=EB=8B=B9=20=ED=95=A8=EC=88=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/usePolygon.js | 70 ++++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js index 89f1b40b..a2c8c9aa 100644 --- a/src/hooks/usePolygon.js +++ b/src/hooks/usePolygon.js @@ -1,7 +1,14 @@ import { ANGLE_TYPE, canvasState, currentAngleTypeSelector, pitchTextSelector } from '@/store/canvasAtom' import { useRecoilValue } from 'recoil' import { fabric } from 'fabric' -import { findAndRemoveClosestPoint, getDegreeByChon, getDegreeInOrientation, isPointOnLine, toFixedWithoutRounding } from '@/util/canvas-util' +import { + distanceBetweenPoints, + findAndRemoveClosestPoint, + getDegreeByChon, + getDegreeInOrientation, + isPointOnLine, + toFixedWithoutRounding, +} from '@/util/canvas-util' import { QPolygon } from '@/components/fabric/QPolygon' import { isSamePoint, removeDuplicatePolygons } from '@/util/qpolygon-utils' import { flowDisplaySelector } from '@/store/settingAtom' @@ -755,7 +762,7 @@ export const usePolygon = () => { const splitPolygonWithLines = (polygon) => { polygon.set({ visible: false }) - let innerLines = [...polygon.innerLines] + let innerLines = [...polygon.innerLines].filter((line) => line.visible) /*// innerLine이 세팅이 안되어있는경우 찾아서 세팅한다. if (!innerLines || innerLines.length === 0) { @@ -825,8 +832,8 @@ export const usePolygon = () => { /*polygonLines.forEach((line) => { line.set({ strokeWidth: 10 }) canvas.add(line) - })*/ - canvas.renderAll() + }) + canvas.renderAll()*/ polygonLines.forEach((line) => { /*const originStroke = line.stroke @@ -838,12 +845,14 @@ export const usePolygon = () => { innerLine.set({ stroke: 'red' }) canvas.renderAll()*/ if (isPointOnLine(line, innerLine.startPoint)) { + canvas.renderAll() if (isSamePoint(line.startPoint, innerLine.startPoint) || isSamePoint(line.endPoint, innerLine.startPoint)) { return } intersections.push(innerLine.startPoint) } if (isPointOnLine(line, innerLine.endPoint)) { + canvas.renderAll() if (isSamePoint(line.startPoint, innerLine.endPoint) || isSamePoint(line.endPoint, innerLine.endPoint)) { return } @@ -852,13 +861,14 @@ export const usePolygon = () => { /*innerLine.set({ stroke: originInnerStroke }) canvas.renderAll()*/ }) - line.set({ intersections }) + /*line.set({ intersections }) - /*line.set({ stroke: originStroke }) + line.set({ stroke: originStroke }) canvas.renderAll()*/ }) - const divideLines = polygonLines.filter((line) => line.intersections.length > 0) + const divideLines = polygonLines.filter((line) => line.intersections?.length > 0) + let newLines = [] divideLines.forEach((line) => { @@ -873,12 +883,14 @@ export const usePolygon = () => { strokeWidth: 3, fontSize: polygon.fontSize, attributes: line.attributes, + name: 'newLine', }) const newLine2 = new QLine(newLinePoint2, { stroke: 'blue', strokeWidth: 3, fontSize: polygon.fontSize, attributes: line.attributes, + name: 'newLine', }) newLine1.attributes = { ...line.attributes, @@ -907,6 +919,7 @@ export const usePolygon = () => { strokeWidth: 3, fontSize: polygon.fontSize, attributes: line.attributes, + name: 'newLine', }) newLine.attributes = { ...line.attributes, @@ -923,6 +936,7 @@ export const usePolygon = () => { strokeWidth: 3, fontSize: polygon.fontSize, attributes: line.attributes, + name: 'newLine', }) newLine.attributes = { ...line.attributes, @@ -936,7 +950,7 @@ export const usePolygon = () => { //polygonLines에서 divideLines를 제거하고 newLines를 추가한다. newLines = newLines.filter((line) => !(Math.abs(line.startPoint.x - line.endPoint.x) < 1 && Math.abs(line.startPoint.y - line.endPoint.y) < 1)) polygonLines = polygonLines.filter((line) => line.intersections?.length === 0) - const originPolygonLines = [...polygonLines] + polygonLines = [...polygonLines, ...newLines] let allLines = [...polygonLines, ...innerLines] @@ -1005,6 +1019,13 @@ export const usePolygon = () => { } }) + /*innerLines.forEach((line) => { + const startPoint = line.startPoint + const endPoint = line.endPoint + /!*canvas.add(new fabric.Circle({ left: startPoint.x, top: startPoint.y + 10, radius: 5, fill: 'red' })) + canvas.add(new fabric.Circle({ left: endPoint.x, top: endPoint.y - 10, radius: 5, fill: 'blue' }))*!/ + })*/ + /** * 왼쪽 상단을 startPoint로 전부 변경 */ @@ -1031,27 +1052,18 @@ export const usePolygon = () => { line.endPoint = endPoint }) - // polygonLines에서 시작점 혹은 끝점이 innerLines와 연결된 line만 가져온다. - let startLines = polygonLines.filter((line) => { - const startPoint = line.startPoint - const endPoint = line.endPoint - - return innerLines.some((innerLine) => { - return ( - isSamePoint(innerLine.startPoint, startPoint) || - isSamePoint(innerLine.endPoint, startPoint) || - isSamePoint(innerLine.startPoint, endPoint) || - isSamePoint(innerLine.endPoint, endPoint) - ) - }) + //allLines에서 중복을 제거한다. + allLines = allLines.filter((line, index, self) => { + return ( + index === + self.findIndex((l) => { + return isSamePoint(l.startPoint, line.startPoint) && isSamePoint(l.endPoint, line.endPoint) + }) + ) }) - if (startLines.length === 0) { - startLines = originPolygonLines - } - // 나눠서 중복 제거된 roof return - const newRoofs = getSplitRoofsPoints(startLines, allLines, innerLines, uniquePoints) + const newRoofs = getSplitRoofsPoints(allLines) newRoofs.forEach((roofPoint, index) => { let defense, pitch @@ -1152,11 +1164,11 @@ export const usePolygon = () => { }) } - const getSplitRoofsPoints = (startLines, allLines, innerLines, uniquePoints) => { + const getSplitRoofsPoints = (allLines) => { // ==== Utility functions ==== function isSamePoint(p1, p2, epsilon = 1) { - return Math.abs(p1.x - p2.x) <= epsilon && Math.abs(p1.y - p2.y) <= epsilon + return Math.abs(p1.x - p2.x) <= 2 && Math.abs(p1.y - p2.y) <= 2 } function normalizePoint(p, epsilon = 1) { @@ -1248,7 +1260,7 @@ export const usePolygon = () => { const roofs = [] - startLines.forEach((line) => { + allLines.forEach((line) => { // 그래프 생성 const graph = {} const edges = allLines From 92c33c9c11c88b4eeed96a1db77e0664e0fe08dd Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Wed, 7 May 2025 10:46:57 +0900 Subject: [PATCH 19/46] =?UTF-8?q?polygonLines=EA=B0=80=20=EC=97=86?= =?UTF-8?q?=EB=8A=94=20=EA=B2=BD=EC=9A=B0=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/usePolygon.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js index a2c8c9aa..7b14b906 100644 --- a/src/hooks/usePolygon.js +++ b/src/hooks/usePolygon.js @@ -949,7 +949,8 @@ export const usePolygon = () => { //polygonLines에서 divideLines를 제거하고 newLines를 추가한다. newLines = newLines.filter((line) => !(Math.abs(line.startPoint.x - line.endPoint.x) < 1 && Math.abs(line.startPoint.y - line.endPoint.y) < 1)) - polygonLines = polygonLines.filter((line) => line.intersections?.length === 0) + + polygonLines = polygonLines.filter((line) => !line.intersections || line.intersections.length === 0) polygonLines = [...polygonLines, ...newLines] From 381c639d187280c5ca891ac79ffce6883e801a80 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Wed, 7 May 2025 11:21:26 +0900 Subject: [PATCH 20/46] =?UTF-8?q?roofPoints=20valid=20=EC=B2=B4=ED=81=AC?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/qpolygon-utils.js | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index 131e9232..b1cf5ae1 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -294,7 +294,7 @@ function arePolygonsEqual(polygon1, polygon2) { } export function removeDuplicatePolygons(polygons) { - const uniquePolygons = [] + let uniquePolygons = [] polygons.forEach((polygon) => { const isDuplicate = uniquePolygons.some((uniquePolygon) => arePolygonsEqual(polygon, uniquePolygon)) @@ -304,12 +304,32 @@ export function removeDuplicatePolygons(polygons) { }) // x가 전부 같거나, y가 전부 같은 경우 제거 - return uniquePolygons.filter((polygon) => { - const xValues = polygon.map((point) => point.x) - const yValues = polygon.map((point) => point.y) - - return !(xValues.every((x) => x === xValues[0]) || yValues.every((y) => y === yValues[0])) + uniquePolygons = uniquePolygons.filter((polygon) => { + return isValidPoints(polygon) }) + + return uniquePolygons +} + +// 현재 point의 x와 이전 포인트의 x와 같을경우, 다음 포인트의 x와 달라야 함. +// 현재 point의 y와 이전 포인트의 y와 같을경우, 다음 포인트의 y와 달라야 함. +const isValidPoints = (points) => { + for (let i = 1; i < points.length - 1; i++) { + const prev = points[i - 1] + const curr = points[i] + const next = points[i + 1] + + // 현재와 이전의 x가 같다면 다음의 x는 달라야 함 + if (curr.x === prev.x && curr.x === next.x) { + return false + } + + // 현재와 이전의 y가 같다면 다음의 y는 달라야 함 + if (curr.y === prev.y && curr.y === next.y) { + return false + } + } + return true } export const isSamePoint = (a, b) => { From 8da2ab08202f6fce4073243bb2169bc387ff68c7 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Wed, 7 May 2025 13:31:00 +0900 Subject: [PATCH 21/46] =?UTF-8?q?=EC=98=A4=EC=B0=A8=20=EB=B2=94=EC=9C=84?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/usePolygon.js | 5 ++--- src/util/canvas-util.js | 6 +++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js index 7b14b906..62130acd 100644 --- a/src/hooks/usePolygon.js +++ b/src/hooks/usePolygon.js @@ -861,14 +861,13 @@ export const usePolygon = () => { /*innerLine.set({ stroke: originInnerStroke }) canvas.renderAll()*/ }) - /*line.set({ intersections }) + line.set({ intersections }) - line.set({ stroke: originStroke }) + /*line.set({ stroke: originStroke }) canvas.renderAll()*/ }) const divideLines = polygonLines.filter((line) => line.intersections?.length > 0) - let newLines = [] divideLines.forEach((line) => { diff --git a/src/util/canvas-util.js b/src/util/canvas-util.js index 6eef894e..0f0b69dc 100644 --- a/src/util/canvas-util.js +++ b/src/util/canvas-util.js @@ -533,11 +533,11 @@ export function isPointOnLine({ x1, y1, x2, y2 }, { x, y }, epsilon = 2) { return result <= 10*!*/ // 직선 방정식 만족 여부 확인 const crossProduct = (y - y1) * (x2 - x1) - (x - x1) * (y2 - y1) - if (Math.abs(crossProduct) > 500) return false // 작은 오차 허용 + if (Math.abs(crossProduct) > 1000) return false // 작은 오차 허용 // 점이 선분의 범위 내에 있는지 확인 - const withinXRange = Math.min(x1, x2) <= x && x <= Math.max(x1, x2) - const withinYRange = Math.min(y1, y2) <= y && y <= Math.max(y1, y2) + const withinXRange = Math.abs(Math.min(x1, x2) - x) <= 2 || 2 <= Math.abs(Math.max(x1, x2) - x) + const withinYRange = Math.abs(Math.min(y1, y2) - y) <= 2 || 2 <= Math.abs(Math.max(y1, y2) - y) return withinXRange && withinYRange } From 78258fc9c10e9a58695843112851a0a3917061d5 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Wed, 7 May 2025 15:05:44 +0900 Subject: [PATCH 22/46] =?UTF-8?q?roof=20=EB=82=98=EB=88=8C=EB=95=8C=20poin?= =?UTF-8?q?t=20=EC=A4=91=EB=B3=B5=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/qpolygon-utils.js | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index b1cf5ae1..d9923616 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -296,6 +296,7 @@ function arePolygonsEqual(polygon1, polygon2) { export function removeDuplicatePolygons(polygons) { let uniquePolygons = [] + // x가 전부 같거나, y가 전부 같은 경우 제거 polygons.forEach((polygon) => { const isDuplicate = uniquePolygons.some((uniquePolygon) => arePolygonsEqual(polygon, uniquePolygon)) if (!isDuplicate) { @@ -303,7 +304,6 @@ export function removeDuplicatePolygons(polygons) { } }) - // x가 전부 같거나, y가 전부 같은 경우 제거 uniquePolygons = uniquePolygons.filter((polygon) => { return isValidPoints(polygon) }) @@ -314,18 +314,24 @@ export function removeDuplicatePolygons(polygons) { // 현재 point의 x와 이전 포인트의 x와 같을경우, 다음 포인트의 x와 달라야 함. // 현재 point의 y와 이전 포인트의 y와 같을경우, 다음 포인트의 y와 달라야 함. const isValidPoints = (points) => { - for (let i = 1; i < points.length - 1; i++) { - const prev = points[i - 1] - const curr = points[i] - const next = points[i + 1] + for (let i = 1; i < points.length; i++) { + let prev = points[i - 1] + let curr = points[i] + let next = points[i + 1] + + if (i === points.length - 1) { + prev = points[i - 1] + curr = points[i] + next = points[0] + } // 현재와 이전의 x가 같다면 다음의 x는 달라야 함 - if (curr.x === prev.x && curr.x === next.x) { + if (Math.abs(curr.x - prev.x) < 1 && Math.abs(curr.x - next.x) < 1) { return false } // 현재와 이전의 y가 같다면 다음의 y는 달라야 함 - if (curr.y === prev.y && curr.y === next.y) { + if (Math.abs(curr.y - prev.y) < 1 && Math.abs(curr.y - next.y) < 1) { return false } } From 14764d295c27f0934ffc8eaa671a373a76fd3de7 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Thu, 8 May 2025 09:59:09 +0900 Subject: [PATCH 23/46] =?UTF-8?q?polygonLines=EC=A4=91=20intersection=20?= =?UTF-8?q?=ED=8F=AC=EC=9D=B8=ED=8A=B8=EA=B0=80=20=EC=9E=88=EB=8A=94=20?= =?UTF-8?q?=EA=B2=BD=EC=9A=B0=20=EC=B2=98=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/usePolygon.js | 42 +++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js index 62130acd..4d59d292 100644 --- a/src/hooks/usePolygon.js +++ b/src/hooks/usePolygon.js @@ -869,14 +869,15 @@ export const usePolygon = () => { const divideLines = polygonLines.filter((line) => line.intersections?.length > 0) let newLines = [] - - divideLines.forEach((line) => { + polygonLines = polygonLines.filter((line) => !line.intersections || line.intersections.length === 0) + for (let i = divideLines.length - 1; i >= 0; i--) { + const line = divideLines[i] const { intersections, startPoint, endPoint } = line if (intersections.length === 1) { - // 한 점만 교차하는 경우 const newLinePoint1 = [line.x1, line.y1, intersections[0].x, intersections[0].y] const newLinePoint2 = [intersections[0].x, intersections[0].y, line.x2, line.y2] + const newLine1 = new QLine(newLinePoint1, { stroke: 'blue', strokeWidth: 3, @@ -891,28 +892,27 @@ export const usePolygon = () => { attributes: line.attributes, name: 'newLine', }) + newLine1.attributes = { ...line.attributes, - planeSize: Math.round(Math.sqrt(Math.pow(newLine1.x1 - newLine1.x2, 2) + Math.pow(newLine1.y1 - newLine1.y2, 2))) * 10, - actualSize: Math.round(Math.sqrt(Math.pow(newLine1.x1 - newLine1.x2, 2) + Math.pow(newLine1.y1 - newLine1.y2, 2))) * 10, + planeSize: Math.round(Math.hypot(newLine1.x1 - newLine1.x2, newLine1.y1 - newLine1.y2)) * 10, + actualSize: Math.round(Math.hypot(newLine1.x1 - newLine1.x2, newLine1.y1 - newLine1.y2)) * 10, } newLine2.attributes = { ...line.attributes, - planeSize: Math.round(Math.sqrt(Math.pow(newLine2.x1 - newLine2.x2, 2) + Math.pow(newLine2.y1 - newLine2.y2, 2))) * 10, - actualSize: Math.round(Math.sqrt(Math.pow(newLine2.x1 - newLine2.x2, 2) + Math.pow(newLine2.y1 - newLine2.y2, 2))) * 10, + planeSize: Math.round(Math.hypot(newLine2.x1 - newLine2.x2, newLine2.y1 - newLine2.y2)) * 10, + actualSize: Math.round(Math.hypot(newLine2.x1 - newLine2.x2, newLine2.y1 - newLine2.y2)) * 10, } - newLines.push(newLine1) - newLines.push(newLine2) - } else { - // 두 점 이상 교차하는 경우 - //1. intersections중에 startPoint와 가장 가까운 점을 찾는다. - //2. 가장 가까운 점을 기준으로 line을 나눈다. + newLines.push(newLine1, newLine2) + divideLines.splice(i, 1) // 기존 line 제거 + } else { let currentPoint = startPoint while (intersections.length !== 0) { const minDistancePoint = findAndRemoveClosestPoint(currentPoint, intersections) const newLinePoint = [currentPoint.x, currentPoint.y, minDistancePoint.x, minDistancePoint.y] + const newLine = new QLine(newLinePoint, { stroke: 'blue', strokeWidth: 3, @@ -920,11 +920,13 @@ export const usePolygon = () => { attributes: line.attributes, name: 'newLine', }) + newLine.attributes = { ...line.attributes, - planeSize: Math.round(Math.sqrt(Math.pow(newLine.x1 - newLine.x2, 2) + Math.pow(newLine.y1 - newLine.y2, 2))) * 10, - actualSize: Math.round(Math.sqrt(Math.pow(newLine.x1 - newLine.x2, 2) + Math.pow(newLine.y1 - newLine.y2, 2))) * 10, + planeSize: Math.round(Math.hypot(newLine.x1 - newLine.x2, newLine.y1 - newLine.y2)) * 10, + actualSize: Math.round(Math.hypot(newLine.x1 - newLine.x2, newLine.y1 - newLine.y2)) * 10, } + newLines.push(newLine) currentPoint = minDistancePoint } @@ -939,18 +941,18 @@ export const usePolygon = () => { }) newLine.attributes = { ...line.attributes, - planeSize: Math.round(Math.sqrt(Math.pow(newLine.x1 - newLine.x2, 2) + Math.pow(newLine.y1 - newLine.y2, 2))) * 10, - actualSize: Math.round(Math.sqrt(Math.pow(newLine.x1 - newLine.x2, 2) + Math.pow(newLine.y1 - newLine.y2, 2))) * 10, + planeSize: Math.round(Math.hypot(newLine.x1 - newLine.x2, newLine.y1 - newLine.y2)) * 10, + actualSize: Math.round(Math.hypot(newLine.x1 - newLine.x2, newLine.y1 - newLine.y2)) * 10, } + newLines.push(newLine) + divideLines.splice(i, 1) // 기존 line 제거 } - }) + } //polygonLines에서 divideLines를 제거하고 newLines를 추가한다. newLines = newLines.filter((line) => !(Math.abs(line.startPoint.x - line.endPoint.x) < 1 && Math.abs(line.startPoint.y - line.endPoint.y) < 1)) - polygonLines = polygonLines.filter((line) => !line.intersections || line.intersections.length === 0) - polygonLines = [...polygonLines, ...newLines] let allLines = [...polygonLines, ...innerLines] From bb6a796b9fd8a170d08711aad83b0030ba2d2d63 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Thu, 8 May 2025 13:44:31 +0900 Subject: [PATCH 24/46] =?UTF-8?q?=EC=A7=80=EB=B6=95=EC=9E=AC=20=ED=95=A0?= =?UTF-8?q?=EB=8B=B9=20=EC=8B=9C=20=EC=B6=94=EA=B0=80=20=EC=9E=91=EC=97=85?= =?UTF-8?q?=20=EB=B0=8F=20=EB=B3=B4=EC=A1=B0=EC=84=A0=20=EC=A0=9C=EA=B1=B0?= =?UTF-8?q?=20=EC=A1=B0=EA=B1=B4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/roofcover/useAuxiliaryDrawing.js | 10 ++++++++-- src/hooks/roofcover/useRoofAllocationSetting.js | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/hooks/roofcover/useAuxiliaryDrawing.js b/src/hooks/roofcover/useAuxiliaryDrawing.js index 6023bce8..1510825b 100644 --- a/src/hooks/roofcover/useAuxiliaryDrawing.js +++ b/src/hooks/roofcover/useAuxiliaryDrawing.js @@ -849,8 +849,14 @@ export function useAuxiliaryDrawing(id, isUseEffect = true) { if ( lineHistory.current.some( (history) => - JSON.stringify(history.startPoint) === JSON.stringify(line.startPoint) && - JSON.stringify(history.endPoint) === JSON.stringify(line.endPoint), + (Math.abs(history.startPoint.x - line.startPoint.x) < 2 && + Math.abs(history.startPoint.y - line.startPoint.y) < 2 && + Math.abs(history.endPoint.x - line.endPoint.x) < 2 && + Math.abs(history.endPoint.y - line.endPoint.y) < 2) || + (Math.abs(history.startPoint.x - line.endPoint.x) < 2 && + Math.abs(history.startPoint.y - line.endPoint.y) < 2 && + Math.abs(history.endPoint.x - line.startPoint.x) < 2 && + Math.abs(history.endPoint.y - line.startPoint.y) < 2), ) ) { canvas.remove(line) diff --git a/src/hooks/roofcover/useRoofAllocationSetting.js b/src/hooks/roofcover/useRoofAllocationSetting.js index fb7d2114..00c17c1d 100644 --- a/src/hooks/roofcover/useRoofAllocationSetting.js +++ b/src/hooks/roofcover/useRoofAllocationSetting.js @@ -412,7 +412,7 @@ export function useRoofAllocationSetting(id) { }) /** 외곽선 삭제 */ - const removeTargets = canvas.getObjects().filter((obj) => obj.name === 'outerLinePoint' || obj.name === 'outerLine') + const removeTargets = canvas.getObjects().filter((obj) => obj.name === 'outerLinePoint' || obj.name === 'outerLine' || obj.name === 'pitchText') removeTargets.forEach((obj) => { canvas.remove(obj) }) From dc743cee8fc167c94c4d4a0a6fbde648e1237752 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Thu, 8 May 2025 14:21:44 +0900 Subject: [PATCH 25/46] =?UTF-8?q?getRoofMaterialList=20=ED=95=9C=EB=B2=88?= =?UTF-8?q?=EB=A7=8C=20=ED=98=B8=EC=B6=9C=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/floor-plan/CanvasFrame.jsx | 29 ++++++++++++++++++++++- src/hooks/option/useCanvasSetting.js | 2 +- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/components/floor-plan/CanvasFrame.jsx b/src/components/floor-plan/CanvasFrame.jsx index ddce32a6..b43fd559 100644 --- a/src/components/floor-plan/CanvasFrame.jsx +++ b/src/components/floor-plan/CanvasFrame.jsx @@ -12,7 +12,7 @@ import { usePlan } from '@/hooks/usePlan' import { useContextMenu } from '@/hooks/useContextMenu' import { useCanvasConfigInitialize } from '@/hooks/common/useCanvasConfigInitialize' import { currentMenuState } from '@/store/canvasAtom' -import { totalDisplaySelector } from '@/store/settingAtom' +import { roofMaterialsAtom, totalDisplaySelector } from '@/store/settingAtom' import { MENU, POLYGON_TYPE } from '@/common/common' import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider' import { QcastContext } from '@/app/QcastProvider' @@ -30,8 +30,35 @@ import { useCanvasSetting } from '@/hooks/option/useCanvasSetting' import { useCanvasMenu } from '@/hooks/common/useCanvasMenu' import { useEvent } from '@/hooks/useEvent' import { compasDegAtom } from '@/store/orientationAtom' +import { ROOF_MATERIAL_LAYOUT } from '@/components/floor-plan/modal/placementShape/PlacementShapeSetting' +import { useMasterController } from '@/hooks/common/useMasterController' export default function CanvasFrame() { + const [roofMaterials, setRoofMaterials] = useRecoilState(roofMaterialsAtom) + const { getRoofMaterialList } = useMasterController() + useEffect(async () => { + if (roofMaterials.length !== 0) { + return + } + const { data } = await getRoofMaterialList() + + const roofLists = data.map((item, idx) => ({ + ...item, + id: item.roofMatlCd, + name: item.roofMatlNm, + selected: idx === 0, + index: idx, + nameJp: item.roofMatlNmJp, + length: item.lenBase && parseInt(item.lenBase), + width: item.widBase && parseInt(item.widBase), + raft: item.raftBase && parseInt(item.raftBase), + layout: ['ROOF_ID_SLATE', 'ROOF_ID_SINGLE'].includes(item.roofMatlCd) ? ROOF_MATERIAL_LAYOUT.STAIRS : ROOF_MATERIAL_LAYOUT.PARALLEL, + hajebichi: item.roofPchBase && parseInt(item.roofPchBase), + pitch: item.pitch ? parseInt(item.pitch) : 4, + angle: item.angle ? parseInt(item.angle) : 21.8, + })) + setRoofMaterials(roofLists) + }, []) const canvasRef = useRef(null) const { canvas } = useCanvas('canvas') const { canvasLoadInit, gridInit } = useCanvasConfigInitialize() diff --git a/src/hooks/option/useCanvasSetting.js b/src/hooks/option/useCanvasSetting.js index 76f42075..2688502a 100644 --- a/src/hooks/option/useCanvasSetting.js +++ b/src/hooks/option/useCanvasSetting.js @@ -148,7 +148,7 @@ export function useCanvasSetting(executeEffect = true) { } /** 초 1회만 실행하도록 처리 */ - addRoofMaterials() + //addRoofMaterials() }, []) /** From 2f261cb0c0a9418a13e2e416f04c2493b49ed798 Mon Sep 17 00:00:00 2001 From: Jaeyoung Lee Date: Thu, 8 May 2025 18:05:38 +0900 Subject: [PATCH 26/46] =?UTF-8?q?=EB=B0=95=EA=B3=B5=EC=A7=80=EB=B6=95=20?= =?UTF-8?q?=EB=8F=99=EC=84=A0=EC=9D=B4=EB=8F=99=20=EB=8C=80=EC=9D=91=20?= =?UTF-8?q?=EC=A0=95=EB=A6=AC=20(=ED=98=BC=ED=95=A9=20=EC=B2=98=EB=A6=AC?= =?UTF-8?q?=20=EC=A4=91)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/qpolygon-utils.js | 935 +++++++++++++++++-------------------- 1 file changed, 422 insertions(+), 513 deletions(-) diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index 9fbfc6a3..677c9a5b 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -1,6 +1,6 @@ import { fabric } from 'fabric' import { QLine } from '@/components/fabric/QLine' -import { getDegreeByChon } from '@/util/canvas-util' +import { getDegreeByChon, isPointOnLine } from '@/util/canvas-util' import { QPolygon } from '@/components/fabric/QPolygon' import * as turf from '@turf/turf' @@ -878,6 +878,8 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { if (checkWallPolygon.inPolygon(checkPoints)) { drawGablePolygonFirst.push({ currentBaseLine, prevBaseLine, nextBaseLine }) + } else { + drawGablePolygonSecond.push({ currentBaseLine, prevBaseLine, nextBaseLine }) } // if (!checkWallPolygon.inPolygon(checkPoints)) { drawGableRidgeSecond.push({ currentBaseLine, prevBaseLine, nextBaseLine }) @@ -1655,410 +1657,27 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { canvas.renderAll() }) - /** 케라바 지붕으로 생성된 마루의 지붕선을 그린다.*/ - baseGableRidgeLines.forEach((ridge) => { - return - const { x1, x2, y1, y2 } = ridge - const checkLine = new fabric.Line([x1, y1, x2, y2], { - stroke: 'red', - strokeWidth: 4, - parentId: roofId, - name: 'checkLine', - }) - canvas.add(checkLine) - canvas.renderAll() - - const ridgeVectorX = Math.sign(Big(x1).minus(x2).toNumber()) - const ridgeVectorY = Math.sign(Big(y1).minus(y2).toNumber()) - const firstPoint = { x: x1, y: y1 } - const secondPoint = { x: x2, y: y2 } - let firstRoofLine, secondRoofLine - roof.lines - .filter((line) => { - if (ridgeVectorX === 0) { - return line.x1 !== line.x2 - } else { - return line.y1 !== line.y2 - } - }) - .forEach((line) => { - if (ridgeVectorX === 0) { - if (line.y1 === firstPoint.y) { - firstRoofLine = line - } - if (line.y1 === secondPoint.y) { - secondRoofLine = line - } - } else { - if (line.x1 === firstPoint.x) { - firstRoofLine = line - } - if (line.x1 === secondPoint.x) { - secondRoofLine = line - } - } - }) - /** 마루 1개에 (위,아래), (좌,우) 로 두가지의 지붕면이 생길 수 있다.*/ - let firstCheckPoints = [] - let secondCheckPoints = [] - - /** - * @param startPoint - * @param nextPoint - * @param endPoint - */ - const findPointToPolygon = (startPoint, nextPoint, endPoint) => { - let findPoint = nextPoint - const points = [startPoint, nextPoint] - let index = 1 - while (!(points[points.length - 1].x === endPoint.x && points[points.length - 1].y === endPoint.y)) { - const prevVector = { x: Math.sign(points[index - 1].x - points[index].x), y: Math.sign(points[index - 1].y - points[index].y) } - const currentPoint = points[index] - - const hipLine = baseHipLines.find( - (line) => - (line.line.x1 === currentPoint.x && line.line.y1 === currentPoint.y) || - (line.line.x2 === currentPoint.x && line.line.y2 === currentPoint.y), - ) - if (hipLine) { - if (hipLine.line.x1 === currentPoint.x && hipLine.line.y1 === currentPoint.y) { - points.push({ x: hipLine.line.x2, y: hipLine.line.y2 }) - } else { - points.push({ x: hipLine.line.x1, y: hipLine.line.y1 }) - } - } else { - const nextRoofLine = roof.lines.find((line) => { - if (prevVector.x !== 0) { - return ( - line.y1 !== line.y2 && - ((line.x1 <= currentPoint.x && currentPoint.x <= line.x2) || (line.x2 <= currentPoint.x && currentPoint.x <= line.x1)) - ) - } else { - return ( - line.x1 !== line.x2 && - ((line.y1 <= currentPoint.y && currentPoint.y <= line.y2) || (line.y2 <= currentPoint.y && currentPoint.y <= line.y1)) - ) - } - }) - const checkLine = new fabric.Line([nextRoofLine.x1, nextRoofLine.y1, nextRoofLine.x2, nextRoofLine.y2], { - stroke: 'yellow', - strokeWidth: 4, - parentId: roofId, - name: 'checkLine', - }) - canvas.add(checkLine) - canvas.renderAll() - - const lineEdge = { vertex1: { x: nextRoofLine.x1, y: nextRoofLine.y1 }, vertex2: { x: nextRoofLine.x2, y: nextRoofLine.y2 } } - let ridgeIntersection - baseGableRidgeLines.forEach((ridgeLine) => { - const ridgeEdge = { vertex1: { x: ridgeLine.x1, y: ridgeLine.y1 }, vertex2: { x: ridgeLine.x2, y: ridgeLine.y2 } } - const intersection = edgesIntersection(lineEdge, ridgeEdge) - console.log('intersection', intersection) - if (intersection && !intersection.isIntersectionOutside) { - ridgeIntersection = { x: intersection.x, y: intersection.y } - } - }) - if (ridgeIntersection) { - points.push(ridgeIntersection) - } else { - if (nextRoofLine.x1 === currentPoint.x && nextRoofLine.y1 === currentPoint.y) { - points.push({ x: nextRoofLine.x2, y: nextRoofLine.y2 }) - } else { - points.push({ x: nextRoofLine.x1, y: nextRoofLine.y1 }) - } - } - } - console.log('points', points, points[points.length - 1], endPoint) - index = index + 1 - } - - canvas - .getObjects() - .filter((obj) => obj.name === 'checkLine' || obj.name === 'checkPoint') - .forEach((obj) => canvas.remove(obj)) - canvas.renderAll() - return points - } - - let startVector - console.log('!firstRoofLine && !secondRoofLine', !firstRoofLine && !secondRoofLine) - let firstPoints, secondPoints - if (!firstRoofLine && !secondRoofLine) { + const uniqueRidgeLines = [] + /** 중복제거 */ + baseGableRidgeLines.forEach((currentLine, index) => { + if (index === 0) { + uniqueRidgeLines.push(currentLine) } else { - if (firstRoofLine) { - firstPoints = findPointToPolygon(firstPoint, { x: firstRoofLine.x1, y: firstRoofLine.y1 }, secondPoint) - secondPoints = findPointToPolygon(firstPoint, { x: firstRoofLine.x2, y: firstRoofLine.y2 }, secondPoint) - } else { - firstPoints = findPointToPolygon(secondPoint, { x: secondRoofLine.x1, y: secondRoofLine.y1 }, firstPoint) - secondPoints = findPointToPolygon(secondPoint, { x: secondRoofLine.x2, y: secondRoofLine.y2 }, firstPoint) - } - } - const firstPolygonPoints = getSortedPoint(firstPoints) - const secondPolygonPoints = getSortedPoint(secondPoints) - - firstPolygonPoints.forEach((point, index) => { - let endPoint - if (index === firstPolygonPoints.length - 1) { - endPoint = firstPolygonPoints[0] - } else { - endPoint = firstPolygonPoints[index + 1] - } - const hipLine = drawHipLine([point.x, point.y, endPoint.x, endPoint.y], canvas, roof, textMode, null, currentDegree, currentDegree) - baseHipLines.push({ x1: point.x, y1: point.y, x2: endPoint.x, y2: endPoint.y, line: hipLine }) - - const checkLine = new fabric.Line([point.x, point.y, endPoint.x, endPoint.y], { - stroke: 'red', - strokeWidth: 4, - parentId: roofId, - name: 'checkLine', - }) - canvas.add(checkLine) - canvas.renderAll() - }) - secondPolygonPoints.forEach((point, index) => { - let endPoint - if (index === secondPolygonPoints.length - 1) { - endPoint = secondPolygonPoints[0] - } else { - endPoint = secondPolygonPoints[index + 1] - } - const hipLine = drawHipLine([point.x, point.y, endPoint.x, endPoint.y], canvas, roof, textMode, null, currentDegree, currentDegree) - baseHipLines.push({ x1: point.x, y1: point.y, x2: endPoint.x, y2: endPoint.y, line: hipLine }) - - const checkLine = new fabric.Line([point.x, point.y, endPoint.x, endPoint.y], { - stroke: 'red', - strokeWidth: 4, - parentId: roofId, - name: 'checkLine', - }) - canvas.add(checkLine) - canvas.renderAll() - }) - }) - - drawGableFirstLines.forEach((current) => { - return - const { currentBaseLine, prevBaseLine, nextBaseLine } = current - const currentLine = currentBaseLine.line - const prevLine = prevBaseLine.line - const nextLine = nextBaseLine.line - let { x1, x2, y1, y2, size } = currentBaseLine - let beforePrevBaseLine, afterNextBaseLine - - /** 이전 라인의 경사 */ - const prevDegree = prevLine.attributes.pitch > 0 ? getDegreeByChon(prevLine.attributes.pitch) : prevLine.attributes.degree - /** 다음 라인의 경사 */ - const nextDegree = nextLine.attributes.pitch > 0 ? getDegreeByChon(nextLine.attributes.pitch) : nextLine.attributes.degree - /** 현재 라인의 경사 */ - const currentDegree = currentLine.attributes.pitch > 0 ? getDegreeByChon(currentLine.attributes.pitch) : currentLine.attributes.degree - - /** 이전 라인의 전라인, 다음 라인의 다음라인을 찾는다 */ - drawBaseLines.forEach((line, index) => { - if (line === prevBaseLine) { - beforePrevBaseLine = drawBaseLines[(index - 1 + drawBaseLines.length) % drawBaseLines.length] - } - if (line === nextBaseLine) { - afterNextBaseLine = drawBaseLines[(index + 1) % drawBaseLines.length] - } - }) - - const beforePrevLine = beforePrevBaseLine?.line - const afterNextLine = afterNextBaseLine?.line - - /** 각 라인의 흐름 방향을 확인한다. */ - const currentAngle = calculateAngle(currentLine.startPoint, currentLine.endPoint) - const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint) - const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint) - const beforePrevAngle = calculateAngle(beforePrevLine.startPoint, beforePrevLine.endPoint) - const afterNextAngle = calculateAngle(afterNextLine.startPoint, afterNextLine.endPoint) - - /** 이전라인의 vector*/ - const prevVectorX = Math.sign(Big(prevLine.x2).minus(Big(prevLine.x1))) - const prevVectorY = Math.sign(Big(prevLine.y2).minus(Big(prevLine.y1))) - - /** 현재라인의 기준점*/ - const currentMidX = Big(x1).plus(Big(x2)).div(2).plus(Big(prevVectorX).times(currentLine.attributes.offset)) - const currentMidY = Big(y1).plus(Big(y2)).div(2).plus(Big(prevVectorY).times(currentLine.attributes.offset)) - - if (beforePrevBaseLine === afterNextBaseLine) { - console.log('박공지붕 사각') - - const afterNextMidX = Big(afterNextLine.x1).plus(Big(afterNextLine.x2)).div(2) - const afterNextMidY = Big(afterNextLine.y1).plus(Big(afterNextLine.y2)).div(2) - const vectorMidX = Math.sign(currentMidX.minus(afterNextMidX)) - const vectorMidY = Math.sign(currentMidY.minus(afterNextMidY)) - - let oppositeMidX, oppositeMidY - if (eavesType.includes(afterNextLine.attributes?.type)) { - const checkSize = currentMidX - .minus(afterNextMidX) - .pow(2) - .plus(currentMidY.minus(afterNextMidY).pow(2)) - .sqrt() - .minus(Big(afterNextLine.attributes.planeSize).div(20)) - .round(1) - oppositeMidX = currentMidX.plus(checkSize.times(vectorMidX).neg()) - oppositeMidY = currentMidY.plus(checkSize.times(vectorMidY).neg()) - - const xVector1 = Math.sign(Big(oppositeMidX).minus(Big(afterNextLine.x1)).neg().toNumber()) - const yVector1 = Math.sign(Big(oppositeMidY).minus(Big(afterNextLine.y1)).neg().toNumber()) - const xVector2 = Math.sign(Big(oppositeMidX).minus(Big(afterNextLine.x2)).neg().toNumber()) - const yVector2 = Math.sign(Big(oppositeMidY).minus(Big(afterNextLine.y2)).neg().toNumber()) - - let addOppositeX1 = 0, - addOppositeY1 = 0, - addOppositeX2 = 0, - addOppositeY2 = 0 - - if (!checkWallPolygon.inPolygon({ x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() })) { - const checkScale = currentMidX.minus(oppositeMidX).pow(2).plus(currentMidY.minus(oppositeMidY).pow(2)).sqrt() - addOppositeX1 = checkScale.times(xVector1).toNumber() - addOppositeY1 = checkScale.times(yVector1).toNumber() - addOppositeX2 = checkScale.times(xVector2).toNumber() - addOppositeY2 = checkScale.times(yVector2).toNumber() - } - - let scale1 = Big(afterNextLine.attributes.offset).pow(2).plus(Big(nextLine.attributes.offset).pow(2)).sqrt() - scale1 = scale1.eq(0) ? Big(1) : scale1 - let scale2 = Big(afterNextLine.attributes.offset).pow(2).plus(Big(prevLine.attributes.offset).pow(2)).sqrt() - scale2 = scale2.eq(0) ? Big(1) : scale2 - - const checkHip1 = { - x1: Big(afterNextLine.x1).plus(scale1.times(xVector1)).toNumber(), - y1: Big(afterNextLine.y1).plus(scale1.times(yVector1)).toNumber(), - x2: oppositeMidX.plus(addOppositeX1).toNumber(), - y2: oppositeMidY.plus(addOppositeY1).toNumber(), - } - - const checkHip2 = { - x1: Big(afterNextLine.x2).plus(scale2.times(xVector2)).toNumber(), - y1: Big(afterNextLine.y2).plus(scale2.times(yVector2)).toNumber(), - x2: oppositeMidX.plus(addOppositeX2).toNumber(), - y2: oppositeMidY.plus(addOppositeY2).toNumber(), - } - - const intersection1 = findRoofIntersection(roof, checkHip1, { x: oppositeMidX.plus(addOppositeX1), y: oppositeMidY.plus(addOppositeY1) }) - const intersection2 = findRoofIntersection(roof, checkHip2, { x: oppositeMidX.plus(addOppositeX2), y: oppositeMidY.plus(addOppositeY2) }) - - const afterNextDegree = afterNextLine.attributes.pitch > 0 ? getDegreeByChon(afterNextLine.attributes.pitch) : afterNextLine.attributes.degree - - if (intersection1) { - const hipLine = drawHipLine( - [intersection1.intersection.x, intersection1.intersection.y, oppositeMidX.plus(addOppositeX1), oppositeMidY.plus(addOppositeY1)], - canvas, - roof, - textMode, - null, - nextDegree, - afterNextDegree, - ) - baseHipLines.push({ - x1: afterNextLine.x1, - y1: afterNextLine.y1, - x2: oppositeMidX.plus(addOppositeX1).toNumber(), - y2: oppositeMidY.plus(addOppositeY1).toNumber(), - line: hipLine, - }) - } - if (intersection2) { - const hipLine = drawHipLine( - [intersection2.intersection.x, intersection2.intersection.y, oppositeMidX.plus(addOppositeX2), oppositeMidY.plus(addOppositeY2)], - canvas, - roof, - textMode, - null, - prevDegree, - afterNextDegree, - ) - baseHipLines.push({ - x1: afterNextLine.x2, - y1: afterNextLine.y2, - x2: oppositeMidX.plus(addOppositeX2).toNumber(), - y2: oppositeMidY.plus(addOppositeY2).toNumber(), - line: hipLine, - }) - } - } else { - oppositeMidX = Big(afterNextLine.x1).plus(Big(afterNextLine.x2)).div(2).plus(Big(prevVectorX).neg().times(afterNextLine.attributes.offset)) - oppositeMidY = Big(afterNextLine.y1).plus(Big(afterNextLine.y2)).div(2).plus(Big(prevVectorY).neg().times(afterNextLine.attributes.offset)) - } - - const vectorOppositeX = Math.sign(currentMidX.minus(oppositeMidX)) - const vectorOppositeY = Math.sign(currentMidY.minus(oppositeMidY)) - - if (vectorMidX === vectorOppositeX && vectorMidY === vectorOppositeY && baseRidgeCount < getMaxRidge(baseLines.length)) { - const ridge = drawRidgeLine( - [currentMidX.toNumber(), currentMidY.toNumber(), oppositeMidX.toNumber(), oppositeMidY.toNumber()], - canvas, - roof, - textMode, - ) - baseRidgeLines.push(ridge) - baseRidgeCount++ - } - } else { - console.log('4각 아님') - const vectorMidX = Math.sign(Big(nextLine.x2).minus(nextLine.x1)) - const vectorMidY = Math.sign(Big(nextLine.y2).minus(nextLine.y1)) - let oppositeMidX = currentMidX, - oppositeMidY = currentMidY - let prevOppositeMidX, prevOppositeMidY, nextOppositeMidX, nextOppositeMidY - const beforePrevOffset = - currentAngle === beforePrevAngle - ? Big(beforePrevLine.attributes.offset) - : Big(beforePrevLine.attributes.offset).plus(currentLine.attributes.offset) - const afterNextOffset = - currentAngle === afterNextAngle - ? Big(afterNextLine.attributes.offset) - : Big(afterNextLine.attributes.offset).plus(currentLine.attributes.offset) - const prevSize = Big(prevLine.attributes.planeSize).div(10) - const nextSize = Big(nextLine.attributes.planeSize).div(10) - - /** 다음 라인이 그 다음 라인과의 사이에 추녀마루가 존재 하는지 확인. 처마-처마 인 경우 추녀마루*/ - if (eavesType.includes(afterNextLine.attributes?.type)) { - } else { - if (vectorMidX === 0) { - prevOppositeMidY = currentMidY.plus(prevSize.plus(beforePrevOffset).times(vectorMidY)) - prevOppositeMidX = currentMidX - } else { - prevOppositeMidX = currentMidX.plus(prevSize.plus(beforePrevOffset).times(vectorMidX)) - prevOppositeMidY = currentMidY - } - } - - /** 이전 라인이 그 이전 라인과의 사이에 추녀마루가 존재 하는지 확인. 처마-처마 인 경우 추녀마루*/ - if (eavesType.includes(beforePrevLine.attributes?.type)) { - } else { - if (vectorMidX === 0) { - nextOppositeMidY = currentMidY.plus(nextSize.plus(afterNextOffset).times(vectorMidY)) - nextOppositeMidX = currentMidX - } else { - nextOppositeMidX = currentMidX.plus(nextSize.plus(afterNextOffset).times(vectorMidX)) - nextOppositeMidY = currentMidY - } - } - - const checkPrevSize = currentMidX.minus(prevOppositeMidX).pow(2).plus(currentMidY.minus(prevOppositeMidY).pow(2)).sqrt() - const checkNextSize = currentMidX.minus(nextOppositeMidX).pow(2).plus(currentMidY.minus(nextOppositeMidY).pow(2)).sqrt() - - /** 두 포인트 중에 current와 가까운 포인트를 사용*/ - if (checkPrevSize.gt(checkNextSize)) { - oppositeMidY = nextOppositeMidY - oppositeMidX = nextOppositeMidX - } else { - oppositeMidY = prevOppositeMidY - oppositeMidX = prevOppositeMidX - } - - if (baseRidgeCount < getMaxRidge(baseLines.length)) { - const ridgeLine = drawRidgeLine([currentMidX, currentMidY, oppositeMidX, oppositeMidY], canvas, roof, textMode) - baseRidgeLines.push(ridgeLine) - baseRidgeCount++ + const duplicateLines = uniqueRidgeLines.filter( + (line) => + (currentLine.x1 === line.x1 && currentLine.y1 === line.y1 && currentLine.x2 === line.x2 && currentLine.y2 === line.y2) || + (currentLine.x1 === line.x2 && currentLine.y1 === line.y2 && currentLine.x2 === line.x1 && currentLine.y2 === line.y1), + ) + if (duplicateLines.length === 0) { + uniqueRidgeLines.push(currentLine) } } }) + baseGableRidgeLines = uniqueRidgeLines + + console.log('baseGableRidgeLines : ', baseGableRidgeLines) + /** 박공지붕 polygon 생성 */ drawGablePolygonFirst.forEach((current) => { const { currentBaseLine, prevBaseLine, nextBaseLine } = current @@ -2093,14 +1712,24 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { roofY2 = roofY1 } - const prevRoofLine = roof.lines.find( - (line) => currentVectorX !== Math.sign(line.x2 - line.x1) && line.x2 === roofX1.toNumber() && line.y2 === roofY1.toNumber(), - ) - const nextRoofLine = roof.lines.find( - (line) => currentVectorX !== Math.sign(line.x2 - line.x1) && line.x1 === roofX2.toNumber() && line.y1 === roofY2.toNumber(), - ) + let prevRoofLine, nextRoofLine + const roofEdge = { vertex1: { x: roofX1.toNumber(), y: roofY1.toNumber() }, vertex2: { x: roofX2.toNumber(), y: roofY2.toNumber() } } + roof.lines + .filter((line) => (currentVectorX === 0 ? line.y1 === line.y2 : line.x1 === line.x2)) + .forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(roofEdge, lineEdge) + if (intersection) { + if (roofX1.eq(intersection.x) && roofY1.eq(intersection.y)) { + prevRoofLine = line + } + if (roofX2.eq(intersection.x) && roofY2.eq(intersection.y)) { + nextRoofLine = line + } + } + }) - const prevRoofEdge = { vertex1: { x: prevRoofLine.x1, y: prevRoofLine.y1 }, vertex2: { x: prevRoofLine.x2, y: prevRoofLine.y2 } } + const prevRoofEdge = { vertex1: { x: prevRoofLine.x2, y: prevRoofLine.y2 }, vertex2: { x: prevRoofLine.x1, y: prevRoofLine.y1 } } const nextRoofEdge = { vertex1: { x: nextRoofLine.x1, y: nextRoofLine.y1 }, vertex2: { x: nextRoofLine.x2, y: nextRoofLine.y2 } } baseGableRidgeLines.forEach((ridge) => { @@ -2110,21 +1739,18 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { if (prevIs) { prevLineRidges.push({ ridge, - size: calcLinePlaneSize({ x1: ridgeEdge.vertex1.x, y1: ridgeEdge.vertex1.y, x2: prevIs.x, y2: prevIs.y }), + size: calcLinePlaneSize({ x1: roofX1, y1: roofY1, x2: prevIs.x, y2: prevIs.y }), }) } if (nextIs) { nextLineRidges.push({ ridge, - size: calcLinePlaneSize({ x1: ridgeEdge.vertex1.x, y1: ridgeEdge.vertex1.y, x2: nextIs.x, y2: nextIs.y }), + size: calcLinePlaneSize({ x1: roofX2, y1: roofY2, x2: nextIs.x, y2: nextIs.y }), }) } }) - const polygonPoints = [ - { x: roofX1.toNumber(), y: roofY1.toNumber() }, - { x: roofX2.toNumber(), y: roofY2.toNumber() }, - ] + const polygonPoints = [] let prevLineRidge, nextLineRidge if (prevLineRidges.length > 0) { @@ -2154,9 +1780,44 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { nextLineRidge = nextLineRidges[0].ridge } + /** 마루에서 현재라인으로 향하는 외벽선까지의 포인트를 확인 하여 처리*/ + let checkEdge + if (currentVectorX === 0) { + checkEdge = { vertex1: { x: prevLineRidge.x1, y: roofY1.toNumber() }, vertex2: { x: roofX1.toNumber(), y: roofY1.toNumber() } } + } else { + checkEdge = { vertex1: { x: roofX1.toNumber(), y: prevLineRidge.y1 }, vertex2: { x: roofX1.toNumber(), y: roofY1.toNumber() } } + } + const checkVectorX = Math.sign(checkEdge.vertex1.x - checkEdge.vertex2.x) + const checkVectorY = Math.sign(checkEdge.vertex1.y - checkEdge.vertex2.y) + const intersectPoints = [] + roof.lines + .filter((line) => (currentVectorX === 0 ? line.x1 === line.x2 : line.y1 === line.y2)) + .forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const is = edgesIntersection(checkEdge, lineEdge) + if (is) { + const isVectorX = Math.sign(checkEdge.vertex1.x - is.x) + const isVectorY = Math.sign(checkEdge.vertex1.y - is.y) + const isLineOtherPoint = is.x === line.x1 && is.y === line.y1 ? { x: line.x2, y: line.y2 } : { x: line.x1, y: line.y1 } + const lineVectorX = Math.sign(isLineOtherPoint.x - is.x) + const lineVectorY = Math.sign(isLineOtherPoint.y - is.y) + if (checkVectorX === isVectorX && checkVectorY === isVectorY && lineVectorX === currentVectorX && lineVectorY === currentVectorY) { + intersectPoints.push(is) + } + } + }) + let intersect = intersectPoints[0] + if (currentVectorX === 0) { + polygonPoints.push({ x: intersect.x, y: roofY1.toNumber() }, { x: intersect.x, y: roofY2.toNumber() }) + } else { + polygonPoints.push({ x: roofX1.toNumber(), y: intersect.y }, { x: roofX2.toNumber(), y: intersect.y }) + } + if (prevLineRidge === nextLineRidge) { + /** 4각*/ polygonPoints.push({ x: prevLineRidge.x1, y: prevLineRidge.y1 }, { x: prevLineRidge.x2, y: prevLineRidge.y2 }) } else { + /** 6각이상*/ let isOverLap = currentVectorX === 0 ? (prevLineRidge.y1 <= nextLineRidge.y1 && prevLineRidge.y2 >= nextLineRidge.y1) || @@ -2167,12 +1828,13 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { (prevLineRidge.x1 >= nextLineRidge.x1 && prevLineRidge.x2 <= nextLineRidge.x1) || (prevLineRidge.x1 <= nextLineRidge.x2 && prevLineRidge.x2 >= nextLineRidge.x2) || (prevLineRidge.x1 >= nextLineRidge.x2 && prevLineRidge.x2 <= nextLineRidge.x2) - console.log('isOverLap : ', isOverLap) if (isOverLap) { const prevDistance = currentVectorX === 0 ? Math.abs(prevLineRidge.x1 - roofX1.toNumber()) : Math.abs(prevLineRidge.y1 - roofY1.toNumber()) const nextDistance = currentVectorX === 0 ? Math.abs(nextLineRidge.x1 - roofX1.toNumber()) : Math.abs(nextLineRidge.y1 - roofY1.toNumber()) /** 현재 지붕 라인과 먼 라인의 포인트를 온전히 사용한다. */ - if (prevDistance < nextDistance) { + if (prevDistance === nextDistance) { + console.log('prevDistance === nextDistance') + } else if (prevDistance < nextDistance) { polygonPoints.push({ x: nextLineRidge.x1, y: nextLineRidge.y1 }, { x: nextLineRidge.x2, y: nextLineRidge.y2 }) /** 이전라인과 교차한 마루의 포인트*/ const prevRidgePoint1 = @@ -2227,11 +1889,178 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { polygonPoints.push(nextRidgePoint2) } } else { + /** 마루가 겹치지 않을때 */ + const otherRidgeLines = [] + + baseGableRidgeLines + .filter((ridge) => ridge !== prevLineRidge && ridge !== nextLineRidge) + .filter((ridge) => (currentVectorX === 0 ? ridge.x1 === ridge.x2 : ridge.y1 === ridge.y2)) + .filter((ridge) => + currentVectorX === 0 ? nextVectorX === Math.sign(nextLine.x1 - ridge.x1) : nextVectorY === Math.sign(nextLine.y1 - ridge.y1), + ) + .forEach((ridge) => { + const size = currentVectorX === 0 ? Math.abs(nextLine.x1 - ridge.x1) : Math.abs(nextLine.y1 - ridge.y1) + otherRidgeLines.push({ ridge, size }) + }) + if (otherRidgeLines.length > 0) { + const otherRidge = otherRidgeLines.sort((a, b) => a.size - b.size)[0].ridge + /** + * otherRidge이 prevRidgeLine, nextRidgeLine 과 currentLine의 사이에 있는지 확인해서 분할하여 작업 + * 지붕의 덮힘이 다르기 때문 + */ + const isInside = + currentVectorX === 0 + ? Math.abs(currentLine.x1 - otherRidge.x1) < Math.abs(currentLine.x1 - prevLineRidge.x1) && + Math.abs(currentLine.x1 - otherRidge.x1) < Math.abs(currentLine.x1 - nextLineRidge.x1) + : Math.abs(currentLine.y1 - otherRidge.y1) < Math.abs(currentLine.y1 - prevLineRidge.y1) && + Math.abs(currentLine.y1 - otherRidge.y1) < Math.abs(currentLine.y1 - nextLineRidge.y1) + + if (isInside) { + polygonPoints.push( + { x: prevLineRidge.x1, y: prevLineRidge.y1 }, + { x: prevLineRidge.x2, y: prevLineRidge.y2 }, + { x: nextLineRidge.x1, y: nextLineRidge.y1 }, + { x: nextLineRidge.x2, y: nextLineRidge.y2 }, + ) + + let ridgeAllPoints = [ + { x: prevLineRidge.x1, y: prevLineRidge.y1 }, + { x: prevLineRidge.x2, y: prevLineRidge.y2 }, + { x: nextLineRidge.x1, y: nextLineRidge.y1 }, + { x: nextLineRidge.x2, y: nextLineRidge.y2 }, + ] + let ridgePoints = [] + ridgeAllPoints.forEach((point) => { + let isOnLine = false + roof.lines.forEach((line) => { + if (isPointOnLine({ x1: line.x1, y1: line.y1, x2: line.x2, y2: line.y2 }, point)) { + isOnLine = true + } + }) + if (!isOnLine) { + ridgePoints.push(point) + } + }) + if (ridgePoints.length === 2) { + if (Math.sign(otherRidge.x1 - otherRidge.x2) === 0) { + polygonPoints.push({ x: otherRidge.x1, y: ridgePoints[0].y }, { x: otherRidge.x1, y: ridgePoints[1].y }) + } else { + polygonPoints.push({ x: ridgePoints[0].x, y: otherRidge.y1 }, { x: ridgePoints[1].x, y: otherRidge.y1 }) + } + } + } else { + polygonPoints.push({ x: otherRidge.x1, y: otherRidge.y1 }, { x: otherRidge.x2, y: otherRidge.y2 }) + + let ridgePoints = [ + { x: prevLineRidge.x1, y: prevLineRidge.y1 }, + { x: prevLineRidge.x2, y: prevLineRidge.y2 }, + { x: nextLineRidge.x1, y: nextLineRidge.y1 }, + { x: nextLineRidge.x2, y: nextLineRidge.y2 }, + ] + + ridgePoints.forEach((point) => { + let isOnLine = false + roof.lines.forEach((line) => { + if (isPointOnLine({ x1: line.x1, y1: line.y1, x2: line.x2, y2: line.y2 }, point)) { + isOnLine = true + } + }) + if (isOnLine) { + polygonPoints.push(point) + } + }) + + if (Math.sign(otherRidge.x1 - otherRidge.x2) === 0) { + const prevY = + (prevLineRidge.y1 <= otherRidge.y1 && otherRidge.y1 <= prevLineRidge.y2) || + (prevLineRidge.y1 >= otherRidge.y1 && otherRidge.y1 >= prevLineRidge.y2) + ? otherRidge.y1 + : otherRidge.y2 + const nextY = + (nextLineRidge.y1 <= otherRidge.y1 && otherRidge.y1 <= nextLineRidge.y2) || + (nextLineRidge.y1 >= otherRidge.y1 && otherRidge.y1 >= nextLineRidge.y2) + ? otherRidge.y1 + : otherRidge.y2 + polygonPoints.push({ x: prevLineRidge.x1, y: prevY }, { x: nextLineRidge.x1, y: nextY }) + } else { + const prevX = + (prevLineRidge.x1 <= otherRidge.x1 && otherRidge.x1 <= prevLineRidge.x2) || + (prevLineRidge.x1 >= otherRidge.x1 && otherRidge.x1 >= prevLineRidge.x2) + ? otherRidge.x1 + : otherRidge.x2 + const nextX = + (nextLineRidge.x1 <= otherRidge.x1 && otherRidge.x1 <= nextLineRidge.x2) || + (nextLineRidge.x1 >= otherRidge.x1 && otherRidge.x1 >= nextLineRidge.x2) + ? otherRidge.x1 + : otherRidge.x2 + polygonPoints.push({ x: prevX, y: prevLineRidge.y1 }, { x: nextX, y: nextLineRidge.y1 }) + } + } + } } } const sortedPolygonPoints = getSortedPoint(polygonPoints) + /** 외벽선 밖으로 나가있는 포인트*/ + const outsidePoints = polygonPoints.filter((point) => { + let isOutside = true + roof.lines.forEach((line) => { + if ( + (line.x1 <= point.x && line.x2 >= point.x && line.y1 <= point.y && line.y2 >= point.y) || + (line.x1 >= point.x && line.x2 <= point.x && line.y1 >= point.y && line.y2 <= point.y) + ) { + isOutside = false + } + }) + baseGableRidgeLines.forEach((line) => { + if ( + (line.x1 <= point.x && line.x2 >= point.x && line.y1 <= point.y && line.y2 >= point.y) || + (line.x1 >= point.x && line.x2 <= point.x && line.y1 >= point.y && line.y2 <= point.y) + ) { + isOutside = false + } + }) + return isOutside + }) + + if (outsidePoints.length > 0) { + sortedPolygonPoints.forEach((currentPoint, index) => { + if (outsidePoints.includes(currentPoint)) { + const prevPoint = sortedPolygonPoints[(index - 1 + sortedPolygonPoints.length) % sortedPolygonPoints.length] + const nextPoint = sortedPolygonPoints[(index + 1) % sortedPolygonPoints.length] + const vectorX = Math.sign(currentPoint.x - prevPoint.x) + const vectorY = Math.sign(currentPoint.y - prevPoint.y) + + const checkEdge = { vertex1: { x: prevPoint.x, y: prevPoint.y }, vertex2: { x: currentPoint.x, y: currentPoint.y } } + const intersectPoints = [] + roof.lines + .filter((line) => (vectorX === 0 ? line.x1 !== line.x2 : line.y1 !== line.y2)) + .forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const is = edgesIntersection(checkEdge, lineEdge) + if (is && !is.isIntersectionOutside) { + const isVectorX = Math.sign(is.x - prevPoint.x) + const isVectorY = Math.sign(is.y - prevPoint.y) + if ((vectorX === 0 && vectorY === isVectorY) || (vectorY === 0 && vectorX === isVectorX)) { + intersectPoints.push(is) + } + } + }) + if (intersectPoints.length > 0) { + const intersection = intersectPoints[0] + if (vectorX === 0) { + currentPoint.y = intersection.y + nextPoint.y = intersection.y + } else { + currentPoint.x = intersection.x + nextPoint.x = intersection.x + } + } + } + }) + } + sortedPolygonPoints.forEach((startPoint, index) => { let endPoint if (index === sortedPolygonPoints.length - 1) { @@ -2239,6 +2068,7 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } else { endPoint = sortedPolygonPoints[index + 1] } + const hipLine = drawHipLine([startPoint.x, startPoint.y, endPoint.x, endPoint.y], canvas, roof, textMode, null, currentDegree, currentDegree) if (currentVectorX === 0) { if (Math.sign(startPoint.x - endPoint.x) === 0) { @@ -2251,12 +2081,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } baseHipLines.push({ x1: hipLine.x1, y1: hipLine.y1, x2: hipLine.x2, y2: hipLine.y2, line: hipLine }) }) - - canvas - .getObjects() - .filter((obj) => obj.name === 'checkLine' || obj.name === 'checkRoofLine') - .forEach((obj) => canvas.remove(obj)) - canvas.renderAll() }) drawGablePolygonSecond.forEach((current) => { @@ -2279,100 +2103,182 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const prevOffset = prevLine.attributes.offset const nextOffset = nextLine.attributes.offset - const prevEdge = { vertex1: { x: prevLine.x1, y: prevLine.y1 }, vertex2: { x: prevLine.x2, y: prevLine.y2 } } - const prevRidge = baseGableRidgeLines.find((ridge) => { - const ridgeEdge = { vertex1: { x: ridge.x1, y: ridge.y1 }, vertex2: { x: ridge.x2, y: ridge.y2 } } - const is = edgesIntersection(prevEdge, ridgeEdge) - if (is && !is.isIntersectionOutside) { - return ridge - } - }) + const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint) + const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint) - const nextEdge = { vertex1: { x: nextLine.x1, y: nextLine.y1 }, vertex2: { x: nextLine.x2, y: nextLine.y2 } } - const nextRidge = baseGableRidgeLines.find((ridge) => { - const ridgeEdge = { vertex1: { x: ridge.x1, y: ridge.y1 }, vertex2: { x: ridge.x2, y: ridge.y2 } } - const is = edgesIntersection(nextEdge, ridgeEdge) - if (is && !is.isIntersectionOutside) { - return ridge - } - }) + const polygonPoints = [] + if (Big(prevAngle).minus(Big(nextAngle)).abs().eq(180)) { + const currentRidge = baseGableRidgeLines.find((line) => + currentVectorX === 0 + ? (line.y1 === y1 && line.y2 === y2) || (line.y1 === y2 && line.y2 === y1) + : (line.x1 === x1 && line.x2 === x2) || (line.x1 === x2 && line.x2 === x1), + ) + if (currentRidge) { + const ridgeVectorX = Math.sign(currentRidge.x1 - currentRidge.x2) + const ridgeVectorY = Math.sign(currentRidge.y1 - currentRidge.y2) - let currentRidge - - if (prevRidge) { - if ( - currentVectorX === 0 && - ((prevRidge.y1 <= currentLine.y1 && prevRidge.y2 >= currentLine.y1 && prevRidge.y1 <= currentLine.y2 && prevRidge.y2 >= currentLine.y2) || - (prevRidge.y1 >= currentLine.y1 && prevRidge.y2 <= currentLine.y1 && prevRidge.y1 >= currentLine.y2 && prevRidge.y2 <= currentLine.y2)) - ) { - currentRidge = prevRidge - } - if ( - currentVectorY === 0 && - ((prevRidge.x1 <= currentLine.x1 && prevRidge.x2 >= currentLine.x1 && prevRidge.x1 <= currentLine.x2 && prevRidge.x2 >= currentLine.x2) || - (prevRidge.x1 >= currentLine.x1 && prevRidge.x2 <= currentLine.x1 && prevRidge.x1 >= currentLine.x2 && prevRidge.x2 <= currentLine.x2)) - ) { - currentRidge = prevRidge - } - } - if (nextRidge) { - if ( - currentVectorX === 0 && - ((nextRidge.y1 <= currentLine.y1 && nextRidge.y2 >= currentLine.y1 && nextRidge.y1 <= currentLine.y2 && nextRidge.y2 >= currentLine.y2) || - (nextRidge.y1 >= currentLine.y1 && nextRidge.y2 <= currentLine.y1 && nextRidge.y1 >= currentLine.y2 && nextRidge.y2 <= currentLine.y2)) - ) { - currentRidge = nextRidge - } - if ( - currentVectorY === 0 && - ((nextRidge.x1 <= currentLine.x1 && nextRidge.x2 >= currentLine.x1 && nextRidge.x1 <= currentLine.x2 && nextRidge.x2 >= currentLine.x2) || - (nextRidge.x1 >= currentLine.x1 && nextRidge.x2 <= currentLine.x1 && nextRidge.x1 >= currentLine.x2 && nextRidge.x2 <= currentLine.x2)) - ) { - currentRidge = nextRidge - } - } - if (currentRidge) { - const vectorX = currentVectorX === 0 ? Math.sign(currentLine.x1 - currentRidge.x1) : 0 - const vectorY = currentVectorY === 0 ? Math.sign(currentLine.y1 - currentRidge.y1) : 0 - - const polygonPoints = [ - { x: currentRidge.x1, y: currentRidge.y1 }, - { x: currentRidge.x2, y: currentRidge.y2 }, - ] - if (currentVectorX === 0) { - polygonPoints.push( - { x: Big(currentLine.x1).plus(Big(currentOffset).times(vectorX)).toNumber(), y: currentRidge.y1 }, - { x: Big(currentLine.x2).plus(Big(currentOffset).times(vectorX)).toNumber(), y: currentRidge.y2 }, - ) - } else { - polygonPoints.push( - { x: currentRidge.x1, y: Big(currentLine.y1).plus(Big(currentOffset).times(vectorY)).toNumber() }, - { x: currentRidge.x2, y: Big(currentLine.y2).plus(Big(currentOffset).times(vectorY)).toNumber() }, - ) - } - - const sortedPolygonPoints = getSortedPoint(polygonPoints) - - sortedPolygonPoints.forEach((startPoint, index) => { - let endPoint - if (index === sortedPolygonPoints.length - 1) { - endPoint = sortedPolygonPoints[0] - } else { - endPoint = sortedPolygonPoints[index + 1] - } - const hipLine = drawHipLine([startPoint.x, startPoint.y, endPoint.x, endPoint.y], canvas, roof, textMode, null, currentDegree, currentDegree) + let checkEdge if (currentVectorX === 0) { - if (Math.sign(startPoint.x - endPoint.x) === 0) { - hipLine.attributes.actualSize = hipLine.attributes.planeSize + checkEdge = { vertex1: { x: currentRidge.x1, y: currentRidge.y1 }, vertex2: { x: currentLine.x1, y: currentRidge.y1 } } + } else { + checkEdge = { vertex1: { x: currentRidge.x1, y: currentRidge.y1 }, vertex2: { x: currentRidge.x1, y: currentLine.y1 } } + } + const isRoofLines = [] + roof.lines + .filter((line) => (currentVectorX === 0 ? line.x1 === line.x2 : line.y1 === line.y2)) + .forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const is = edgesIntersection(checkEdge, lineEdge) + if (is) { + const checkVectorX = Math.sign(checkEdge.vertex1.x - checkEdge.vertex2.x) + const checkVectorY = Math.sign(checkEdge.vertex1.y - checkEdge.vertex2.y) + const isVectorX = Math.sign(checkEdge.vertex1.x - is.x) + const isVectorY = Math.sign(checkEdge.vertex1.y - is.y) + if ((ridgeVectorX === 0 && checkVectorX === isVectorX) || (ridgeVectorY === 0 && checkVectorY === isVectorY)) { + const size = ridgeVectorX === 0 ? Math.abs(checkEdge.vertex1.x - is.x) : Math.abs(checkEdge.vertex1.y - is.y) + isRoofLines.push({ line, size }) + } + } + }) + isRoofLines.sort((a, b) => a.size - b.size) + const roofLine = isRoofLines[0].line + polygonPoints.push({ x: currentRidge.x1, y: currentRidge.y1 }, { x: currentRidge.x2, y: currentRidge.y2 }) + if (ridgeVectorX === 0) { + polygonPoints.push({ x: roofLine.x1, y: currentRidge.y1 }, { x: roofLine.x1, y: currentRidge.y2 }) + } else { + polygonPoints.push({ x: currentRidge.x1, y: roofLine.y1 }, { x: currentRidge.x2, y: roofLine.y1 }) + } + } + } else { + const prevEdge = { vertex1: { x: prevLine.x1, y: prevLine.y1 }, vertex2: { x: prevLine.x2, y: prevLine.y2 } } + const prevRidge = baseGableRidgeLines.find((ridge) => { + const ridgeEdge = { vertex1: { x: ridge.x1, y: ridge.y1 }, vertex2: { x: ridge.x2, y: ridge.y2 } } + const is = edgesIntersection(prevEdge, ridgeEdge) + if (is && !is.isIntersectionOutside) { + return ridge + } + }) + + const nextEdge = { vertex1: { x: nextLine.x1, y: nextLine.y1 }, vertex2: { x: nextLine.x2, y: nextLine.y2 } } + const nextRidge = baseGableRidgeLines.find((ridge) => { + const ridgeEdge = { vertex1: { x: ridge.x1, y: ridge.y1 }, vertex2: { x: ridge.x2, y: ridge.y2 } } + const is = edgesIntersection(nextEdge, ridgeEdge) + if (is && !is.isIntersectionOutside) { + return ridge + } + }) + + let currentRidge + + if (prevRidge) { + if ( + currentVectorX === 0 && + ((prevRidge.y1 <= currentLine.y1 && prevRidge.y2 >= currentLine.y1 && prevRidge.y1 <= currentLine.y2 && prevRidge.y2 >= currentLine.y2) || + (prevRidge.y1 >= currentLine.y1 && prevRidge.y2 <= currentLine.y1 && prevRidge.y1 >= currentLine.y2 && prevRidge.y2 <= currentLine.y2)) + ) { + currentRidge = prevRidge + } + if ( + currentVectorY === 0 && + ((prevRidge.x1 <= currentLine.x1 && prevRidge.x2 >= currentLine.x1 && prevRidge.x1 <= currentLine.x2 && prevRidge.x2 >= currentLine.x2) || + (prevRidge.x1 >= currentLine.x1 && prevRidge.x2 <= currentLine.x1 && prevRidge.x1 >= currentLine.x2 && prevRidge.x2 <= currentLine.x2)) + ) { + currentRidge = prevRidge + } + } + if (nextRidge) { + if ( + currentVectorX === 0 && + ((nextRidge.y1 <= currentLine.y1 && nextRidge.y2 >= currentLine.y1 && nextRidge.y1 <= currentLine.y2 && nextRidge.y2 >= currentLine.y2) || + (nextRidge.y1 >= currentLine.y1 && nextRidge.y2 <= currentLine.y1 && nextRidge.y1 >= currentLine.y2 && nextRidge.y2 <= currentLine.y2)) + ) { + currentRidge = nextRidge + } + if ( + currentVectorY === 0 && + ((nextRidge.x1 <= currentLine.x1 && nextRidge.x2 >= currentLine.x1 && nextRidge.x1 <= currentLine.x2 && nextRidge.x2 >= currentLine.x2) || + (nextRidge.x1 >= currentLine.x1 && nextRidge.x2 <= currentLine.x1 && nextRidge.x1 >= currentLine.x2 && nextRidge.x2 <= currentLine.x2)) + ) { + currentRidge = nextRidge + } + } + if (currentRidge) { + polygonPoints.push({ x: currentRidge.x1, y: currentRidge.y1 }, { x: currentRidge.x2, y: currentRidge.y2 }) + + /** 마루에서 현재라인으로 향하는 외벽선까지의 포인트를 확인 하여 처리*/ + let checkEdge + if (currentVectorX === 0) { + checkEdge = { + vertex1: { x: currentRidge.x1, y: currentRidge.y1 }, + vertex2: { x: currentLine.x1, y: currentRidge.y1 }, } } else { - if (Math.sign(startPoint.y - endPoint.y) === 0) { - hipLine.attributes.actualSize = hipLine.attributes.planeSize + checkEdge = { + vertex1: { x: currentRidge.x1, y: currentRidge.y1 }, + vertex2: { x: currentRidge.x1, y: currentLine.y1 }, } } - baseHipLines.push({ x1: hipLine.x1, y1: hipLine.y1, x2: hipLine.x2, y2: hipLine.y2, line: hipLine }) - }) + + const checkVectorX = Math.sign(checkEdge.vertex1.x - checkEdge.vertex2.x) + const checkVectorY = Math.sign(checkEdge.vertex1.y - checkEdge.vertex2.y) + const intersectPoints = [] + roof.lines + .filter((line) => (currentVectorX === 0 ? line.x1 === line.x2 : line.y1 === line.y2)) + .forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const is = edgesIntersection(checkEdge, lineEdge) + if (is) { + const isVectorX = Math.sign(checkEdge.vertex1.x - is.x) + const isVectorY = Math.sign(checkEdge.vertex1.y - is.y) + const isLineOtherPoint = + is.x === line.x1 && is.y === line.y1 + ? { x: line.x2, y: line.y2 } + : { + x: line.x1, + y: line.y1, + } + const isInPoint = + checkVectorX === 0 + ? (currentRidge.x1 <= isLineOtherPoint.x && isLineOtherPoint.x <= currentRidge.x2) || + (currentRidge.x1 >= isLineOtherPoint.x && isLineOtherPoint.x >= currentRidge.x2) + : (currentRidge.y1 <= isLineOtherPoint.y && isLineOtherPoint.y <= currentRidge.y2) || + (currentRidge.y1 >= isLineOtherPoint.y && isLineOtherPoint.y >= currentRidge.y2) + if (checkVectorX === isVectorX && checkVectorY === isVectorY && isInPoint) { + const size = Big(checkEdge.vertex1.x).minus(is.x).abs().pow(2).plus(Big(checkEdge.vertex1.y).minus(is.y).abs().pow(2)).sqrt() + intersectPoints.push({ is, size }) + } + } + }) + intersectPoints.sort((a, b) => a.size - b.size) + let intersect = intersectPoints[0].is + if (currentVectorX === 0) { + polygonPoints.push({ x: intersect.x, y: currentRidge.y1 }, { x: intersect.x, y: currentRidge.y2 }) + } else { + polygonPoints.push({ x: currentRidge.x1, y: intersect.y }, { x: currentRidge.x2, y: intersect.y }) + } + } } + + const sortedPolygonPoints = getSortedPoint(polygonPoints) + sortedPolygonPoints.forEach((startPoint, index) => { + let endPoint + if (index === sortedPolygonPoints.length - 1) { + endPoint = sortedPolygonPoints[0] + } else { + endPoint = sortedPolygonPoints[index + 1] + } + const hipLine = drawHipLine([startPoint.x, startPoint.y, endPoint.x, endPoint.y], canvas, roof, textMode, null, currentDegree, currentDegree) + if (currentVectorX === 0) { + if (Math.sign(startPoint.x - endPoint.x) === 0) { + hipLine.attributes.actualSize = hipLine.attributes.planeSize + } + } else { + if (Math.sign(startPoint.y - endPoint.y) === 0) { + hipLine.attributes.actualSize = hipLine.attributes.planeSize + } + } + baseHipLines.push({ x1: hipLine.x1, y1: hipLine.y1, x2: hipLine.x2, y2: hipLine.y2, line: hipLine }) + }) }) /** ⨆ 모양 처마에 추녀마루를 그린다. */ @@ -4716,7 +4622,8 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { }) const innerLines = [...baseRidgeLines, ...baseGableRidgeLines, ...baseHipLines.map((line) => line.line)] - const uniqueInnerLines = [] + roof.innerLines = innerLines + /*const uniqueInnerLines = [] innerLines.forEach((currentLine) => { const sameLines = uniqueInnerLines.filter( @@ -4733,7 +4640,7 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } }) canvas.renderAll() - roof.innerLines = uniqueInnerLines + roof.innerLines = uniqueInnerLines*/ /** 확인용 라인, 포인트 제거 */ canvas @@ -7729,18 +7636,20 @@ const getSortedPoint = (points) => { let prevPoint = startPoint for (let i = 0; i < points.length - 1; i++) { + const samePoints = [] points .filter((point) => !sortedPoints.includes(point)) .forEach((point) => { if (i % 2 === 1 && prevPoint.y === point.y) { - sortedPoints.push(point) - prevPoint = point + samePoints.push({ point, size: Math.abs(point.x - prevPoint.x) }) } if (i % 2 === 0 && prevPoint.x === point.x) { - sortedPoints.push(point) - prevPoint = point + samePoints.push({ point, size: Math.abs(point.y - prevPoint.y) }) } }) + const samePoint = samePoints.sort((a, b) => a.size - b.size)[0].point + sortedPoints.push(samePoint) + prevPoint = samePoint } return sortedPoints } From d54103943d8605d212f712b9fde7dacf51c3111b Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Fri, 9 May 2025 13:45:10 +0900 Subject: [PATCH 27/46] =?UTF-8?q?0=EB=B2=88=EC=A7=B8=EC=9D=BC=20=EA=B2=BD?= =?UTF-8?q?=EC=9A=B0=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/qpolygon-utils.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index 475d2ae6..1af80680 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -314,15 +314,22 @@ export function removeDuplicatePolygons(polygons) { // 현재 point의 x와 이전 포인트의 x와 같을경우, 다음 포인트의 x와 달라야 함. // 현재 point의 y와 이전 포인트의 y와 같을경우, 다음 포인트의 y와 달라야 함. const isValidPoints = (points) => { - for (let i = 1; i < points.length; i++) { - let prev = points[i - 1] - let curr = points[i] - let next = points[i + 1] - - if (i === points.length - 1) { + let prev + let curr + let next + for (let i = 0; i < points.length; i++) { + if (i === 0) { + prev = points[points.length - 1] + curr = points[i] + next = points[i + 1] + } else if (i === points.length - 1) { prev = points[i - 1] curr = points[i] next = points[0] + } else { + prev = points[i - 1] + curr = points[i] + next = points[i + 1] } // 현재와 이전의 x가 같다면 다음의 x는 달라야 함 From 553cbd44dbc86a5b157402fbf513a1dea59d0398 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Fri, 9 May 2025 16:52:04 +0900 Subject: [PATCH 28/46] =?UTF-8?q?=EC=98=A4=EC=B0=A8=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/canvas-util.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/util/canvas-util.js b/src/util/canvas-util.js index 0f0b69dc..5955cbce 100644 --- a/src/util/canvas-util.js +++ b/src/util/canvas-util.js @@ -360,16 +360,16 @@ export const calculateIntersection = (line1, line2) => { // Check if the intersection X and Y are within the range of both lines if ( - result[0] >= line1MinX && - result[0] <= line1MaxX && - result[0] >= line2MinX && - result[0] <= line2MaxX && - result[1] >= line1MinY && - result[1] <= line1MaxY && - result[1] >= line2MinY && - result[1] <= line2MaxY + result[0] >= line1MinX - 1 && + result[0] <= line1MaxX + 1 && + result[0] >= line2MinX - 1 && + result[0] <= line2MaxX + 1 && + result[1] >= line1MinY - 1 && + result[1] <= line1MaxY + 1 && + result[1] >= line2MinY - 1 && + result[1] <= line2MaxY + 1 ) { - return { x: Math.round(result[0]), y: Math.round(result[1]) } + return { x: result[0], y: result[1] } } else { return null // Intersection is out of range } From f48a8b31d370981e5dceabb3628b5a8678e9b9e4 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Mon, 12 May 2025 14:09:48 +0900 Subject: [PATCH 29/46] =?UTF-8?q?=EC=A7=80=EB=B6=95=EC=9E=AC=20=EB=B6=84?= =?UTF-8?q?=ED=95=A0=20=EC=8B=9C=20addLengthText=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/usePolygon.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js index 4d59d292..735a85f5 100644 --- a/src/hooks/usePolygon.js +++ b/src/hooks/usePolygon.js @@ -1160,7 +1160,7 @@ export const usePolygon = () => { }) canvas.add(roof) - addLengthText(roof) + // addLengthText(roof) canvas.remove(polygon) canvas.renderAll() }) From f4ba3058c7443b67406d12461bf84315983cacc6 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Mon, 12 May 2025 16:04:53 +0900 Subject: [PATCH 30/46] =?UTF-8?q?range=20=EA=B3=84=EC=82=B0=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/canvas-util.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/canvas-util.js b/src/util/canvas-util.js index 5955cbce..9420e910 100644 --- a/src/util/canvas-util.js +++ b/src/util/canvas-util.js @@ -536,8 +536,8 @@ export function isPointOnLine({ x1, y1, x2, y2 }, { x, y }, epsilon = 2) { if (Math.abs(crossProduct) > 1000) return false // 작은 오차 허용 // 점이 선분의 범위 내에 있는지 확인 - const withinXRange = Math.abs(Math.min(x1, x2) - x) <= 2 || 2 <= Math.abs(Math.max(x1, x2) - x) - const withinYRange = Math.abs(Math.min(y1, y2) - y) <= 2 || 2 <= Math.abs(Math.max(y1, y2) - y) + const withinXRange = Math.min(x1, x2) - x <= 2 && 2 <= Math.max(x1, x2) - x + const withinYRange = Math.min(y1, y2) - y <= 2 && 2 <= Math.max(y1, y2) - y return withinXRange && withinYRange } From 976396cd787a0b30f66668494e2fdda9fecfe288 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Mon, 12 May 2025 18:18:31 +0900 Subject: [PATCH 31/46] =?UTF-8?q?valid=EC=97=90=20=ED=95=B4=EB=8B=B9?= =?UTF-8?q?=ED=95=98=EC=A7=80=EC=95=8A=EB=8A=94=20polygon=EC=9D=B4=202?= =?UTF-8?q?=EA=B0=9C=20=EC=9D=B4=EC=83=81=EC=9D=BC=20=EA=B2=BD=EC=9A=B0=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/qpolygon-utils.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index 1af80680..280245c6 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -304,9 +304,19 @@ export function removeDuplicatePolygons(polygons) { } }) - uniquePolygons = uniquePolygons.filter((polygon) => { - return isValidPoints(polygon) - }) + //uniquePolygons중 isValidPoints의 조건을 만족하는 카운트 계산 + const validCount = uniquePolygons.reduce((acc, polygon) => { + if (!isValidPoints(polygon)) { + return acc + 1 + } + return acc + }, 0) + + if (validCount > 1) { + uniquePolygons = uniquePolygons.filter((polygon) => { + return isValidPoints(polygon) + }) + } return uniquePolygons } From 72c020aceaf9eedbc9d2852e4acf4177d13c2dd0 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Tue, 13 May 2025 09:57:36 +0900 Subject: [PATCH 32/46] =?UTF-8?q?pointOnLine=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/canvas-util.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/util/canvas-util.js b/src/util/canvas-util.js index 9420e910..3e3f4059 100644 --- a/src/util/canvas-util.js +++ b/src/util/canvas-util.js @@ -535,9 +535,21 @@ export function isPointOnLine({ x1, y1, x2, y2 }, { x, y }, epsilon = 2) { const crossProduct = (y - y1) * (x2 - x1) - (x - x1) * (y2 - y1) if (Math.abs(crossProduct) > 1000) return false // 작은 오차 허용 + const isSameX = Math.abs(x1 - x2) < 2 + const isSameY = Math.abs(y1 - y2) < 2 + // 점이 선분의 범위 내에 있는지 확인 - const withinXRange = Math.min(x1, x2) - x <= 2 && 2 <= Math.max(x1, x2) - x - const withinYRange = Math.min(y1, y2) - y <= 2 && 2 <= Math.max(y1, y2) - y + let withinXRange = Math.min(x1, x2) - x <= 2 + if (!isSameX) { + withinXRange = withinXRange && 2 <= Math.max(x1, x2) - x + } + + let withinYRange = Math.min(y1, y2) - y <= 2 + if (!isSameY) { + withinYRange = withinYRange && 2 <= Math.max(y1, y2) - y + } + + console.log(Math.min(x1, x2) - x, Math.max(x1, x2) - x) return withinXRange && withinYRange } From c52d914c6e940edf8e80eb7d895d01a690c301fc Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Tue, 13 May 2025 10:55:58 +0900 Subject: [PATCH 33/46] =?UTF-8?q?validCheck=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/qpolygon-utils.js | 67 +++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 34 deletions(-) diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index 280245c6..d13a9e54 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -304,19 +304,9 @@ export function removeDuplicatePolygons(polygons) { } }) - //uniquePolygons중 isValidPoints의 조건을 만족하는 카운트 계산 - const validCount = uniquePolygons.reduce((acc, polygon) => { - if (!isValidPoints(polygon)) { - return acc + 1 - } - return acc - }, 0) - - if (validCount > 1) { - uniquePolygons = uniquePolygons.filter((polygon) => { - return isValidPoints(polygon) - }) - } + uniquePolygons = uniquePolygons.filter((polygon) => { + return isValidPoints(polygon) + }) return uniquePolygons } @@ -324,34 +314,43 @@ export function removeDuplicatePolygons(polygons) { // 현재 point의 x와 이전 포인트의 x와 같을경우, 다음 포인트의 x와 달라야 함. // 현재 point의 y와 이전 포인트의 y와 같을경우, 다음 포인트의 y와 달라야 함. const isValidPoints = (points) => { - let prev - let curr - let next - for (let i = 0; i < points.length; i++) { - if (i === 0) { - prev = points[points.length - 1] - curr = points[i] - next = points[i + 1] - } else if (i === points.length - 1) { - prev = points[i - 1] - curr = points[i] - next = points[0] - } else { - prev = points[i - 1] - curr = points[i] - next = points[i + 1] - } + function isColinear(p1, p2, p3) { + return (p2.x - p1.x) * (p3.y - p1.y) === (p3.x - p1.x) * (p2.y - p1.y) + } - // 현재와 이전의 x가 같다면 다음의 x는 달라야 함 - if (Math.abs(curr.x - prev.x) < 1 && Math.abs(curr.x - next.x) < 1) { + function segmentsOverlap(a1, a2, b1, b2) { + // 같은 직선 상에 있는가? + if (!isColinear(a1, a2, b1) || !isColinear(a1, a2, b2)) { return false } - // 현재와 이전의 y가 같다면 다음의 y는 달라야 함 - if (Math.abs(curr.y - prev.y) < 1 && Math.abs(curr.y - next.y) < 1) { + const isHorizontal = a1.y === a2.y + if (isHorizontal) { + const aMin = Math.min(a1.x, a2.x), + aMax = Math.max(a1.x, a2.x) + const bMin = Math.min(b1.x, b2.x), + bMax = Math.max(b1.x, b2.x) + return Math.max(aMin, bMin) < Math.min(aMax, bMax) + } else { + const aMin = Math.min(a1.y, a2.y), + aMax = Math.max(a1.y, a2.y) + const bMin = Math.min(b1.y, b2.y), + bMax = Math.max(b1.y, b2.y) + return Math.max(aMin, bMin) < Math.min(aMax, bMax) + } + } + + for (let i = 0; i < points.length - 2; i++) { + const a1 = points[i] + const a2 = points[i + 1] + const b1 = points[i + 1] // 연속되는 점 + const b2 = points[i + 2] + + if (segmentsOverlap(a1, a2, b1, b2)) { return false } } + return true } From 7bf548de2b8ec3eb6374340c02270dfad2af39fb Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Tue, 13 May 2025 14:22:25 +0900 Subject: [PATCH 34/46] =?UTF-8?q?=EC=A3=BC=EC=84=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/qpolygon-utils.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index d13a9e54..a3f78cb6 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -311,8 +311,7 @@ export function removeDuplicatePolygons(polygons) { return uniquePolygons } -// 현재 point의 x와 이전 포인트의 x와 같을경우, 다음 포인트의 x와 달라야 함. -// 현재 point의 y와 이전 포인트의 y와 같을경우, 다음 포인트의 y와 달라야 함. +// 같은 직선상에 있는지 확인 같은 직선이라면 polygon을 생성할 수 없으므로 false const isValidPoints = (points) => { function isColinear(p1, p2, p3) { return (p2.x - p1.x) * (p3.y - p1.y) === (p3.x - p1.x) * (p2.y - p1.y) From bac3fbef70da593efd8c4756b33859ce31b50845 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Tue, 13 May 2025 14:34:10 +0900 Subject: [PATCH 35/46] =?UTF-8?q?addLengthText=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/usePolygon.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js index 735a85f5..3fd5fd2e 100644 --- a/src/hooks/usePolygon.js +++ b/src/hooks/usePolygon.js @@ -1160,7 +1160,8 @@ export const usePolygon = () => { }) canvas.add(roof) - // addLengthText(roof) + canvas.renderAll() + addLengthText(roof) canvas.remove(polygon) canvas.renderAll() }) From d06733ed4cfb31449217f75e2485eaa7fec00685 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Tue, 13 May 2025 14:36:11 +0900 Subject: [PATCH 36/46] =?UTF-8?q?=EC=9B=90=EB=B3=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/usePolygon.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js index 3fd5fd2e..735a85f5 100644 --- a/src/hooks/usePolygon.js +++ b/src/hooks/usePolygon.js @@ -1160,8 +1160,7 @@ export const usePolygon = () => { }) canvas.add(roof) - canvas.renderAll() - addLengthText(roof) + // addLengthText(roof) canvas.remove(polygon) canvas.renderAll() }) From 001c1cfd95a118e197ad4ba8c1c943e38f7ab9dd Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Tue, 13 May 2025 14:46:57 +0900 Subject: [PATCH 37/46] =?UTF-8?q?useEffect=EB=82=B4=EB=B6=80=20async=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/floor-plan/CanvasFrame.jsx | 45 ++++++++++++----------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/src/components/floor-plan/CanvasFrame.jsx b/src/components/floor-plan/CanvasFrame.jsx index b43fd559..4e0a14c0 100644 --- a/src/components/floor-plan/CanvasFrame.jsx +++ b/src/components/floor-plan/CanvasFrame.jsx @@ -36,28 +36,31 @@ import { useMasterController } from '@/hooks/common/useMasterController' export default function CanvasFrame() { const [roofMaterials, setRoofMaterials] = useRecoilState(roofMaterialsAtom) const { getRoofMaterialList } = useMasterController() - useEffect(async () => { - if (roofMaterials.length !== 0) { - return - } - const { data } = await getRoofMaterialList() + useEffect(() => { + async function initRoofMaterial() { + if (roofMaterials.length !== 0) { + return + } + const { data } = await getRoofMaterialList() - const roofLists = data.map((item, idx) => ({ - ...item, - id: item.roofMatlCd, - name: item.roofMatlNm, - selected: idx === 0, - index: idx, - nameJp: item.roofMatlNmJp, - length: item.lenBase && parseInt(item.lenBase), - width: item.widBase && parseInt(item.widBase), - raft: item.raftBase && parseInt(item.raftBase), - layout: ['ROOF_ID_SLATE', 'ROOF_ID_SINGLE'].includes(item.roofMatlCd) ? ROOF_MATERIAL_LAYOUT.STAIRS : ROOF_MATERIAL_LAYOUT.PARALLEL, - hajebichi: item.roofPchBase && parseInt(item.roofPchBase), - pitch: item.pitch ? parseInt(item.pitch) : 4, - angle: item.angle ? parseInt(item.angle) : 21.8, - })) - setRoofMaterials(roofLists) + const roofLists = data.map((item, idx) => ({ + ...item, + id: item.roofMatlCd, + name: item.roofMatlNm, + selected: idx === 0, + index: idx, + nameJp: item.roofMatlNmJp, + length: item.lenBase && parseInt(item.lenBase), + width: item.widBase && parseInt(item.widBase), + raft: item.raftBase && parseInt(item.raftBase), + layout: ['ROOF_ID_SLATE', 'ROOF_ID_SINGLE'].includes(item.roofMatlCd) ? ROOF_MATERIAL_LAYOUT.STAIRS : ROOF_MATERIAL_LAYOUT.PARALLEL, + hajebichi: item.roofPchBase && parseInt(item.roofPchBase), + pitch: item.pitch ? parseInt(item.pitch) : 4, + angle: item.angle ? parseInt(item.angle) : 21.8, + })) + setRoofMaterials(roofLists) + } + initRoofMaterial() }, []) const canvasRef = useRef(null) const { canvas } = useCanvas('canvas') From 3574ed3a9b23104b6c3625f4392750e73a039f85 Mon Sep 17 00:00:00 2001 From: Jaeyoung Lee Date: Tue, 13 May 2025 15:21:33 +0900 Subject: [PATCH 38/46] =?UTF-8?q?=EC=A7=80=EB=B6=95=ED=98=95=EC=83=81=20?= =?UTF-8?q?=EB=B6=84=ED=95=A0=20=EB=8C=80=EC=9D=91=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/qpolygon-utils.js | 1390 +++++++++++++++++++++++------------- 1 file changed, 893 insertions(+), 497 deletions(-) diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index 475d2ae6..e0175551 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -806,8 +806,8 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { let { x1, y1, x2, y2 } = currentLine - if (currentAngle !== prevAngle) { - if (currentAngle === nextAngle) { + if (currentAngle !== prevAngle || (currentAngle === prevAngle && currentLine.attributes.type !== prevLine.attributes.type)) { + if (currentAngle === nextAngle && currentLine.attributes.type === nextLine.attributes.type) { let nextIndex = baseLines.findIndex((line) => line === nextLine) while (nextIndex !== index) { const checkNextLine = baseLines[(nextIndex + 1 + baseLines.length) % baseLines.length] @@ -825,6 +825,8 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } }) + console.log('drawBaseLines : ', drawBaseLines) + /** baseLine을 기준으로 확인용 polygon 작성 */ const checkWallPolygon = new QPolygon(baseLinePoints, {}) @@ -1020,350 +1022,399 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const currentMidX = Big(x1).plus(Big(x2)).div(2).plus(Big(prevVectorX).times(currentLine.attributes.offset)) const currentMidY = Big(y1).plus(Big(y2)).div(2).plus(Big(prevVectorY).times(currentLine.attributes.offset)) - if (beforePrevBaseLine === afterNextBaseLine) { - const afterNextMidX = Big(afterNextLine.x1).plus(Big(afterNextLine.x2)).div(2) - const afterNextMidY = Big(afterNextLine.y1).plus(Big(afterNextLine.y2)).div(2) - const vectorMidX = Math.sign(currentMidX.minus(afterNextMidX)) - const vectorMidY = Math.sign(currentMidY.minus(afterNextMidY)) + /** 마루 반대 좌표*/ + let oppositeMidX = currentMidX, + oppositeMidY = currentMidY - let oppositeMidX, oppositeMidY - if (eavesType.includes(afterNextLine.attributes?.type)) { - const checkSize = currentMidX - .minus(afterNextMidX) - .pow(2) - .plus(currentMidY.minus(afterNextMidY).pow(2)) - .sqrt() - .minus(Big(afterNextLine.attributes.planeSize).div(20)) - .round(1) - oppositeMidX = currentMidX.plus(checkSize.times(vectorMidX).neg()) - oppositeMidY = currentMidY.plus(checkSize.times(vectorMidY).neg()) + if (prevAngle === beforePrevAngle || nextAngle === afterNextAngle) { + console.log('지붕선 분할 : start') + const checkLine1 = new fabric.Line([prevLine.x1, prevLine.y1, prevLine.x2, prevLine.y2], { + stroke: 'red', + strokeWidth: 4, + parentId: roofId, + name: 'checkLine', + }) + const checkLine2 = new fabric.Line([nextLine.x1, nextLine.y1, nextLine.x2, nextLine.y2], { + stroke: 'blue', + strokeWidth: 4, + parentId: roofId, + name: 'checkLine', + }) + const checkPoint = new fabric.Circle({ + left: currentMidX.toNumber(), + top: currentMidY.toNumber(), + radius: 5, + fill: 'red', + parentId: roofId, + name: 'checkPoint', + }) + canvas.add(checkLine1) + canvas.add(checkLine2) + canvas.add(checkPoint) + canvas.renderAll() - const xVector1 = Math.sign(Big(oppositeMidX).minus(Big(afterNextLine.x1)).neg().toNumber()) - const yVector1 = Math.sign(Big(oppositeMidY).minus(Big(afterNextLine.y1)).neg().toNumber()) - const xVector2 = Math.sign(Big(oppositeMidX).minus(Big(afterNextLine.x2)).neg().toNumber()) - const yVector2 = Math.sign(Big(oppositeMidY).minus(Big(afterNextLine.y2)).neg().toNumber()) + if (currentVectorX === 0) { + } else { + } - let addOppositeX1 = 0, - addOppositeY1 = 0, - addOppositeX2 = 0, - addOppositeY2 = 0 + console.log('지붕선 분할 : end') + } else { + if (beforePrevBaseLine === afterNextBaseLine) { + const afterNextMidX = Big(afterNextLine.x1).plus(Big(afterNextLine.x2)).div(2) + const afterNextMidY = Big(afterNextLine.y1).plus(Big(afterNextLine.y2)).div(2) + const vectorMidX = Math.sign(currentMidX.minus(afterNextMidX)) + const vectorMidY = Math.sign(currentMidY.minus(afterNextMidY)) - if (!checkWallPolygon.inPolygon({ x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() })) { - const checkScale = currentMidX.minus(oppositeMidX).pow(2).plus(currentMidY.minus(oppositeMidY).pow(2)).sqrt() - addOppositeX1 = checkScale.times(xVector1).toNumber() - addOppositeY1 = checkScale.times(yVector1).toNumber() - addOppositeX2 = checkScale.times(xVector2).toNumber() - addOppositeY2 = checkScale.times(yVector2).toNumber() - } + let oppositeMidX, oppositeMidY + if (eavesType.includes(afterNextLine.attributes?.type)) { + const checkSize = currentMidX + .minus(afterNextMidX) + .pow(2) + .plus(currentMidY.minus(afterNextMidY).pow(2)) + .sqrt() + .minus(Big(afterNextLine.attributes.planeSize).div(20)) + .round(1) + oppositeMidX = currentMidX.plus(checkSize.times(vectorMidX).neg()) + oppositeMidY = currentMidY.plus(checkSize.times(vectorMidY).neg()) - let scale1 = Big(afterNextLine.attributes.offset).pow(2).plus(Big(nextLine.attributes.offset).pow(2)).sqrt() - scale1 = scale1.eq(0) ? Big(1) : scale1 - let scale2 = Big(afterNextLine.attributes.offset).pow(2).plus(Big(prevLine.attributes.offset).pow(2)).sqrt() - scale2 = scale2.eq(0) ? Big(1) : scale2 + const xVector1 = Math.sign(Big(oppositeMidX).minus(Big(afterNextLine.x1)).neg().toNumber()) + const yVector1 = Math.sign(Big(oppositeMidY).minus(Big(afterNextLine.y1)).neg().toNumber()) + const xVector2 = Math.sign(Big(oppositeMidX).minus(Big(afterNextLine.x2)).neg().toNumber()) + const yVector2 = Math.sign(Big(oppositeMidY).minus(Big(afterNextLine.y2)).neg().toNumber()) - const checkHip1 = { - x1: Big(afterNextLine.x1).plus(scale1.times(xVector1)).toNumber(), - y1: Big(afterNextLine.y1).plus(scale1.times(yVector1)).toNumber(), - x2: oppositeMidX.plus(addOppositeX1).toNumber(), - y2: oppositeMidY.plus(addOppositeY1).toNumber(), - } + let addOppositeX1 = 0, + addOppositeY1 = 0, + addOppositeX2 = 0, + addOppositeY2 = 0 - const checkHip2 = { - x1: Big(afterNextLine.x2).plus(scale2.times(xVector2)).toNumber(), - y1: Big(afterNextLine.y2).plus(scale2.times(yVector2)).toNumber(), - x2: oppositeMidX.plus(addOppositeX2).toNumber(), - y2: oppositeMidY.plus(addOppositeY2).toNumber(), - } + if (!checkWallPolygon.inPolygon({ x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() })) { + const checkScale = currentMidX.minus(oppositeMidX).pow(2).plus(currentMidY.minus(oppositeMidY).pow(2)).sqrt() + addOppositeX1 = checkScale.times(xVector1).toNumber() + addOppositeY1 = checkScale.times(yVector1).toNumber() + addOppositeX2 = checkScale.times(xVector2).toNumber() + addOppositeY2 = checkScale.times(yVector2).toNumber() + } - const intersection1 = findRoofIntersection(roof, checkHip1, { x: oppositeMidX.plus(addOppositeX1), y: oppositeMidY.plus(addOppositeY1) }) - const intersection2 = findRoofIntersection(roof, checkHip2, { x: oppositeMidX.plus(addOppositeX2), y: oppositeMidY.plus(addOppositeY2) }) + let scale1 = Big(afterNextLine.attributes.offset).pow(2).plus(Big(nextLine.attributes.offset).pow(2)).sqrt() + scale1 = scale1.eq(0) ? Big(1) : scale1 + let scale2 = Big(afterNextLine.attributes.offset).pow(2).plus(Big(prevLine.attributes.offset).pow(2)).sqrt() + scale2 = scale2.eq(0) ? Big(1) : scale2 - const afterNextDegree = afterNextLine.attributes.pitch > 0 ? getDegreeByChon(afterNextLine.attributes.pitch) : afterNextLine.attributes.degree - - if (intersection1) { - const hipLine = drawHipLine( - [intersection1.intersection.x, intersection1.intersection.y, oppositeMidX.plus(addOppositeX1), oppositeMidY.plus(addOppositeY1)], - canvas, - roof, - textMode, - null, - nextDegree, - afterNextDegree, - ) - baseHipLines.push({ - x1: afterNextLine.x1, - y1: afterNextLine.y1, + const checkHip1 = { + x1: Big(afterNextLine.x1).plus(scale1.times(xVector1)).toNumber(), + y1: Big(afterNextLine.y1).plus(scale1.times(yVector1)).toNumber(), x2: oppositeMidX.plus(addOppositeX1).toNumber(), y2: oppositeMidY.plus(addOppositeY1).toNumber(), - line: hipLine, - }) + } + + const checkHip2 = { + x1: Big(afterNextLine.x2).plus(scale2.times(xVector2)).toNumber(), + y1: Big(afterNextLine.y2).plus(scale2.times(yVector2)).toNumber(), + x2: oppositeMidX.plus(addOppositeX2).toNumber(), + y2: oppositeMidY.plus(addOppositeY2).toNumber(), + } + + const intersection1 = findRoofIntersection(roof, checkHip1, { x: oppositeMidX.plus(addOppositeX1), y: oppositeMidY.plus(addOppositeY1) }) + const intersection2 = findRoofIntersection(roof, checkHip2, { x: oppositeMidX.plus(addOppositeX2), y: oppositeMidY.plus(addOppositeY2) }) + + const afterNextDegree = + afterNextLine.attributes.pitch > 0 ? getDegreeByChon(afterNextLine.attributes.pitch) : afterNextLine.attributes.degree + + if (intersection1) { + const hipLine = drawHipLine( + [intersection1.intersection.x, intersection1.intersection.y, oppositeMidX.plus(addOppositeX1), oppositeMidY.plus(addOppositeY1)], + canvas, + roof, + textMode, + null, + nextDegree, + afterNextDegree, + ) + baseHipLines.push({ + x1: afterNextLine.x1, + y1: afterNextLine.y1, + x2: oppositeMidX.plus(addOppositeX1).toNumber(), + y2: oppositeMidY.plus(addOppositeY1).toNumber(), + line: hipLine, + }) + } + if (intersection2) { + const hipLine = drawHipLine( + [intersection2.intersection.x, intersection2.intersection.y, oppositeMidX.plus(addOppositeX2), oppositeMidY.plus(addOppositeY2)], + canvas, + roof, + textMode, + null, + prevDegree, + afterNextDegree, + ) + baseHipLines.push({ + x1: afterNextLine.x2, + y1: afterNextLine.y2, + x2: oppositeMidX.plus(addOppositeX2).toNumber(), + y2: oppositeMidY.plus(addOppositeY2).toNumber(), + line: hipLine, + }) + } + } else { + oppositeMidX = Big(afterNextLine.x1).plus(Big(afterNextLine.x2)).div(2).plus(Big(prevVectorX).neg().times(afterNextLine.attributes.offset)) + oppositeMidY = Big(afterNextLine.y1).plus(Big(afterNextLine.y2)).div(2).plus(Big(prevVectorY).neg().times(afterNextLine.attributes.offset)) } - if (intersection2) { - const hipLine = drawHipLine( - [intersection2.intersection.x, intersection2.intersection.y, oppositeMidX.plus(addOppositeX2), oppositeMidY.plus(addOppositeY2)], + + const vectorOppositeX = Math.sign(currentMidX.minus(oppositeMidX)) + const vectorOppositeY = Math.sign(currentMidY.minus(oppositeMidY)) + + if (vectorMidX === vectorOppositeX && vectorMidY === vectorOppositeY && baseRidgeCount < getMaxRidge(baseLines.length)) { + const ridge = drawRidgeLine( + [currentMidX.toNumber(), currentMidY.toNumber(), oppositeMidX.toNumber(), oppositeMidY.toNumber()], canvas, roof, textMode, - null, - prevDegree, - afterNextDegree, ) - baseHipLines.push({ - x1: afterNextLine.x2, - y1: afterNextLine.y2, - x2: oppositeMidX.plus(addOppositeX2).toNumber(), - y2: oppositeMidY.plus(addOppositeY2).toNumber(), - line: hipLine, - }) + baseGableRidgeLines.push(ridge) + baseRidgeCount++ } } else { - oppositeMidX = Big(afterNextLine.x1).plus(Big(afterNextLine.x2)).div(2).plus(Big(prevVectorX).neg().times(afterNextLine.attributes.offset)) - oppositeMidY = Big(afterNextLine.y1).plus(Big(afterNextLine.y2)).div(2).plus(Big(prevVectorY).neg().times(afterNextLine.attributes.offset)) - } + const vectorMidX = Math.sign(Big(nextLine.x2).minus(nextLine.x1)) + const vectorMidY = Math.sign(Big(nextLine.y2).minus(nextLine.y1)) - const vectorOppositeX = Math.sign(currentMidX.minus(oppositeMidX)) - const vectorOppositeY = Math.sign(currentMidY.minus(oppositeMidY)) + let prevOppositeMidX, prevOppositeMidY, nextOppositeMidX, nextOppositeMidY + const beforePrevOffset = + currentAngle === beforePrevAngle + ? Big(beforePrevLine.attributes.offset) + : Big(beforePrevLine.attributes.offset).plus(currentLine.attributes.offset) + const afterNextOffset = + currentAngle === afterNextAngle + ? Big(afterNextLine.attributes.offset) + : Big(afterNextLine.attributes.offset).plus(currentLine.attributes.offset) + const prevSize = Big(prevLine.attributes.planeSize).div(10) + const nextSize = Big(nextLine.attributes.planeSize).div(10) - if (vectorMidX === vectorOppositeX && vectorMidY === vectorOppositeY && baseRidgeCount < getMaxRidge(baseLines.length)) { - const ridge = drawRidgeLine( - [currentMidX.toNumber(), currentMidY.toNumber(), oppositeMidX.toNumber(), oppositeMidY.toNumber()], - canvas, - roof, - textMode, - ) - baseGableRidgeLines.push(ridge) - baseRidgeCount++ - } - } else { - const vectorMidX = Math.sign(Big(nextLine.x2).minus(nextLine.x1)) - const vectorMidY = Math.sign(Big(nextLine.y2).minus(nextLine.y1)) - let oppositeMidX = currentMidX, - oppositeMidY = currentMidY - let prevOppositeMidX, prevOppositeMidY, nextOppositeMidX, nextOppositeMidY - const beforePrevOffset = - currentAngle === beforePrevAngle - ? Big(beforePrevLine.attributes.offset) - : Big(beforePrevLine.attributes.offset).plus(currentLine.attributes.offset) - const afterNextOffset = - currentAngle === afterNextAngle - ? Big(afterNextLine.attributes.offset) - : Big(afterNextLine.attributes.offset).plus(currentLine.attributes.offset) - const prevSize = Big(prevLine.attributes.planeSize).div(10) - const nextSize = Big(nextLine.attributes.planeSize).div(10) + let prevHipCoords, nextHipCoords - let prevHipCoords, nextHipCoords + /** 다음 라인이 그 다음 라인과의 사이에 추녀마루가 존재 하는지 확인. 처마-처마 인 경우 추녀마루*/ + if (eavesType.includes(afterNextLine.attributes?.type)) { + /** 현재 라인의 길이를 기준으로 추녀 마루의 길이를 삼각함수를 사용하여 판단한다.*/ + let hipLength = Big(size).div(10).div(2).pow(2).plus(Big(size).div(10).div(2).pow(2)).sqrt() - /** 다음 라인이 그 다음 라인과의 사이에 추녀마루가 존재 하는지 확인. 처마-처마 인 경우 추녀마루*/ - if (eavesType.includes(afterNextLine.attributes?.type)) { - /** 현재 라인의 길이를 기준으로 추녀 마루의 길이를 삼각함수를 사용하여 판단한다.*/ - let hipLength = Big(size).div(10).div(2).pow(2).plus(Big(size).div(10).div(2).pow(2)).sqrt() + const nextHalfVector = getHalfAngleVector(nextLine, afterNextLine) + let nextHipVector = { x: nextHalfVector.x, y: nextHalfVector.y } - const nextHalfVector = getHalfAngleVector(nextLine, afterNextLine) - let nextHipVector = { x: nextHalfVector.x, y: nextHalfVector.y } + /** 이전 라인과의 사이 추녀마루의 각도를 확인한다, 각도가 지붕안쪽으로 향하지 않을때 반대로 처리한다.*/ + const nextCheckPoint = { + x: Big(nextLine.x2).plus(Big(nextHalfVector.x).times(10)), + y: Big(nextLine.y2).plus(Big(nextHalfVector.y).times(10)), + } + if (!checkWallPolygon.inPolygon(nextCheckPoint)) { + nextHipVector = { x: Big(nextHipVector.x).neg().toNumber(), y: Big(nextHipVector.y).neg().toNumber() } + } - /** 이전 라인과의 사이 추녀마루의 각도를 확인한다, 각도가 지붕안쪽으로 향하지 않을때 반대로 처리한다.*/ - const nextCheckPoint = { - x: Big(nextLine.x2).plus(Big(nextHalfVector.x).times(10)), - y: Big(nextLine.y2).plus(Big(nextHalfVector.y).times(10)), - } - if (!checkWallPolygon.inPolygon(nextCheckPoint)) { - nextHipVector = { x: Big(nextHipVector.x).neg().toNumber(), y: Big(nextHipVector.y).neg().toNumber() } - } + const nextEndPoint = { + x: Big(nextLine.x2).plus(Big(nextHipVector.x).times(hipLength)), + y: Big(nextLine.y2).plus(Big(nextHipVector.y).times(hipLength)), + } - const nextEndPoint = { - x: Big(nextLine.x2).plus(Big(nextHipVector.x).times(hipLength)), - y: Big(nextLine.y2).plus(Big(nextHipVector.y).times(hipLength)), - } - - let ridgeEdge = { - vertex1: { x: currentMidX.toNumber(), y: currentMidY.toNumber() }, - vertex2: { - x: currentMidX.plus(Big(nextVectorX).times(nextBaseLine.size)).toNumber(), - y: currentMidY.plus(Big(nextVectorY).times(nextBaseLine.size)).toNumber(), - }, - } - let hipEdge = { vertex1: { x: nextLine.x2, y: nextLine.y2 }, vertex2: { x: nextEndPoint.x, y: nextEndPoint.y } } - let intersection = edgesIntersection(ridgeEdge, hipEdge) - if (intersection) { - nextHipCoords = { x1: nextLine.x2, y1: nextLine.y2, x2: intersection.x, y2: intersection.y } - nextOppositeMidY = Big(intersection.y) - nextOppositeMidX = Big(intersection.x) - } - } else { - if (vectorMidX === 0) { - nextOppositeMidY = currentMidY.plus(nextSize.plus(afterNextOffset).times(vectorMidY)) - nextOppositeMidX = currentMidX + let ridgeEdge = { + vertex1: { x: currentMidX.toNumber(), y: currentMidY.toNumber() }, + vertex2: { + x: currentMidX.plus(Big(nextVectorX).times(nextBaseLine.size)).toNumber(), + y: currentMidY.plus(Big(nextVectorY).times(nextBaseLine.size)).toNumber(), + }, + } + let hipEdge = { vertex1: { x: nextLine.x2, y: nextLine.y2 }, vertex2: { x: nextEndPoint.x, y: nextEndPoint.y } } + let intersection = edgesIntersection(ridgeEdge, hipEdge) + if (intersection) { + nextHipCoords = { x1: nextLine.x2, y1: nextLine.y2, x2: intersection.x, y2: intersection.y } + nextOppositeMidY = Big(intersection.y) + nextOppositeMidX = Big(intersection.x) + } } else { - nextOppositeMidX = currentMidX.plus(nextSize.plus(afterNextOffset).times(vectorMidX)) - nextOppositeMidY = currentMidY - } - } - - /** 이전 라인이 그 이전 라인과의 사이에 추녀마루가 존재 하는지 확인. 처마-처마 인 경우 추녀마루*/ - if (eavesType.includes(beforePrevLine.attributes?.type)) { - /** 현재 라인의 길이를 기준으로 추녀 마루의 길이를 삼각함수를 사용하여 판단한다.*/ - let hipLength = Big(size).div(10).div(2).pow(2).plus(Big(size).div(10).div(2).pow(2)).sqrt() - - const prevHalfVector = getHalfAngleVector(prevLine, beforePrevLine) - let prevHipVector = { x: prevHalfVector.x, y: prevHalfVector.y } - - /** 이전 라인과의 사이 추녀마루의 각도를 확인한다, 각도가 지붕안쪽으로 향하지 않을때 반대로 처리한다.*/ - const prevCheckPoint = { - x: Big(prevLine.x1).plus(Big(prevHalfVector.x).times(10)), - y: Big(prevLine.y1).plus(Big(prevHalfVector.y).times(10)), - } - - if (!checkWallPolygon.inPolygon(prevCheckPoint)) { - prevHipVector = { x: Big(prevHipVector.x).neg().toNumber(), y: Big(prevHipVector.y).neg().toNumber() } - } - - const prevEndPoint = { - x: Big(prevLine.x1).plus(Big(prevHipVector.x).times(hipLength)), - y: Big(prevLine.y1).plus(Big(prevHipVector.y).times(hipLength)), - } - - let ridgeEdge = { - vertex1: { x: currentMidX.toNumber(), y: currentMidY.toNumber() }, - vertex2: { - x: currentMidX.plus(Big(prevVectorX).times(prevBaseLine.size)).toNumber(), - y: currentMidY.plus(Big(prevVectorY).times(prevBaseLine.size)).toNumber(), - }, - } - let hipEdge = { vertex1: { x: prevLine.x1, y: prevLine.y1 }, vertex2: { x: prevEndPoint.x, y: prevEndPoint.y } } - let intersection = edgesIntersection(ridgeEdge, hipEdge) - if (intersection) { - prevHipCoords = { x1: prevLine.x1, y1: prevLine.y1, x2: intersection.x, y2: intersection.y } - prevOppositeMidY = Big(intersection.y) - prevOppositeMidX = Big(intersection.x) - } - } else { - if (vectorMidX === 0) { - prevOppositeMidY = currentMidY.plus(prevSize.plus(beforePrevOffset).times(vectorMidY)) - prevOppositeMidX = currentMidX - } else { - prevOppositeMidX = currentMidX.plus(prevSize.plus(beforePrevOffset).times(vectorMidX)) - prevOppositeMidY = currentMidY - } - } - const checkPrevSize = currentMidX.minus(prevOppositeMidX).pow(2).plus(currentMidY.minus(prevOppositeMidY).pow(2)).sqrt() - const checkNextSize = currentMidX.minus(nextOppositeMidX).pow(2).plus(currentMidY.minus(nextOppositeMidY).pow(2)).sqrt() - - /** 두 포인트 중에 current와 가까운 포인트를 사용*/ - if (checkPrevSize.gt(checkNextSize)) { - if (nextHipCoords) { - let intersectPoints = [] - const hipEdge = { vertex1: { x: nextHipCoords.x2, y: nextHipCoords.y2 }, vertex2: { x: nextHipCoords.x1, y: nextHipCoords.y1 } } - - /** 외벽선에서 라인 겹치는 경우에 대한 확인*/ - roof.lines.forEach((line) => { - const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } - const intersection = edgesIntersection(hipEdge, lineEdge) - if ( - intersection && - Math.sign(nextHipCoords.x2 - nextHipCoords.x1) === Math.sign(nextHipCoords.x2 - intersection.x) && - Math.sign(nextHipCoords.y2 - nextHipCoords.y1) === Math.sign(nextHipCoords.y2 - intersection.y) - ) { - const intersectEdge = { vertex1: { x: nextHipCoords.x2, y: nextHipCoords.y2 }, vertex2: { x: intersection.x, y: intersection.y } } - const is = edgesIntersection(intersectEdge, lineEdge) - if (!is.isIntersectionOutside) { - const intersectSize = Big(nextHipCoords.x2) - .minus(Big(intersection.x)) - .pow(2) - .plus(Big(nextHipCoords.y2).minus(Big(intersection.y)).pow(2)) - .abs() - .sqrt() - .toNumber() - - intersectPoints.push({ - intersection, - size: intersectSize, - line, - }) - } - } - }) - const intersect = intersectPoints.sort((a, b) => a.size - b.size)[0] - if (intersect) { - const degree = intersect.line.attributes.pitch > 0 ? getDegreeByChon(intersect.line.attributes.pitch) : intersect.line.attributes.degree - const hipLine = drawHipLine( - [intersect.intersection.x, intersect.intersection.y, nextOppositeMidX.toNumber(), nextOppositeMidY.toNumber()], - canvas, - roof, - textMode, - null, - degree, - degree, - ) - baseHipLines.push({ - x1: nextHipCoords.x1, - y1: nextHipCoords.y1, - x2: nextHipCoords.x2, - y2: nextHipCoords.y2, - line: hipLine, - }) + if (vectorMidX === 0) { + nextOppositeMidY = currentMidY.plus(nextSize.plus(afterNextOffset).times(vectorMidY)) + nextOppositeMidX = currentMidX + } else { + nextOppositeMidX = currentMidX.plus(nextSize.plus(afterNextOffset).times(vectorMidX)) + nextOppositeMidY = currentMidY } } - oppositeMidY = nextOppositeMidY - oppositeMidX = nextOppositeMidX - } else { - if (prevHipCoords) { - let intersectPoints = [] - const hipEdge = { vertex1: { x: prevHipCoords.x2, y: prevHipCoords.y2 }, vertex2: { x: prevHipCoords.x1, y: prevHipCoords.y1 } } - /** 외벽선에서 라인 겹치는 경우에 대한 확인*/ - roof.lines.forEach((line) => { - const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } - const intersection = edgesIntersection(hipEdge, lineEdge) - if ( - intersection && - Math.sign(prevHipCoords.x2 - prevHipCoords.x1) === Math.sign(prevHipCoords.x2 - intersection.x) && - Math.sign(prevHipCoords.y2 - prevHipCoords.y1) === Math.sign(prevHipCoords.y2 - intersection.y) - ) { - const intersectEdge = { vertex1: { x: prevHipCoords.x2, y: prevHipCoords.y2 }, vertex2: { x: intersection.x, y: intersection.y } } - const is = edgesIntersection(intersectEdge, lineEdge) - if (!is.isIntersectionOutside) { - const intersectSize = Big(prevHipCoords.x2) - .minus(Big(intersection.x)) - .pow(2) - .plus(Big(prevHipCoords.y2).minus(Big(intersection.y)).pow(2)) - .abs() - .sqrt() - .toNumber() + /** 이전 라인이 그 이전 라인과의 사이에 추녀마루가 존재 하는지 확인. 처마-처마 인 경우 추녀마루*/ + if (eavesType.includes(beforePrevLine.attributes?.type)) { + /** 현재 라인의 길이를 기준으로 추녀 마루의 길이를 삼각함수를 사용하여 판단한다.*/ + let hipLength = Big(size).div(10).div(2).pow(2).plus(Big(size).div(10).div(2).pow(2)).sqrt() - intersectPoints.push({ - intersection, - size: intersectSize, - line, - }) - } - } - }) - const intersect = intersectPoints.sort((a, b) => a.size - b.size)[0] + const prevHalfVector = getHalfAngleVector(prevLine, beforePrevLine) + let prevHipVector = { x: prevHalfVector.x, y: prevHalfVector.y } - if (intersect) { - const degree = intersect.line.attributes.pitch > 0 ? getDegreeByChon(intersect.line.attributes.pitch) : intersect.line.attributes.degree - const hipLine = drawHipLine( - [intersect.intersection.x, intersect.intersection.y, prevOppositeMidX.toNumber(), prevOppositeMidY.toNumber()], - canvas, - roof, - textMode, - null, - degree, - degree, - ) - baseHipLines.push({ - x1: prevHipCoords.x1, - y1: prevHipCoords.y1, - x2: prevHipCoords.x2, - y2: prevHipCoords.y2, - line: hipLine, - }) + /** 이전 라인과의 사이 추녀마루의 각도를 확인한다, 각도가 지붕안쪽으로 향하지 않을때 반대로 처리한다.*/ + const prevCheckPoint = { + x: Big(prevLine.x1).plus(Big(prevHalfVector.x).times(10)), + y: Big(prevLine.y1).plus(Big(prevHalfVector.y).times(10)), + } + + if (!checkWallPolygon.inPolygon(prevCheckPoint)) { + prevHipVector = { x: Big(prevHipVector.x).neg().toNumber(), y: Big(prevHipVector.y).neg().toNumber() } + } + + const prevEndPoint = { + x: Big(prevLine.x1).plus(Big(prevHipVector.x).times(hipLength)), + y: Big(prevLine.y1).plus(Big(prevHipVector.y).times(hipLength)), + } + + let ridgeEdge = { + vertex1: { x: currentMidX.toNumber(), y: currentMidY.toNumber() }, + vertex2: { + x: currentMidX.plus(Big(prevVectorX).times(prevBaseLine.size)).toNumber(), + y: currentMidY.plus(Big(prevVectorY).times(prevBaseLine.size)).toNumber(), + }, + } + let hipEdge = { vertex1: { x: prevLine.x1, y: prevLine.y1 }, vertex2: { x: prevEndPoint.x, y: prevEndPoint.y } } + let intersection = edgesIntersection(ridgeEdge, hipEdge) + if (intersection) { + prevHipCoords = { x1: prevLine.x1, y1: prevLine.y1, x2: intersection.x, y2: intersection.y } + prevOppositeMidY = Big(intersection.y) + prevOppositeMidX = Big(intersection.x) + } + } else { + if (vectorMidX === 0) { + prevOppositeMidY = currentMidY.plus(prevSize.plus(beforePrevOffset).times(vectorMidY)) + prevOppositeMidX = currentMidX + } else { + prevOppositeMidX = currentMidX.plus(prevSize.plus(beforePrevOffset).times(vectorMidX)) + prevOppositeMidY = currentMidY } } - oppositeMidY = prevOppositeMidY - oppositeMidX = prevOppositeMidX - } + const checkPrevSize = currentMidX.minus(prevOppositeMidX).pow(2).plus(currentMidY.minus(prevOppositeMidY).pow(2)).sqrt() + const checkNextSize = currentMidX.minus(nextOppositeMidX).pow(2).plus(currentMidY.minus(nextOppositeMidY).pow(2)).sqrt() + /** 두 포인트 중에 current와 가까운 포인트를 사용*/ + if (checkPrevSize.gt(checkNextSize)) { + if (nextHipCoords) { + let intersectPoints = [] + const hipEdge = { + vertex1: { x: nextHipCoords.x2, y: nextHipCoords.y2 }, + vertex2: { x: nextHipCoords.x1, y: nextHipCoords.y1 }, + } + + /** 외벽선에서 라인 겹치는 경우에 대한 확인*/ + roof.lines.forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(hipEdge, lineEdge) + if ( + intersection && + Math.sign(nextHipCoords.x2 - nextHipCoords.x1) === Math.sign(nextHipCoords.x2 - intersection.x) && + Math.sign(nextHipCoords.y2 - nextHipCoords.y1) === Math.sign(nextHipCoords.y2 - intersection.y) + ) { + const intersectEdge = { + vertex1: { x: nextHipCoords.x2, y: nextHipCoords.y2 }, + vertex2: { x: intersection.x, y: intersection.y }, + } + const is = edgesIntersection(intersectEdge, lineEdge) + if (!is.isIntersectionOutside) { + const intersectSize = Big(nextHipCoords.x2) + .minus(Big(intersection.x)) + .pow(2) + .plus(Big(nextHipCoords.y2).minus(Big(intersection.y)).pow(2)) + .abs() + .sqrt() + .toNumber() + + intersectPoints.push({ + intersection, + size: intersectSize, + line, + }) + } + } + }) + const intersect = intersectPoints.sort((a, b) => a.size - b.size)[0] + if (intersect) { + const degree = intersect.line.attributes.pitch > 0 ? getDegreeByChon(intersect.line.attributes.pitch) : intersect.line.attributes.degree + const hipLine = drawHipLine( + [intersect.intersection.x, intersect.intersection.y, nextOppositeMidX.toNumber(), nextOppositeMidY.toNumber()], + canvas, + roof, + textMode, + null, + degree, + degree, + ) + baseHipLines.push({ + x1: nextHipCoords.x1, + y1: nextHipCoords.y1, + x2: nextHipCoords.x2, + y2: nextHipCoords.y2, + line: hipLine, + }) + } + } + oppositeMidY = nextOppositeMidY + oppositeMidX = nextOppositeMidX + } else { + if (prevHipCoords) { + let intersectPoints = [] + const hipEdge = { + vertex1: { x: prevHipCoords.x2, y: prevHipCoords.y2 }, + vertex2: { x: prevHipCoords.x1, y: prevHipCoords.y1 }, + } + + /** 외벽선에서 라인 겹치는 경우에 대한 확인*/ + roof.lines.forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(hipEdge, lineEdge) + if ( + intersection && + Math.sign(prevHipCoords.x2 - prevHipCoords.x1) === Math.sign(prevHipCoords.x2 - intersection.x) && + Math.sign(prevHipCoords.y2 - prevHipCoords.y1) === Math.sign(prevHipCoords.y2 - intersection.y) + ) { + const intersectEdge = { + vertex1: { x: prevHipCoords.x2, y: prevHipCoords.y2 }, + vertex2: { x: intersection.x, y: intersection.y }, + } + const is = edgesIntersection(intersectEdge, lineEdge) + if (!is.isIntersectionOutside) { + const intersectSize = Big(prevHipCoords.x2) + .minus(Big(intersection.x)) + .pow(2) + .plus(Big(prevHipCoords.y2).minus(Big(intersection.y)).pow(2)) + .abs() + .sqrt() + .toNumber() + + intersectPoints.push({ + intersection, + size: intersectSize, + line, + }) + } + } + }) + const intersect = intersectPoints.sort((a, b) => a.size - b.size)[0] + + if (intersect) { + const degree = intersect.line.attributes.pitch > 0 ? getDegreeByChon(intersect.line.attributes.pitch) : intersect.line.attributes.degree + const hipLine = drawHipLine( + [intersect.intersection.x, intersect.intersection.y, prevOppositeMidX.toNumber(), prevOppositeMidY.toNumber()], + canvas, + roof, + textMode, + null, + degree, + degree, + ) + baseHipLines.push({ + x1: prevHipCoords.x1, + y1: prevHipCoords.y1, + x2: prevHipCoords.x2, + y2: prevHipCoords.y2, + line: hipLine, + }) + } + } + oppositeMidY = prevOppositeMidY + oppositeMidX = prevOppositeMidX + } + } if (baseRidgeCount < getMaxRidge(baseLines.length)) { /** 마루가 맞은편 외벽선에 닿는 경우 해당 부분까지로 한정한다. */ const ridgeEdge = { @@ -2415,9 +2466,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } else { if (currentAngle !== beforePrevAngle && currentAngle !== afterNextAngle && beforePrevLine !== afterNextLine) { console.log('4각 아님') - // console.log('currentLine : ', currentLine.attributes.planeSize) - // console.log('beforePrevLine : ', beforePrevLine) - // console.log('afterNextLine : ', afterNextLine) const beforePrevX1 = beforePrevLine.x1, beforePrevY1 = beforePrevLine.y1, beforePrevX2 = beforePrevLine.x2, @@ -2692,6 +2740,8 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { /** 겹치는 외벽선이 있을때 추녀마루를 외벽선 까지 늘려서 수정*/ if (intersectPoints && intersectPoints.intersection) { + console.log('prevDegree', prevDegree, 'currentDegree', currentDegree) + prevHipLine = drawHipLine( [intersectPoints.intersection.x, intersectPoints.intersection.y, prevEndPoint.x.toNumber(), prevEndPoint.y.toNumber()], canvas, @@ -2788,8 +2838,53 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } } - // console.log('prevHipLine : ', prevHipLine) - // console.log('nextHipLine : ', nextHipLine) + console.log('prevHipLine : ', prevHipLine) + console.log('nextHipLine : ', nextHipLine) + /** 두 선이 교차하면 해당 포인트까지로 선 조정*/ + if (prevHipLine !== undefined && nextHipLine !== undefined) { + const prevEdge = { vertex1: { x: prevHipLine.x1, y: prevHipLine.y1 }, vertex2: { x: prevHipLine.x2, y: prevHipLine.y2 } } + const nextEdge = { vertex1: { x: nextHipLine.x1, y: nextHipLine.y1 }, vertex2: { x: nextHipLine.x2, y: nextHipLine.y2 } } + const intersection = edgesIntersection(prevEdge, nextEdge) + console.log('intersection : ', intersection) + if (intersection) { + const checkPoint = new fabric.Circle({ + left: intersection.x, + top: intersection.y, + radius: 4, + fill: 'yellow', + parentId: roofId, + name: 'checkPoint', + }) + canvas.add(checkPoint) + canvas.renderAll() + + /** 포인트 조정*/ + baseHipLines + .filter((line) => line.line === prevHipLine || line.line === nextHipLine) + .forEach((line) => { + line.x2 = intersection.x + line.y2 = intersection.y + line.line.set({ x2: intersection.x, y2: intersection.y }) + }) + prevHipLine.x2 = intersection.x + prevHipLine.y2 = intersection.y + const prevSize = reCalculateSize(prevHipLine) + + prevHipLine.attributes.planeSize = prevSize.planeSize + prevHipLine.attributes.actualSize = prevSize.actualSize + prevHipLine.fire('modified') + + nextHipLine.x2 = intersection.x + nextHipLine.y2 = intersection.y + const nextSize = reCalculateSize(nextHipLine) + + nextHipLine.attributes.planeSize = nextSize.planeSize + nextHipLine.attributes.actualSize = nextSize.actualSize + nextHipLine.fire('modified') + canvas.renderAll() + } + } + /** 두 추녀마루가 한점에서 만나는 경우 해당 점을 기점으로 마루를 작성한다.*/ if ( prevHipLine !== undefined && @@ -2797,34 +2892,8 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { Big(prevHipLine.x2).minus(Big(nextHipLine.x2)).abs().lte(1) && Big(prevHipLine.y2).minus(Big(nextHipLine.y2)).abs().lte(1) ) { - console.log('마루 작성') const startPoint = { x: prevHipLine.x2, y: prevHipLine.y2 } - - // baseLines에서 가장 작은 x1과 가장 큰 x1, 가장 작은 y1과 가장 큰 y1을 계산 - let minX = Infinity - let maxX = -Infinity - let minY = Infinity - let maxY = -Infinity - - baseLines.forEach((line) => { - if (line.x1 < minX) { - minX = line.x1 - } - if (line.x1 > maxX) { - maxX = line.x1 - } - if (line.y1 < minY) { - minY = line.y1 - } - if (line.y1 > maxY) { - maxY = line.y1 - } - }) - const checkLength = Big(maxX) - .minus(Big(minX)) - .pow(2) - .plus(Big(maxY).minus(Big(minY)).pow(2)) - .sqrt() + let ridgeSize = 0 const currentMidX = Big(currentLine.x2).plus(Big(currentLine.x1)).div(2) const currentMidY = Big(currentLine.y2).plus(Big(currentLine.y1)).div(2) @@ -2832,130 +2901,192 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const xVector = Big(currentMidX).minus(Big(startPoint.x)).round(0, Big.roundDown) const yVector = Big(currentMidY).minus(Big(startPoint.y)).round(0, Big.roundDown) - const checkEdges = { - vertex1: { x: startPoint.x, y: startPoint.y }, - vertex2: { - x: Big(startPoint.x).minus(checkLength.times(Math.sign(xVector))), - y: Big(startPoint.y).minus(checkLength.times(Math.sign(yVector))), - }, - } - - const checkLine = new fabric.Line([checkEdges.vertex1.x, checkEdges.vertex1.y, checkEdges.vertex2.x, checkEdges.vertex2.y], { - stroke: 'purple', - strokeWidth: 2, - parentId: roof.id, - name: 'checkLine', - }) - canvas.add(checkLine) - canvas.renderAll() - - /** 맞은편 벽 까지의 길이 판단을 위한 교차되는 line*/ - const intersectBaseLine = [] - baseLines - .filter((line) => { - /** currentAngle 의 반대 각도인 라인 */ - const angle = calculateAngle(line.startPoint, line.endPoint) - switch (currentAngle) { - case 90: - return angle === -90 - case -90: - return angle === 90 - case 0: - return angle === 180 - case 180: - return angle === 0 - } - }) - .filter((line) => { - const currentMinX = Math.min(x1, x2) - const currentMaxX = Math.max(x1, x2) - const currentMinY = Math.min(y1, y2) - const currentMaxY = Math.max(y1, y2) - 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) - - /** currentLine 의 안쪽에 있거나 currentLine이 line의 안쪽에 있는 라인 */ - if (Big(currentLine.y1).minus(Big(currentLine.y2)).abs().lte(1)) { - return ( - (currentMinX <= lineMinX && lineMinX <= currentMaxX) || - (currentMinX <= lineMaxX && lineMaxX <= currentMaxX) || - (lineMinX <= currentMinX && currentMinX <= lineMaxX) || - (lineMinX <= currentMaxX && currentMaxX <= lineMaxX) - ) - } else { - return ( - (currentMinY <= lineMinY && lineMinY <= currentMaxY) || - (currentMinY <= lineMaxY && lineMaxY <= currentMaxY) || - (lineMinY <= currentMinY && currentMinY <= lineMaxY) || - (lineMinY <= currentMaxY && currentMaxY <= lineMaxY) - ) - } - }) - .forEach((line, index) => { - const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } - const intersection = edgesIntersection(checkEdges, lineEdge) + console.log('beforePrevLine', beforePrevLine) + if (gableType.includes(beforePrevLine.line.attributes.type) || gableType.includes(afterNextLine.line.attributes.type)) { + console.log('박공지붕 확인 후 마루 작성 ') + const oppositeLine = gableType.includes(beforePrevLine.line.attributes.type) ? beforePrevLine.line : afterNextLine.line + const oppositeAngle = calculateAngle(oppositeLine.startPoint, oppositeLine.endPoint) + console.log('currentAngle', currentAngle, 'oppositeAngle', oppositeAngle) + let checkEdge + if (Math.sign(oppositeLine.x1 - oppositeLine.x2) === 0) { + checkEdge = { vertex1: startPoint, vertex2: { x: oppositeLine.x1, y: startPoint.y } } + } else { + checkEdge = { vertex1: startPoint, vertex2: { x: startPoint.x, y: oppositeLine.y1 } } + } + if (currentAngle === oppositeAngle) { + const oppositeEdge = { vertex1: { x: oppositeLine.x1, y: oppositeLine.y1 }, vertex2: { x: oppositeLine.x2, y: oppositeLine.y2 } } + const intersection = edgesIntersection(oppositeEdge, checkEdge) + console.log('intersection', intersection) if (intersection) { - /* const intersectPoint = new fabric.Circle({ - left: intersection.x - 2, - top: intersection.y - 2, - radius: 4, - fill: 'green', - parentId: roof.id, + ridgeSize = Big(intersection.x) + .minus(Big(startPoint.x)) + .pow(2) + .plus(Big(intersection.y).minus(Big(startPoint.y)).pow(2)) + .sqrt() + } + console.log('aaa') + } else { + const intersectPoints = [] + roof.lines + .filter( + (line) => + Math.sign(oppositeLine.x1 - oppositeLine.x2) === Math.sign(line.x1 - line.x2) && + Math.sign(oppositeLine.y1 - oppositeLine.y2) === Math.sign(line.y1 - line.y2), + ) + .forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(lineEdge, checkEdge) + console.log('intersection', intersection) + if (intersection) { + const checkPoint = new fabric.Circle({ + left: intersection.x, + top: intersection.y, + radius: 4, + fill: 'red', + parentId: roofId, + name: 'checkPoint', + }) + canvas.add(checkPoint) + canvas.renderAll() + const size = Big(startPoint.x) + .minus(Big(intersection.x)) + .pow(2) + .plus(Big(startPoint.y).minus(Big(intersection.y)).pow(2)) + .sqrt() + .toNumber() + intersectPoints.push({ intersection, size }) + } }) - canvas.add(intersectPoint) - canvas.renderAll()*/ - intersectBaseLine.push({ intersection, line }) + intersectPoints.sort((a, b) => a.size - b.size) + console.log('intersectPoints', intersectPoints) + if (intersectPoints.length > 0) { + ridgeSize = Big(intersectPoints[0].size) + } + console.log('bbb') + } + } else { + console.log('마루 작성') + // baseLines에서 가장 작은 x1과 가장 큰 x1, 가장 작은 y1과 가장 큰 y1을 계산 + let minX = Infinity + let maxX = -Infinity + let minY = Infinity + let maxY = -Infinity + + baseLines.forEach((line) => { + if (line.x1 < minX) { + minX = line.x1 + } + if (line.x1 > maxX) { + maxX = line.x1 + } + if (line.y1 < minY) { + minY = line.y1 + } + if (line.y1 > maxY) { + maxY = line.y1 } }) - - // console.log('intersectBaseLine : ', intersectBaseLine) - - /** 맞은편 라인 */ - const oppositeLine = intersectBaseLine.reduce((prev, current) => { - const prevDistance = Big(prev.intersection.x) - .minus(Big(startPoint.x)) + const checkLength = Big(maxX) + .minus(Big(minX)) .pow(2) - .plus(Big(prev.intersection.y).minus(Big(startPoint.y)).pow(2)) + .plus(Big(maxY).minus(Big(minY)).pow(2)) .sqrt() - const currentDistance = Big(current.intersection.x) - .minus(Big(startPoint.x)) - .pow(2) - .plus(Big(current.intersection.y).minus(Big(startPoint.y)).pow(2)) - .sqrt() - return prevDistance < currentDistance ? prev : current - }, intersectBaseLine[0]) - console.log('oppositeLine : ', oppositeLine) + const checkEdges = { + vertex1: { x: startPoint.x, y: startPoint.y }, + vertex2: { + x: Big(startPoint.x).minus(checkLength.times(Math.sign(xVector))), + y: Big(startPoint.y).minus(checkLength.times(Math.sign(yVector))), + }, + } - /** 맞은편 라인까지의 길이 = 전체 길이 - 현재라인의 길이 */ - const oppositeSize = oppositeLine - ? Big(oppositeLine.intersection.x) + /** 맞은편 벽 까지의 길이 판단을 위한 교차되는 line*/ + const intersectBaseLine = [] + baseLines + .filter((line) => { + /** currentAngle 의 반대 각도인 라인 */ + const angle = calculateAngle(line.startPoint, line.endPoint) + switch (currentAngle) { + case 90: + return angle === -90 + case -90: + return angle === 90 + case 0: + return angle === 180 + case 180: + return angle === 0 + } + }) + .filter((line) => { + const currentMinX = Math.min(x1, x2) + const currentMaxX = Math.max(x1, x2) + const currentMinY = Math.min(y1, y2) + const currentMaxY = Math.max(y1, y2) + 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) + + /** currentLine 의 안쪽에 있거나 currentLine이 line의 안쪽에 있는 라인 */ + if (Big(currentLine.y1).minus(Big(currentLine.y2)).abs().lte(1)) { + return ( + (currentMinX <= lineMinX && lineMinX <= currentMaxX) || + (currentMinX <= lineMaxX && lineMaxX <= currentMaxX) || + (lineMinX <= currentMinX && currentMinX <= lineMaxX) || + (lineMinX <= currentMaxX && currentMaxX <= lineMaxX) + ) + } else { + return ( + (currentMinY <= lineMinY && lineMinY <= currentMaxY) || + (currentMinY <= lineMaxY && lineMaxY <= currentMaxY) || + (lineMinY <= currentMinY && currentMinY <= lineMaxY) || + (lineMinY <= currentMaxY && currentMaxY <= lineMaxY) + ) + } + }) + .forEach((line, index) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(checkEdges, lineEdge) + if (intersection) { + intersectBaseLine.push({ intersection, line }) + } + }) + + /** 맞은편 라인 */ + const oppositeLine = intersectBaseLine.reduce((prev, current) => { + const prevDistance = Big(prev.intersection.x) .minus(Big(startPoint.x)) .pow(2) - .plus(Big(oppositeLine.intersection.y).minus(Big(startPoint.y)).pow(2)) + .plus(Big(prev.intersection.y).minus(Big(startPoint.y)).pow(2)) .sqrt() - .minus(currentSize.div(2)) - .round(1) - : Infinity + const currentDistance = Big(current.intersection.x) + .minus(Big(startPoint.x)) + .pow(2) + .plus(Big(current.intersection.y).minus(Big(startPoint.y)).pow(2)) + .sqrt() + return prevDistance < currentDistance ? prev : current + }, intersectBaseLine[0]) - // console.log('startPoint : ', startPoint, 'currentSize : ', currentSize.toNumber()) + /** 맞은편 라인까지의 길이 = 전체 길이 - 현재라인의 길이 */ + const oppositeSize = oppositeLine + ? Big(oppositeLine.intersection.x) + .minus(Big(startPoint.x)) + .pow(2) + .plus(Big(oppositeLine.intersection.y).minus(Big(startPoint.y)).pow(2)) + .sqrt() + .minus(currentSize.div(2)) + .round(1) + : Infinity - /** 이전, 다음 라인중 길이가 짧은 길이*/ - /*const lineMinSize = - prevLine.attributes.planeSize < nextLine.attributes.planeSize - ? Big(prevLine.attributes.planeSize).div(10) - : Big(nextLine.attributes.planeSize).div(10)*/ - const lineMinSize = prevBaseLine.size < nextBaseLine.size ? Big(prevBaseLine.size).div(10) : Big(nextBaseLine.size).div(10) + /** 이전, 다음 라인중 길이가 짧은 길이*/ + const lineMinSize = prevBaseLine.size < nextBaseLine.size ? Big(prevBaseLine.size).div(10) : Big(nextBaseLine.size).div(10) - /** 마루의 길이는 이전 다음 라인중 짧은것의 길이와 현재라인부터 맞은편 라인까지의 길이에서 현재 라인의 길이를 뺀 것중 짧은 길이 */ - const ridgeSize = Big(Math.min(oppositeSize, lineMinSize)) - - // console.log('oppositeSize : ', oppositeSize, 'lineMinSize : ', lineMinSize.toNumber(), 'ridgeSize : ', ridgeSize.toNumber()) + /** 마루의 길이는 이전 다음 라인중 짧은것의 길이와 현재라인부터 맞은편 라인까지의 길이에서 현재 라인의 길이를 뺀 것중 짧은 길이 */ + ridgeSize = Big(Math.min(oppositeSize, lineMinSize)) + } + console.log('ridgeSize', ridgeSize) if (ridgeSize.gt(0) && baseRidgeCount < getMaxRidge(baseLines.length)) { - // console.log('baseRidgeCount : ', baseRidgeCount) const points = [ startPoint.x, startPoint.y, @@ -2981,16 +3112,12 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { return } - /* const checkMidLine = new fabric.Line(points, { - stroke: 'blue', - strokeWidth: 2, - parentId: roof.id, - }) - canvas.add(checkMidLine) - canvas.renderAll()*/ - // console.log('points : ', points) const ridgeLine = drawRidgeLine(points, canvas, roof, textMode) - baseRidgeLines.push(ridgeLine) + if (gableType.includes(beforePrevLine.line.attributes.type) || gableType.includes(afterNextLine.line.attributes.type)) { + baseGableRidgeLines.push(ridgeLine) + } else { + baseRidgeLines.push(ridgeLine) + } baseRidgeCount = baseRidgeCount + 1 /** 포인트 조정*/ @@ -2999,13 +3126,234 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { .forEach((line) => { line.x2 = points[0] line.y2 = points[1] + line.line.set({ x2: points[0], y2: points[1] }) + // line.line.fire('modified') }) prevHipLine.x2 = points[0] prevHipLine.y2 = points[1] + const prevSize = reCalculateSize(prevHipLine) + prevHipLine.attributes.planeSize = prevSize.planeSize + prevHipLine.attributes.actualSize = prevSize.actualSize + prevHipLine.fire('modified') nextHipLine.x2 = points[0] nextHipLine.y2 = points[1] - prevHipLine.fire('modified') + const nextSize = reCalculateSize(nextHipLine) + nextHipLine.attributes.planeSize = nextSize.planeSize + nextHipLine.attributes.actualSize = nextSize.actualSize nextHipLine.fire('modified') + canvas.renderAll() + + if (gableType.includes(beforePrevLine.line.attributes.type) || gableType.includes(afterNextLine.line.attributes.type)) { + const oppositeLine = gableType.includes(beforePrevLine.line.attributes.type) ? beforePrevLine.line : afterNextLine.line + const oppositeAngle = calculateAngle(oppositeLine.startPoint, oppositeLine.endPoint) + if (Math.sign(ridgeLine.x1 - ridgeLine.x2) === 0) { + const gableVector = Math.sign(ridgeLine.x1 - oppositeLine.x1) + const prevVector = ridgeLine.x1 === prevHipLine.x1 ? Math.sign(ridgeLine.x1 - prevHipLine.x2) : Math.sign(ridgeLine.x2 - prevHipLine.x1) + const nextVector = ridgeLine.x1 === nextHipLine.x1 ? Math.sign(ridgeLine.x1 - nextHipLine.x2) : Math.sign(ridgeLine.x2 - nextHipLine.x1) + + const firstHipLine = gableVector === prevVector ? prevHipLine : nextHipLine + const firstDegree = + gableVector === Math.sign(ridgeLine.x1 - prevLine.x1) + ? prevLine.attributes.pitch > 0 + ? getDegreeByChon(prevLine.attributes.pitch) + : prevLine.attributes.degree + : nextLine.attributes.pitch > 0 + ? getDegreeByChon(nextLine.attributes.pitch) + : nextLine.attributes.degree + + const oppositeRoofPoints = [ + ridgeLine.x2, + ridgeLine.y2, + ridgeLine.x1 === firstHipLine.x1 ? firstHipLine.x2 : firstHipLine.x1, + ridgeLine.y2, + ] + const oppositeRoofLine = drawHipLine(oppositeRoofPoints, canvas, roof, textMode, null, firstDegree, firstDegree) + baseHipLines.push({ + x1: oppositeRoofLine.x1, + y1: oppositeRoofLine.y1, + x2: oppositeRoofLine.x2, + y2: oppositeRoofLine.y2, + line: oppositeRoofLine, + }) + + const connectRoofPoints = [oppositeRoofLine.x2, oppositeRoofLine.y2] + if (ridgeLine.x1 === firstHipLine.x1) { + connectRoofPoints.push(firstHipLine.x2, firstHipLine.y2) + } else { + connectRoofPoints.push(firstHipLine.x1, firstHipLine.y1) + } + const connectRoofLine = drawRoofLine(connectRoofPoints, canvas, roof, textMode) + baseHipLines.push({ + x1: connectRoofLine.x1, + y1: connectRoofLine.y1, + x2: connectRoofLine.x2, + y2: connectRoofLine.y2, + line: connectRoofLine, + }) + + /** 다른 방향의 추녀마루 */ + const secondHipLine = gableVector === prevVector ? nextHipLine : prevHipLine + const secondDegree = + gableVector === Math.sign(ridgeLine.x1 - prevLine.x1) + ? nextLine.attributes.pitch > 0 + ? getDegreeByChon(nextLine.attributes.pitch) + : nextLine.attributes.degree + : prevLine.attributes.pitch > 0 + ? getDegreeByChon(prevLine.attributes.pitch) + : prevLine.attributes.degree + + const intersections = [] + const checkEdge = { + vertex1: { x: ridgeLine.x2, y: ridgeLine.y2 }, + vertex2: { x: ridgeLine.x1 === secondHipLine.x1 ? secondHipLine.x2 : secondHipLine.x1, y: ridgeLine.y2 }, + } + + baseGableRidgeLines + .filter((ridge) => ridge !== ridgeLine) + .forEach((ridge) => { + const ridgeEdge = { vertex1: { x: ridge.x1, y: ridge.y1 }, vertex2: { x: ridge.x2, y: ridge.y2 } } + const intersection = edgesIntersection(ridgeEdge, checkEdge) + if (intersection && !intersections.includes(intersection)) { + const size = Big(intersection.x) + .minus(Big(ridgeLine.x2)) + .pow(2) + .plus(Big(intersection.y).minus(Big(ridgeLine.y2)).pow(2)) + .sqrt() + .toNumber() + intersections.push({ intersection, size, ridge }) + } + }) + intersections.sort((a, b) => a.size - b.size) + + if (intersections.length > 0) { + const intersection = intersections[0].intersection + const intersectRidge = intersections[0].ridge + const oppositeRoofPoints = [ridgeLine.x2, ridgeLine.y2, intersection.x, intersection.y] + const oppositeRoofLine = drawHipLine(oppositeRoofPoints, canvas, roof, textMode, null, secondDegree, secondDegree) + baseHipLines.push({ x1: oppositeLine.x1, y1: oppositeLine.y1, x2: oppositeLine.x2, y2: oppositeLine.y2, line: oppositeRoofLine }) + + const ridgeVector = Math.sign(ridgeLine.y1 - ridgeLine.y2) + const connectRoofPoints = [oppositeRoofLine.x2, oppositeRoofLine.y2] + if (ridgeVector === Math.sign(oppositeRoofLine.y1 - intersectRidge.y2)) { + connectRoofPoints.push(intersectRidge.x2, intersectRidge.y2) + } else { + connectRoofPoints.push(intersectRidge.x1, intersectRidge.y1) + } + + const connectRoofLine = drawRoofLine(connectRoofPoints, canvas, roof, textMode) + baseHipLines.push({ + x1: connectRoofLine.x1, + y1: connectRoofLine.y1, + x2: connectRoofLine.x2, + y2: connectRoofLine.y2, + line: connectRoofLine, + }) + } + } else { + const gableVector = Math.sign(ridgeLine.y1 - oppositeLine.y1) + const prevVector = ridgeLine.y1 === prevHipLine.y1 ? Math.sign(ridgeLine.y1 - prevHipLine.y2) : Math.sign(ridgeLine.y1 - prevHipLine.y1) + const nextVector = ridgeLine.y1 === nextHipLine.y1 ? Math.sign(ridgeLine.y1 - nextHipLine.y2) : Math.sign(ridgeLine.y1 - nextHipLine.y1) + + /** 마루와 박공지붕을 연결하기위한 추녀마루 라인 */ + const firstHipLine = gableVector === prevVector ? prevHipLine : nextHipLine + const firstDegree = + gableVector === Math.sign(ridgeLine.y1 - prevLine.y1) + ? prevLine.attributes.pitch > 0 + ? getDegreeByChon(prevLine.attributes.pitch) + : prevLine.attributes.degree + : nextLine.attributes.pitch > 0 + ? getDegreeByChon(nextLine.attributes.pitch) + : nextLine.attributes.degree + + const oppositeRoofPoints = [ + ridgeLine.x2, + ridgeLine.y2, + ridgeLine.x2, + ridgeLine.y1 === firstHipLine.y1 ? firstHipLine.y2 : firstHipLine.y1, + ] + const oppositeRoofLine = drawHipLine(oppositeRoofPoints, canvas, roof, textMode, null, firstDegree, firstDegree) + baseHipLines.push({ + x1: oppositeRoofLine.x1, + y1: oppositeRoofLine.y1, + x2: oppositeRoofLine.x2, + y2: oppositeRoofLine.y2, + line: oppositeRoofLine, + }) + + const connectRoofPoints = [oppositeRoofLine.x2, oppositeRoofLine.y2] + if (ridgeLine.y1 === firstHipLine.y1) { + connectRoofPoints.push(firstHipLine.x2, firstHipLine.y2) + } else { + connectRoofPoints.push(firstHipLine.x1, firstHipLine.y1) + } + + const connectRoofLine = drawRoofLine(connectRoofPoints, canvas, roof, textMode) + baseHipLines.push({ + x1: connectRoofLine.x1, + y1: connectRoofLine.y1, + x2: connectRoofLine.x2, + y2: connectRoofLine.y2, + line: connectRoofLine, + }) + + /** 다른 방향의 추녀마루 */ + const secondHipLine = gableVector === prevVector ? nextHipLine : prevHipLine + const secondDegree = + gableVector === Math.sign(ridgeLine.y1 - prevLine.y1) + ? nextLine.attributes.pitch > 0 + ? getDegreeByChon(nextLine.attributes.pitch) + : nextLine.attributes.degree + : prevLine.attributes.pitch > 0 + ? getDegreeByChon(prevLine.attributes.pitch) + : prevLine.attributes.degree + + const intersections = [] + const checkEdge = { + vertex1: { x: ridgeLine.x2, y: ridgeLine.y2 }, + vertex2: { x: ridgeLine.x2, y: ridgeLine.y1 === secondHipLine.y1 ? secondHipLine.y2 : secondHipLine.y1 }, + } + baseGableRidgeLines + .filter((ridge) => ridge !== ridgeLine) + .forEach((ridge) => { + const ridgeEdge = { vertex1: { x: ridge.x1, y: ridge.y1 }, vertex2: { x: ridge.x2, y: ridge.y2 } } + const intersection = edgesIntersection(ridgeEdge, checkEdge) + if (intersection && !intersections.includes(intersection)) { + const size = Big(intersection.x) + .minus(Big(ridgeLine.x2)) + .pow(2) + .plus(Big(intersection.y).minus(Big(ridgeLine.y2)).pow(2)) + .sqrt() + .toNumber() + intersections.push({ intersection, size, ridge }) + } + }) + intersections.sort((a, b) => a.size - b.size) + if (intersections.length > 0) { + const intersection = intersections[0].intersection + const intersectRidge = intersections[0].ridge + const oppositeRoofPoints = [ridgeLine.x2, ridgeLine.y2, intersection.x, intersection.y] + const oppositeRoofLine = drawHipLine(oppositeRoofPoints, canvas, roof, textMode, null, secondDegree, secondDegree) + baseHipLines.push({ x1: oppositeLine.x1, y1: oppositeLine.y1, x2: oppositeLine.x2, y2: oppositeLine.y2, line: oppositeRoofLine }) + + const ridgeVector = Math.sign(ridgeLine.x1 - ridgeLine.x2) + const connectRoofPoints = [oppositeRoofLine.x2, oppositeRoofLine.y2] + if (ridgeVector === Math.sign(oppositeRoofLine.x1 - intersectRidge.x2)) { + connectRoofPoints.push(intersectRidge.x2, intersectRidge.y2) + } else { + connectRoofPoints.push(intersectRidge.x1, intersectRidge.y1) + } + + const connectRoofLine = drawRoofLine(connectRoofPoints, canvas, roof, textMode) + baseHipLines.push({ + x1: connectRoofLine.x1, + y1: connectRoofLine.y1, + x2: connectRoofLine.x2, + y2: connectRoofLine.y2, + line: connectRoofLine, + }) + } + } + } } } @@ -3941,6 +4289,7 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { /** hip이 짝수개가 맞다아있는데 마루와 연결되지 않는 포인트를 찾는다. 그려지지 않은 마루를 찾기 위함.*/ let noRidgeHipPoints = baseHipLines + .filter((current) => current.x1 !== current.x2 && current.y1 !== current.y2) .filter((current) => { const lines = baseHipLines .filter((line) => line !== current) @@ -3951,43 +4300,13 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { .filter( (current) => baseRidgeLines.filter((line) => (line.x1 === current.x2 && line.y1 === current.y2) || (line.x2 === current.x2 && line.y2 === current.y2)) + .length === 0 && + baseGableRidgeLines.filter((line) => (line.x1 === current.x2 && line.y1 === current.y2) || (line.x2 === current.x2 && line.y2 === current.y2)) .length === 0, ) noRidgeHipPoints.forEach((current) => { - /* const checkPoint = new fabric.Circle({ - left: current.x2, - top: current.y2, - radius: 4, - fill: 'green', - parentId: roofId, - name: 'checkPoint', - }) - canvas.add(checkPoint) - canvas.renderAll() - console.log('noRidgeHipPoints current : ', current.x1, current.y1, current.x2, current.y2)*/ const orthogonalPoints = noRidgeHipPoints.filter((point) => { - /*const checkPoint1 = new fabric.Circle({ - left: point.x2, - top: point.y2, - radius: 4, - fill: 'blue', - parentId: roofId, - name: 'checkPoint', - }) - canvas.add(checkPoint1) - canvas.renderAll() - console.log(' orthogonalPoints : ', point.x1, point.y1, point.x2, point.y2) - console.log( - 'same point :', - point !== current, - 'y point 다름', - current.x2 === point.x2 && current.y2 !== point.y2, - 'x point 다름', - current.x2 !== point.x2 && current.y2 === point.y2, - ) - canvas.remove(checkPoint1) - canvas.renderAll()*/ if (point !== current && ((current.x2 === point.x2 && current.y2 !== point.y2) || (current.x2 !== point.x2 && current.y2 === point.y2))) { return true } @@ -4002,8 +4321,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const points = [current.x2, current.y2, orthogonalPoints[0].x2, orthogonalPoints[0].y2] const ridgeLine = drawRidgeLine(points, canvas, roof, textMode) baseRidgeLines.push(ridgeLine) - /** array에서 object 삭제*/ - // noRidgeHipPoints = noRidgeHipPoints.filter((point) => point !== current && point !== orthogonalPoints[0]) } }) @@ -4097,8 +4414,18 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } }) + console.log('noRidgeHipPoints : ', noRidgeHipPoints) /** 마루를 그릴수 있는지 찾는다. */ noRidgeHipPoints.forEach((hipPoint) => { + const checkLine = new fabric.Line([hipPoint.x2, hipPoint.y2, hipPoint.x1, hipPoint.y1], { + stroke: 'red', + strokeWidth: 4, + parentId: roofId, + name: 'checkLine', + }) + canvas.add(checkLine) + canvas.renderAll() + const ridgePoints = [] intersectPoints .filter( @@ -4214,11 +4541,13 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { if (distanceX < distanceY) { linePoints.push({ intersection: intersectX, intersectPoint }) - } else { + } + if (distanceX > distanceY) { linePoints.push({ intersection: intersectY, intersectPoint }) } }) + console.log('linePoints : ', linePoints) const linePoint = linePoints.reduce((prev, current) => { const prevDistance = Big(prev.intersection.x) .minus(Big(hipPoint.x2)) @@ -4821,6 +5150,45 @@ const drawRidgeLine = (points, canvas, roof, textMode) => { return ridge } +/** + * 지붕선을 그린다. + * @param points + * @param canvas + * @param roof + * @param textMode + * @returns {*} + */ +const drawRoofLine = (points, canvas, roof, textMode) => { + const ridge = new QLine(points, { + parentId: roof.id, + fontSize: roof.fontSize, + stroke: '#1083E3', + strokeWidth: 2, + name: LINE_TYPE.SUBLINE.HIP, + textMode: textMode, + attributes: { + roofId: roof.id, + planeSize: calcLinePlaneSize({ + x1: points[0], + y1: points[1], + x2: points[2], + y2: points[3], + }), + actualSize: calcLinePlaneSize({ + x1: points[0], + y1: points[1], + x2: points[2], + y2: points[3], + }), + }, + }) + canvas.add(ridge) + ridge.bringToFront() + canvas.renderAll() + + return ridge +} + /** * 벡터를 정규화(Normalization)하는 함수 * @param v @@ -7685,3 +8053,31 @@ const getSortedPoint = (points) => { } return sortedPoints } + +const reCalculateSize = (line) => { + const oldPlaneSize = line.attributes.planeSize + const oldActualSize = line.attributes.actualSize + const theta = Big(Math.acos(Big(oldPlaneSize).div(oldActualSize))) + .times(180) + .div(Math.PI) + console.log('theta : ', theta.toNumber()) + const planeSize = calcLinePlaneSize({ + x1: line.x1, + y1: line.y1, + x2: line.x2, + y2: line.y2, + }) + const actualSize = + planeSize === oldActualSize + ? 0 + : calcLineActualSize( + { + x1: line.x1, + y1: line.y1, + x2: line.x2, + y2: line.y2, + }, + theta, + ) + return { planeSize, actualSize } +} From 4b120326db5aa317f6d3853ae959dc20dba9e6e9 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Wed, 14 May 2025 10:30:08 +0900 Subject: [PATCH 39/46] =?UTF-8?q?length=EA=B0=80=201=20=EB=AF=B8=EB=A7=8C?= =?UTF-8?q?=EC=9D=B8=20=EA=B2=BD=EC=9A=B0=20actualSize=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=ED=95=A0=20=ED=95=84=EC=9A=94=20=EC=97=86=EC=9D=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/roofcover/useRoofAllocationSetting.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/roofcover/useRoofAllocationSetting.js b/src/hooks/roofcover/useRoofAllocationSetting.js index f8ad7d30..79510bc1 100644 --- a/src/hooks/roofcover/useRoofAllocationSetting.js +++ b/src/hooks/roofcover/useRoofAllocationSetting.js @@ -356,7 +356,7 @@ export function useRoofAllocationSetting(id) { roofBases.forEach((roof) => { if (roof.separatePolygon.length === 0) { roof.innerLines.forEach((line) => { - if (!line.attributes.actualSize || line.attributes?.actualSize === 0) { + if ((!line.attributes.actualSize || line.attributes?.actualSize === 0) && line.length > 1) { line.set({ strokeWidth: 4, stroke: 'black', selectable: true }) result = true } From e8457da05f82ab30e1f20858e5f2555337a8fc64 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Wed, 14 May 2025 10:50:04 +0900 Subject: [PATCH 40/46] =?UTF-8?q?=EA=B1=B0=EB=A6=AC=200=EC=9D=B4=20?= =?UTF-8?q?=EC=95=84=EB=8B=8C=201=20=EB=AF=B8=EB=A7=8C=EC=9C=BC=EB=A1=9C?= =?UTF-8?q?=20=EB=B2=94=EC=9C=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/roofcover/useAuxiliaryDrawing.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/roofcover/useAuxiliaryDrawing.js b/src/hooks/roofcover/useAuxiliaryDrawing.js index 82c47447..d66014f8 100644 --- a/src/hooks/roofcover/useAuxiliaryDrawing.js +++ b/src/hooks/roofcover/useAuxiliaryDrawing.js @@ -722,7 +722,7 @@ export function useAuxiliaryDrawing(id, isUseEffect = true) { const distance1 = distanceBetweenPoints({ x: line1.x1, y: line1.y1 }, intersectionPoint) const distance2 = distanceBetweenPoints({ x: line1.x2, y: line1.y2 }, intersectionPoint) - if (distance1 === 0 || distance2 === 0) { + if (distance1 < 1 || distance2 < 1) { return } //historyLine에서 기존 line을 제거한다. From a8c57b1c53e886345c084485dbc12093cc75be5e Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Wed, 14 May 2025 10:59:29 +0900 Subject: [PATCH 41/46] =?UTF-8?q?=EC=A7=80=EB=B6=95=EC=9E=AC=20=ED=95=A0?= =?UTF-8?q?=EB=8B=B9=ED=9B=84=20=EC=B2=98=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/roofcover/useRoofAllocationSetting.js | 1 + src/hooks/usePolygon.js | 9 +++++++++ src/util/canvas-util.js | 2 -- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/hooks/roofcover/useRoofAllocationSetting.js b/src/hooks/roofcover/useRoofAllocationSetting.js index 79510bc1..fee893a6 100644 --- a/src/hooks/roofcover/useRoofAllocationSetting.js +++ b/src/hooks/roofcover/useRoofAllocationSetting.js @@ -383,6 +383,7 @@ export function useRoofAllocationSetting(id) { } } catch (e) { console.log(e) + canvas.discardActiveObject() return } diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js index 7e2e28ee..577274ad 100644 --- a/src/hooks/usePolygon.js +++ b/src/hooks/usePolygon.js @@ -1168,6 +1168,15 @@ export const usePolygon = () => { canvas.remove(polygon) canvas.renderAll() }) + + //지붕 완료 후 보조선을 전부 제거한다. + const auxiliaryLines = canvas.getObjects().filter((obj) => obj.name === 'auxiliaryLine') + + auxiliaryLines.forEach((line) => { + canvas.remove(line) + }) + canvas.renderAll() + canvas.discardActiveObject() } const getSplitRoofsPoints = (allLines) => { diff --git a/src/util/canvas-util.js b/src/util/canvas-util.js index 1d067ad8..c7a1fc17 100644 --- a/src/util/canvas-util.js +++ b/src/util/canvas-util.js @@ -549,8 +549,6 @@ export function isPointOnLine({ x1, y1, x2, y2 }, { x, y }, epsilon = 2) { withinYRange = withinYRange && 2 <= Math.max(y1, y2) - y } - console.log(Math.min(x1, x2) - x, Math.max(x1, x2) - x) - return withinXRange && withinYRange } /** From 457253f0f6d617c245d7a0b8cd7faa439f210ad3 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Wed, 14 May 2025 14:41:23 +0900 Subject: [PATCH 42/46] =?UTF-8?q?=EB=B2=94=EC=9C=84=EB=A1=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/roofcover/useAuxiliaryDrawing.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/roofcover/useAuxiliaryDrawing.js b/src/hooks/roofcover/useAuxiliaryDrawing.js index d66014f8..78d0b883 100644 --- a/src/hooks/roofcover/useAuxiliaryDrawing.js +++ b/src/hooks/roofcover/useAuxiliaryDrawing.js @@ -675,7 +675,7 @@ export function useAuxiliaryDrawing(id, isUseEffect = true) { const distance1 = distanceBetweenPoints({ x: line1.x1, y: line1.y1 }, interSectionPointsWithRoofLines[0]) const distance2 = distanceBetweenPoints({ x: line1.x2, y: line1.y2 }, interSectionPointsWithRoofLines[0]) - if (!(distance1 === 0 || distance2 === 0)) { + if (!(distance1 < 1 || distance2 < 1)) { if (distance1 >= distance2) { const newLine = addLine([line1.x1, line1.y1, interSectionPointsWithRoofLines[0].x, interSectionPointsWithRoofLines[0].y], { stroke: 'black', From 410da6f541032f14b63a8330b755c13bee4b5d65 Mon Sep 17 00:00:00 2001 From: Jaeyoung Lee Date: Thu, 15 May 2025 16:00:40 +0900 Subject: [PATCH 43/46] =?UTF-8?q?=EC=A7=80=EB=B6=95=20=EB=B3=B4=EC=A1=B0?= =?UTF-8?q?=EC=84=A0=20=EC=9E=91=EC=84=B1=20=EC=88=98=EC=A0=95=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/qpolygon-utils.js | 563 +++++++++++++++++++++++++++---------- 1 file changed, 413 insertions(+), 150 deletions(-) diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index 3082e682..bf5c8b97 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -879,6 +879,7 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const currentLine = currentBaseLine.line const prevLine = prevBaseLine.line const nextLine = nextBaseLine.line + const currentAngle = calculateAngle(currentLine.startPoint, currentLine.endPoint) const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint) const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint) @@ -918,7 +919,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { drawEavesSecondLines.push({ currentBaseLine, prevBaseLine, nextBaseLine }) } } else if (gableType.includes(nextLine.attributes?.type) && gableType.includes(prevLine.attributes?.type)) { - console.log('currentLine :', currentBaseLine.size, 'prevAngle :', prevAngle, 'nextAngle :', nextAngle) if (Big(prevAngle).minus(Big(nextAngle)).abs().eq(180)) { const checkPoints = { x: currentMidX.plus(checkScale.times(Math.sign(xVector.toNumber()))).toNumber(), @@ -934,7 +934,9 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { drawGableRidgeSecond.push({ currentBaseLine, prevBaseLine, nextBaseLine }) // } } else { - drawGablePolygonSecond.push({ currentBaseLine, prevBaseLine, nextBaseLine }) + if (currentAngle !== prevAngle && currentAngle !== nextAngle) { + drawGablePolygonSecond.push({ currentBaseLine, prevBaseLine, nextBaseLine }) + } } } } @@ -1034,44 +1036,158 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const nextVectorY = Math.sign(Big(nextLine.y2).minus(Big(nextLine.y1))) /** 현재라인의 기준점*/ - const currentMidX = Big(x1).plus(Big(x2)).div(2).plus(Big(prevVectorX).times(currentLine.attributes.offset)) - const currentMidY = Big(y1).plus(Big(y2)).div(2).plus(Big(prevVectorY).times(currentLine.attributes.offset)) + let currentMidX = Big(x1).plus(Big(x2)).div(2).plus(Big(prevVectorX).times(currentLine.attributes.offset)) + let currentMidY = Big(y1).plus(Big(y2)).div(2).plus(Big(prevVectorY).times(currentLine.attributes.offset)) /** 마루 반대 좌표*/ let oppositeMidX = currentMidX, oppositeMidY = currentMidY + /** 한개의 지붕선을 둘로 나누어서 처리 하는 경우 */ if (prevAngle === beforePrevAngle || nextAngle === afterNextAngle) { - console.log('지붕선 분할 : start') - const checkLine1 = new fabric.Line([prevLine.x1, prevLine.y1, prevLine.x2, prevLine.y2], { - stroke: 'red', - strokeWidth: 4, - parentId: roofId, - name: 'checkLine', - }) - const checkLine2 = new fabric.Line([nextLine.x1, nextLine.y1, nextLine.x2, nextLine.y2], { - stroke: 'blue', - strokeWidth: 4, - parentId: roofId, - name: 'checkLine', - }) - const checkPoint = new fabric.Circle({ - left: currentMidX.toNumber(), - top: currentMidY.toNumber(), - radius: 5, - fill: 'red', - parentId: roofId, - name: 'checkPoint', - }) - canvas.add(checkLine1) - canvas.add(checkLine2) - canvas.add(checkPoint) - canvas.renderAll() - if (currentVectorX === 0) { + const addLength = Big(currentLine.y1).minus(Big(currentLine.y2)).abs().div(2) + const ridgeVector = Math.sign(prevLine.x1 - currentLine.x1) + oppositeMidX = Big(prevLine.x1).plus(Big(addLength).times(ridgeVector)) + + const ridgeEdge = { + vertex1: { x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() }, + vertex2: { x: currentMidX.toNumber(), y: currentMidY.toNumber() }, + } + + const ridgeVectorX = Math.sign(ridgeEdge.vertex1.x - ridgeEdge.vertex2.x) + roof.lines + .filter((line) => line.x1 === line.x2) + .forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const is = edgesIntersection(ridgeEdge, lineEdge) + if (is) { + const isVectorX = Math.sign(ridgeEdge.vertex1.x - is.x) + if ( + isVectorX === ridgeVectorX && + ((line.x1 <= currentMidX && line.x2 >= currentMidX) || (line.x2 <= currentMidX && line.x1 >= currentMidX)) + ) { + currentMidX = Big(is.x) + currentMidY = Big(is.y) + } + } + }) } else { + const addLength = Big(currentLine.x1).minus(Big(currentLine.x2)).abs().div(2) + const ridgeVector = Math.sign(prevLine.y1 - currentLine.y1) + oppositeMidY = Big(prevLine.y1).plus(addLength.times(ridgeVector)) + + const ridgeEdge = { + vertex1: { x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() }, + vertex2: { x: currentMidX.toNumber(), y: currentMidY.toNumber() }, + } + const ridgeVectorY = Math.sign(ridgeEdge.vertex1.y - ridgeEdge.vertex2.y) + roof.lines + .filter((line) => line.y1 === line.y2) + .forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const is = edgesIntersection(ridgeEdge, lineEdge) + if (is) { + const isVectorY = Math.sign(ridgeEdge.vertex1.y - is.y) + if ( + isVectorY === ridgeVectorY && + ((line.x1 <= currentMidX && line.x2 >= currentMidX) || (line.x2 <= currentMidX && line.x1 >= currentMidX)) + ) { + currentMidX = Big(is.x) + currentMidY = Big(is.y) + } + } + }) } + const prevHipEdge = { vertex1: { x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() }, vertex2: { x: prevLine.x1, y: prevLine.y1 } } + const prevHipVectorX = Math.sign(prevHipEdge.vertex1.x - prevHipEdge.vertex2.x) + const prevHipVectorY = Math.sign(prevHipEdge.vertex1.y - prevHipEdge.vertex2.y) + const prevIsPoints = [] + roof.lines + .filter((line) => (Math.sign(prevLine.x1 - prevLine.x2) === 0 ? line.x1 === line.x2 : line.y1 === line.y2)) + .forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const is = edgesIntersection(prevHipEdge, lineEdge) + if (is) { + const isVectorX = Math.sign(prevHipEdge.vertex1.x - is.x) + const isVectorY = Math.sign(prevHipEdge.vertex1.y - is.y) + if (isVectorX === prevHipVectorX && isVectorY === prevHipVectorY) { + const size = Big(prevHipEdge.vertex1.x) + .minus(Big(is.x)) + .abs() + .pow(2) + .plus(Big(prevHipEdge.vertex1.y).minus(Big(is.y)).abs().pow(2)) + .sqrt() + .toNumber() + prevIsPoints.push({ is, size }) + } + } + }) + + if (prevIsPoints.length > 0) { + const prevIs = prevIsPoints.sort((a, b) => a.size - b.size)[0].is + const prevHipLine = drawHipLine( + [prevIs.x, prevIs.y, oppositeMidX.toNumber(), oppositeMidY.toNumber()], + canvas, + roof, + textMode, + null, + prevDegree, + prevDegree, + ) + baseHipLines.push({ x1: prevLine.x1, y1: prevLine.y1, x2: oppositeMidX.toNumber(), y2: oppositeMidY.toNumber(), line: prevHipLine }) + } + + const nextHipEdge = { vertex1: { x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() }, vertex2: { x: nextLine.x2, y: nextLine.y2 } } + const nextHipVectorX = Math.sign(nextHipEdge.vertex1.x - nextHipEdge.vertex2.x) + const nextHipVectorY = Math.sign(nextHipEdge.vertex1.y - nextHipEdge.vertex2.y) + const nextIsPoints = [] + + roof.lines + .filter((line) => (Math.sign(nextLine.x1 - nextLine.x2) === 0 ? line.x1 === line.x2 : line.y1 === line.y2)) + .forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const is = edgesIntersection(nextHipEdge, lineEdge) + if (is) { + const isVectorX = Math.sign(nextHipEdge.vertex1.x - is.x) + const isVectorY = Math.sign(nextHipEdge.vertex1.y - is.y) + if (isVectorX === nextHipVectorX && isVectorY === nextHipVectorY) { + const size = Big(nextHipEdge.vertex1.x) + .minus(Big(is.x)) + .abs() + .pow(2) + .plus(Big(nextHipEdge.vertex1.y).minus(Big(is.y)).abs().pow(2)) + .sqrt() + .toNumber() + nextIsPoints.push({ is, size }) + } + } + }) + if (nextIsPoints.length > 0) { + const nextIs = nextIsPoints.sort((a, b) => a.size - b.size)[0].is + const nextHipLine = drawHipLine( + [nextIs.x, nextIs.y, oppositeMidX.toNumber(), oppositeMidY.toNumber()], + canvas, + roof, + textMode, + null, + nextDegree, + nextDegree, + ) + baseHipLines.push({ x1: nextLine.x2, y1: nextLine.y2, x2: oppositeMidX.toNumber(), y2: oppositeMidY.toNumber(), line: nextHipLine }) + } + + if (baseRidgeCount < getMaxRidge(baseLines.length)) { + const ridgeLine = drawRidgeLine( + [currentMidX.toNumber(), currentMidY.toNumber(), oppositeMidX.toNumber(), oppositeMidY.toNumber()], + canvas, + roof, + textMode, + ) + baseGableRidgeLines.push(ridgeLine) + baseRidgeCount++ + } console.log('지붕선 분할 : end') } else { if (beforePrevBaseLine === afterNextBaseLine) { @@ -1236,7 +1352,10 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { y: currentMidY.plus(Big(nextVectorY).times(nextBaseLine.size)).toNumber(), }, } - let hipEdge = { vertex1: { x: nextLine.x2, y: nextLine.y2 }, vertex2: { x: nextEndPoint.x, y: nextEndPoint.y } } + let hipEdge = { + vertex1: { x: nextLine.x2, y: nextLine.y2 }, + vertex2: { x: nextEndPoint.x, y: nextEndPoint.y }, + } let intersection = edgesIntersection(ridgeEdge, hipEdge) if (intersection) { nextHipCoords = { x1: nextLine.x2, y1: nextLine.y2, x2: intersection.x, y2: intersection.y } @@ -1283,7 +1402,10 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { y: currentMidY.plus(Big(prevVectorY).times(prevBaseLine.size)).toNumber(), }, } - let hipEdge = { vertex1: { x: prevLine.x1, y: prevLine.y1 }, vertex2: { x: prevEndPoint.x, y: prevEndPoint.y } } + let hipEdge = { + vertex1: { x: prevLine.x1, y: prevLine.y1 }, + vertex2: { x: prevEndPoint.x, y: prevEndPoint.y }, + } let intersection = edgesIntersection(ridgeEdge, hipEdge) if (intersection) { prevHipCoords = { x1: prevLine.x1, y1: prevLine.y1, x2: intersection.x, y2: intersection.y } @@ -1299,135 +1421,268 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { prevOppositeMidY = currentMidY } } - const checkPrevSize = currentMidX.minus(prevOppositeMidX).pow(2).plus(currentMidY.minus(prevOppositeMidY).pow(2)).sqrt() - const checkNextSize = currentMidX.minus(nextOppositeMidX).pow(2).plus(currentMidY.minus(nextOppositeMidY).pow(2)).sqrt() - /** 두 포인트 중에 current와 가까운 포인트를 사용*/ - if (checkPrevSize.gt(checkNextSize)) { - if (nextHipCoords) { - let intersectPoints = [] - const hipEdge = { - vertex1: { x: nextHipCoords.x2, y: nextHipCoords.y2 }, - vertex2: { x: nextHipCoords.x1, y: nextHipCoords.y1 }, - } + const currentMidEdge = { + vertex1: { x: currentMidX.toNumber(), y: currentMidY.toNumber() }, + vertex2: { + x: currentVectorX === 0 ? nextLine.x2 : currentMidX.toNumber(), + y: currentVectorX === 0 ? currentMidY.toNumber() : nextLine.y2, + }, + } - /** 외벽선에서 라인 겹치는 경우에 대한 확인*/ - roof.lines.forEach((line) => { - const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } - const intersection = edgesIntersection(hipEdge, lineEdge) - if ( - intersection && - Math.sign(nextHipCoords.x2 - nextHipCoords.x1) === Math.sign(nextHipCoords.x2 - intersection.x) && - Math.sign(nextHipCoords.y2 - nextHipCoords.y1) === Math.sign(nextHipCoords.y2 - intersection.y) - ) { - const intersectEdge = { - vertex1: { x: nextHipCoords.x2, y: nextHipCoords.y2 }, - vertex2: { x: intersection.x, y: intersection.y }, - } - const is = edgesIntersection(intersectEdge, lineEdge) - if (!is.isIntersectionOutside) { - const intersectSize = Big(nextHipCoords.x2) - .minus(Big(intersection.x)) - .pow(2) - .plus(Big(nextHipCoords.y2).minus(Big(intersection.y)).pow(2)) - .abs() - .sqrt() - .toNumber() - - intersectPoints.push({ - intersection, - size: intersectSize, - line, - }) + let oppositeLines = [] + drawBaseLines + .filter((line, index) => { + const currentLine = line.line + const nextLine = drawBaseLines[(index + 1) % drawBaseLines.length].line + const prevLine = drawBaseLines[(index - 1 + drawBaseLines.length) % drawBaseLines.length].line + console.log('line.startPoint :', currentLine.startPoint, 'line.endPoint :', currentLine.endPoint) + const angle = calculateAngle(currentLine.startPoint, currentLine.endPoint) + const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint) + const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint) + if (angle === prevAngle || angle === nextAngle) { + const sameAngleLine = angle === prevAngle ? prevLine : nextLine + if (gableType.includes(currentLine.attributes.type) && !gableType.includes(sameAngleLine.attributes.type)) { + switch (currentAngle) { + case 90: + return angle === -90 + case -90: + return angle === 90 + case 0: + return angle === 180 + case 180: + return angle === 0 } } - }) - const intersect = intersectPoints.sort((a, b) => a.size - b.size)[0] - if (intersect) { - const degree = intersect.line.attributes.pitch > 0 ? getDegreeByChon(intersect.line.attributes.pitch) : intersect.line.attributes.degree - const hipLine = drawHipLine( - [intersect.intersection.x, intersect.intersection.y, nextOppositeMidX.toNumber(), nextOppositeMidY.toNumber()], - canvas, - roof, - textMode, - null, - degree, - degree, - ) - baseHipLines.push({ - x1: nextHipCoords.x1, - y1: nextHipCoords.y1, - x2: nextHipCoords.x2, - y2: nextHipCoords.y2, - line: hipLine, - }) } - } - oppositeMidY = nextOppositeMidY - oppositeMidX = nextOppositeMidX + return false + }) + .forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(currentMidEdge, lineEdge) + if (intersection) { + if (line.x1 <= intersection.x && line.x2 >= intersection.x && line.y1 <= intersection.y && line.y2 >= intersection.y) { + oppositeLines.push({ + line, + intersection, + size: Big(intersection.x) + .minus(currentMidX) + .abs() + .pow(2) + .plus(Big(intersection.y).minus(currentMidY).abs().pow(2)) + .sqrt() + .toNumber(), + }) + } + } + }) + + if (oppositeLines.length > 0) { + const oppositePoint = oppositeLines.sort((a, b) => a.size - b.size)[0].intersection + const checkEdge = { vertex1: { x: currentMidX.toNumber(), y: currentMidY.toNumber() }, vertex2: { x: oppositePoint.x, y: oppositePoint.y } } + const oppositeRoofPoints = [] + roof.lines + .filter((line) => { + const angle = calculateAngle(line.startPoint, line.endPoint) + switch (currentAngle) { + case 90: + return angle === -90 + case -90: + return angle === 90 + case 0: + return angle === 180 + case 180: + return angle === 0 + } + }) + .forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(checkEdge, lineEdge) + if ( + intersection && + ((line.x1 <= intersection.x && line.x2 >= intersection.x && line.y1 <= intersection.y && line.y2 >= intersection.y) || + (line.x1 >= intersection.x && line.x2 <= intersection.x && line.y1 >= intersection.y && line.y2 <= intersection.y)) + ) { + oppositeRoofPoints.push({ + line, + intersection, + size: Big(intersection.x) + .minus(currentMidX.toNumber()) + .abs() + .pow(2) + .plus(Big(intersection.y).minus(currentMidY.toNumber()).abs().pow(2)) + .sqrt() + .toNumber(), + }) + } + }) + const oppositeRoofPoint = oppositeRoofPoints.sort((a, b) => a.size - b.size)[0].intersection + oppositeMidX = Big(oppositeRoofPoint.x) + oppositeMidY = Big(oppositeRoofPoint.y) + + const currentRoofPoints = [] + roof.lines + .filter((line) => { + const angle = calculateAngle(line.startPoint, line.endPoint) + return currentAngle === angle + }) + .forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(checkEdge, lineEdge) + if ( + intersection && + ((line.x1 <= intersection.x && line.x2 >= intersection.x && line.y1 <= intersection.y && line.y2 >= intersection.y) || + (line.x1 >= intersection.x && line.x2 <= intersection.x && line.y1 >= intersection.y && line.y2 <= intersection.y)) + ) { + currentRoofPoints.push({ + line, + intersection, + size: Big(intersection.x) + .minus(currentMidX.toNumber()) + .abs() + .pow(2) + .plus(Big(intersection.y).minus(currentMidY.toNumber()).abs().pow(2)) + .sqrt() + .toNumber(), + }) + } + }) + const currentRoofPoint = currentRoofPoints.sort((a, b) => a.size - b.size)[0].intersection + currentMidX = Big(currentRoofPoint.x) + currentMidY = Big(currentRoofPoint.y) } else { - if (prevHipCoords) { - let intersectPoints = [] - const hipEdge = { - vertex1: { x: prevHipCoords.x2, y: prevHipCoords.y2 }, - vertex2: { x: prevHipCoords.x1, y: prevHipCoords.y1 }, - } + const checkPrevSize = currentMidX.minus(prevOppositeMidX).pow(2).plus(currentMidY.minus(prevOppositeMidY).pow(2)).sqrt() + const checkNextSize = currentMidX.minus(nextOppositeMidX).pow(2).plus(currentMidY.minus(nextOppositeMidY).pow(2)).sqrt() - /** 외벽선에서 라인 겹치는 경우에 대한 확인*/ - roof.lines.forEach((line) => { - const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } - const intersection = edgesIntersection(hipEdge, lineEdge) - if ( - intersection && - Math.sign(prevHipCoords.x2 - prevHipCoords.x1) === Math.sign(prevHipCoords.x2 - intersection.x) && - Math.sign(prevHipCoords.y2 - prevHipCoords.y1) === Math.sign(prevHipCoords.y2 - intersection.y) - ) { - const intersectEdge = { - vertex1: { x: prevHipCoords.x2, y: prevHipCoords.y2 }, - vertex2: { x: intersection.x, y: intersection.y }, - } - const is = edgesIntersection(intersectEdge, lineEdge) - if (!is.isIntersectionOutside) { - const intersectSize = Big(prevHipCoords.x2) - .minus(Big(intersection.x)) - .pow(2) - .plus(Big(prevHipCoords.y2).minus(Big(intersection.y)).pow(2)) - .abs() - .sqrt() - .toNumber() - - intersectPoints.push({ - intersection, - size: intersectSize, - line, - }) - } + /** 두 포인트 중에 current와 가까운 포인트를 사용*/ + if (checkPrevSize.gt(checkNextSize)) { + if (nextHipCoords) { + let intersectPoints = [] + const hipEdge = { + vertex1: { x: nextHipCoords.x2, y: nextHipCoords.y2 }, + vertex2: { x: nextHipCoords.x1, y: nextHipCoords.y1 }, } - }) - const intersect = intersectPoints.sort((a, b) => a.size - b.size)[0] - if (intersect) { - const degree = intersect.line.attributes.pitch > 0 ? getDegreeByChon(intersect.line.attributes.pitch) : intersect.line.attributes.degree - const hipLine = drawHipLine( - [intersect.intersection.x, intersect.intersection.y, prevOppositeMidX.toNumber(), prevOppositeMidY.toNumber()], - canvas, - roof, - textMode, - null, - degree, - degree, - ) - baseHipLines.push({ - x1: prevHipCoords.x1, - y1: prevHipCoords.y1, - x2: prevHipCoords.x2, - y2: prevHipCoords.y2, - line: hipLine, + /** 외벽선에서 라인 겹치는 경우에 대한 확인*/ + roof.lines.forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(hipEdge, lineEdge) + if ( + intersection && + Math.sign(nextHipCoords.x2 - nextHipCoords.x1) === Math.sign(nextHipCoords.x2 - intersection.x) && + Math.sign(nextHipCoords.y2 - nextHipCoords.y1) === Math.sign(nextHipCoords.y2 - intersection.y) + ) { + const intersectEdge = { + vertex1: { x: nextHipCoords.x2, y: nextHipCoords.y2 }, + vertex2: { x: intersection.x, y: intersection.y }, + } + const is = edgesIntersection(intersectEdge, lineEdge) + if (!is.isIntersectionOutside) { + const intersectSize = Big(nextHipCoords.x2) + .minus(Big(intersection.x)) + .pow(2) + .plus(Big(nextHipCoords.y2).minus(Big(intersection.y)).pow(2)) + .abs() + .sqrt() + .toNumber() + + intersectPoints.push({ + intersection, + size: intersectSize, + line, + }) + } + } }) + const intersect = intersectPoints.sort((a, b) => a.size - b.size)[0] + if (intersect) { + const degree = + intersect.line.attributes.pitch > 0 ? getDegreeByChon(intersect.line.attributes.pitch) : intersect.line.attributes.degree + const hipLine = drawHipLine( + [intersect.intersection.x, intersect.intersection.y, nextOppositeMidX.toNumber(), nextOppositeMidY.toNumber()], + canvas, + roof, + textMode, + null, + degree, + degree, + ) + baseHipLines.push({ + x1: nextHipCoords.x1, + y1: nextHipCoords.y1, + x2: nextHipCoords.x2, + y2: nextHipCoords.y2, + line: hipLine, + }) + } } + oppositeMidY = nextOppositeMidY + oppositeMidX = nextOppositeMidX + } else { + if (prevHipCoords) { + let intersectPoints = [] + const hipEdge = { + vertex1: { x: prevHipCoords.x2, y: prevHipCoords.y2 }, + vertex2: { x: prevHipCoords.x1, y: prevHipCoords.y1 }, + } + + /** 외벽선에서 라인 겹치는 경우에 대한 확인*/ + roof.lines.forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(hipEdge, lineEdge) + if ( + intersection && + Math.sign(prevHipCoords.x2 - prevHipCoords.x1) === Math.sign(prevHipCoords.x2 - intersection.x) && + Math.sign(prevHipCoords.y2 - prevHipCoords.y1) === Math.sign(prevHipCoords.y2 - intersection.y) + ) { + const intersectEdge = { + vertex1: { x: prevHipCoords.x2, y: prevHipCoords.y2 }, + vertex2: { x: intersection.x, y: intersection.y }, + } + const is = edgesIntersection(intersectEdge, lineEdge) + if (!is.isIntersectionOutside) { + const intersectSize = Big(prevHipCoords.x2) + .minus(Big(intersection.x)) + .pow(2) + .plus(Big(prevHipCoords.y2).minus(Big(intersection.y)).pow(2)) + .abs() + .sqrt() + .toNumber() + + intersectPoints.push({ + intersection, + size: intersectSize, + line, + }) + } + } + }) + const intersect = intersectPoints.sort((a, b) => a.size - b.size)[0] + + if (intersect) { + const degree = + intersect.line.attributes.pitch > 0 ? getDegreeByChon(intersect.line.attributes.pitch) : intersect.line.attributes.degree + const hipLine = drawHipLine( + [intersect.intersection.x, intersect.intersection.y, prevOppositeMidX.toNumber(), prevOppositeMidY.toNumber()], + canvas, + roof, + textMode, + null, + degree, + degree, + ) + baseHipLines.push({ + x1: prevHipCoords.x1, + y1: prevHipCoords.y1, + x2: prevHipCoords.x2, + y2: prevHipCoords.y2, + line: hipLine, + }) + } + } + oppositeMidY = prevOppositeMidY + oppositeMidX = prevOppositeMidX } - oppositeMidY = prevOppositeMidY - oppositeMidX = prevOppositeMidX } } if (baseRidgeCount < getMaxRidge(baseLines.length)) { @@ -1459,7 +1714,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } } }) - const ridgeLine = drawRidgeLine([currentMidX, currentMidY, oppositeMidX, oppositeMidY], canvas, roof, textMode) baseGableRidgeLines.push(ridgeLine) baseRidgeCount++ @@ -2190,6 +2444,15 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { let prevLineRidges = [], nextLineRidges = [] + const checkLine = new fabric.Line([x1, y1, x2, y2], { + stroke: 'red', + strokeWidth: 4, + parentId: roofId, + name: 'checkLine', + }) + canvas.add(checkLine) + canvas.renderAll() + const currentVectorX = Math.sign(currentLine.x2 - currentLine.x1) const currentVectorY = Math.sign(currentLine.y2 - currentLine.y1) const prevVectorX = Math.sign(prevLine.x2 - prevLine.x1) From b5b2ac21047272197b507772393426038c4a8b1e Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Fri, 16 May 2025 10:09:22 +0900 Subject: [PATCH 44/46] =?UTF-8?q?=EB=B3=B4=EC=A1=B0=EC=84=A0=20,=20?= =?UTF-8?q?=EC=A7=80=EB=B6=95=EC=84=A0=20=EB=A7=8C=EB=82=98=EB=8A=94=20?= =?UTF-8?q?=EA=B2=BD=EC=9A=B0=20=EC=A7=80=EB=B6=95=EC=84=A0=EB=B6=80?= =?UTF-8?q?=ED=84=B0=20=EC=9E=91=EC=97=85=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/roofcover/useAuxiliaryDrawing.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hooks/roofcover/useAuxiliaryDrawing.js b/src/hooks/roofcover/useAuxiliaryDrawing.js index 78d0b883..c854ae77 100644 --- a/src/hooks/roofcover/useAuxiliaryDrawing.js +++ b/src/hooks/roofcover/useAuxiliaryDrawing.js @@ -704,6 +704,7 @@ export function useAuxiliaryDrawing(id, isUseEffect = true) { removeLine(line1) } intersectionPoints.current.push(interSectionPointsWithRoofLines[0]) + return } } From 79e33880acdeab9ca990da531516eef4e5d7e7fb Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Fri, 16 May 2025 14:39:11 +0900 Subject: [PATCH 45/46] =?UTF-8?q?direction=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EC=8B=9C=20default=EA=B0=92=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/usePolygon.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js index 577274ad..91562948 100644 --- a/src/hooks/usePolygon.js +++ b/src/hooks/usePolygon.js @@ -1127,6 +1127,9 @@ export const usePolygon = () => { case 'left': defense = 'north' break + default: + defense = 'south' + break } pitch = polygon.lines[index]?.attributes?.pitch ?? 0 From 2a8d373f6718ecbd07f51f8b4c5dba041a5d579b Mon Sep 17 00:00:00 2001 From: Jaeyoung Lee Date: Wed, 21 May 2025 13:43:01 +0900 Subject: [PATCH 46/46] =?UTF-8?q?=EB=A7=88=EB=A3=A8=EC=A7=80=EB=B6=95,=20?= =?UTF-8?q?=EC=BC=80=EB=9D=BC=EB=B0=94=EC=A7=80=EB=B6=95=20=EA=B7=B8?= =?UTF-8?q?=EB=A6=AC=EA=B8=B0=20=EC=B2=AB=EB=B2=88=EC=A7=B8=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC=20=EC=9E=91=EC=97=85=EB=B3=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/qpolygon-utils.js | 2653 ++++++++++++++++-------------------- 1 file changed, 1208 insertions(+), 1445 deletions(-) diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index e27b49d6..b780cdd9 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -719,7 +719,16 @@ export const drawShedRoof = (roofId, canvas, textMode) => { } gables.forEach( - (gable) => (gable.attributes.actualSize = calcLineActualSize({ x1: gable.x1, y1: gable.y1, x2: gable.x2, y2: gable.y2 }, shedDegree)), + (gable) => + (gable.attributes.actualSize = calcLineActualSize( + { + x1: gable.x1, + y1: gable.y1, + x2: gable.x2, + y2: gable.y2, + }, + shedDegree, + )), ) const pitchSizeLines = [] @@ -789,20 +798,13 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const roof = canvas?.getObjects().find((object) => object.id === roofId) const wall = canvas.getObjects().find((object) => object.name === POLYGON_TYPE.WALL && object.attributes.roofId === roofId) - console.log('wall :', wall) - console.log('roof.lines :', roof.lines) - //Math.abs(line.x1 - line.x2) > 1 && Math.abs(line.y1 - line.y2) > 1 const hasNonParallelLines = roof.lines.filter((line) => Big(line.x1).minus(Big(line.x2)).gt(1) && Big(line.y1).minus(Big(line.y2)).gt(1)) if (hasNonParallelLines.length > 0) { - // alert('대각선이 존재합니다.') return } const eavesType = [LINE_TYPE.WALLLINE.EAVES, LINE_TYPE.WALLLINE.HIPANDGABLE] const gableType = [LINE_TYPE.WALLLINE.GABLE, LINE_TYPE.WALLLINE.JERKINHEAD] - // - // const eaves = roof.lines.filter((line) => eavesType.includes(line.attributes?.type)) - // const gables = roof.lines.filter((line) => gableType.includes(line.attributes?.type)) /** 외벽선 */ const baseLines = wall.baseLines.filter((line) => line.attributes.planeSize > 0) @@ -840,8 +842,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } }) - console.log('drawBaseLines : ', drawBaseLines) - /** baseLine을 기준으로 확인용 polygon 작성 */ const checkWallPolygon = new QPolygon(baseLinePoints, {}) @@ -865,17 +865,12 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const drawEavesSecondLines = [] const drawGableRidgeFirst = [] const drawGableRidgeSecond = [] - const drawGableFirstLines = [] - const drawGableSecondLines = [] const drawGablePolygonFirst = [] const drawGablePolygonSecond = [] /** 모양을 판단한다. */ drawBaseLines.forEach((currentBaseLine, index) => { let prevBaseLine = drawBaseLines[(index - 1 + drawBaseLines.length) % drawBaseLines.length] let nextBaseLine = drawBaseLines[(index + 1) % drawBaseLines.length] - // console.log('currentLine :', currentBaseLine) - // console.log('prevLine :', prevBaseLine) - // console.log('nextLine :', nextBaseLine) const currentLine = currentBaseLine.line const prevLine = prevBaseLine.line const nextLine = nextBaseLine.line @@ -894,7 +889,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { y: currentMidY.plus(checkScale.times(Math.sign(yVector.toNumber()))).toNumber(), } - // console.log('prevAngle :', prevAngle, 'nextAngle :', nextAngle) /** 현재 라인이 처마유형일 경우 */ if (eavesType.includes(currentLine.attributes?.type)) { if (eavesType.includes(nextLine.attributes?.type)) { @@ -950,12 +944,9 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { ) { if (checkWallPolygon.inPolygon(checkPoints)) { drawGableRidgeFirst.push({ currentBaseLine, prevBaseLine, nextBaseLine }) - drawGableFirstLines.push({ currentBaseLine, prevBaseLine, nextBaseLine }) } else { drawGableRidgeSecond.push({ currentBaseLine, prevBaseLine, nextBaseLine }) } - } else { - drawGableSecondLines.push({ currentBaseLine, prevBaseLine, nextBaseLine }) } } } @@ -979,12 +970,12 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { // console.log('drawGableFirstLines :', drawGableFirstLines) // console.log('drawGableSecondLines :', drawGableSecondLines) - console.log('drawEavesFirstLines :', drawEavesFirstLines) - console.log('drawEavesSecondLines :', drawEavesSecondLines) - console.log('drawGableRidgeFirst : ', drawGableRidgeFirst) - console.log('drawGableRidgeSecond : ', drawGableRidgeSecond) - console.log('drawGablePolygonFirst :', drawGablePolygonFirst) - console.log('drawGablePolygonSecond :', drawGablePolygonSecond) + // console.log('drawEavesFirstLines :', drawEavesFirstLines) + // console.log('drawEavesSecondLines :', drawEavesSecondLines) + // console.log('drawGableRidgeFirst : ', drawGableRidgeFirst) + // console.log('drawGableRidgeSecond : ', drawGableRidgeSecond) + // console.log('drawGablePolygonFirst :', drawGablePolygonFirst) + // console.log('drawGablePolygonSecond :', drawGablePolygonSecond) /** 박공지붕에서 파생되는 마루를 그린다. ㄷ 형태*/ drawGableRidgeFirst.forEach((current) => { @@ -1100,7 +1091,10 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { }) } - const prevHipEdge = { vertex1: { x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() }, vertex2: { x: prevLine.x1, y: prevLine.y1 } } + const prevHipEdge = { + vertex1: { x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() }, + vertex2: { x: prevLine.x1, y: prevLine.y1 }, + } const prevHipVectorX = Math.sign(prevHipEdge.vertex1.x - prevHipEdge.vertex2.x) const prevHipVectorY = Math.sign(prevHipEdge.vertex1.y - prevHipEdge.vertex2.y) const prevIsPoints = [] @@ -1136,10 +1130,19 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { prevDegree, prevDegree, ) - baseHipLines.push({ x1: prevLine.x1, y1: prevLine.y1, x2: oppositeMidX.toNumber(), y2: oppositeMidY.toNumber(), line: prevHipLine }) + baseHipLines.push({ + x1: prevLine.x1, + y1: prevLine.y1, + x2: oppositeMidX.toNumber(), + y2: oppositeMidY.toNumber(), + line: prevHipLine, + }) } - const nextHipEdge = { vertex1: { x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() }, vertex2: { x: nextLine.x2, y: nextLine.y2 } } + const nextHipEdge = { + vertex1: { x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() }, + vertex2: { x: nextLine.x2, y: nextLine.y2 }, + } const nextHipVectorX = Math.sign(nextHipEdge.vertex1.x - nextHipEdge.vertex2.x) const nextHipVectorY = Math.sign(nextHipEdge.vertex1.y - nextHipEdge.vertex2.y) const nextIsPoints = [] @@ -1175,7 +1178,13 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { nextDegree, nextDegree, ) - baseHipLines.push({ x1: nextLine.x2, y1: nextLine.y2, x2: oppositeMidX.toNumber(), y2: oppositeMidY.toNumber(), line: nextHipLine }) + baseHipLines.push({ + x1: nextLine.x2, + y1: nextLine.y2, + x2: oppositeMidX.toNumber(), + y2: oppositeMidY.toNumber(), + line: nextHipLine, + }) } if (baseRidgeCount < getMaxRidge(baseLines.length)) { @@ -1188,7 +1197,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { baseGableRidgeLines.push(ridgeLine) baseRidgeCount++ } - console.log('지붕선 분할 : end') } else { if (beforePrevBaseLine === afterNextBaseLine) { const afterNextMidX = Big(afterNextLine.x1).plus(Big(afterNextLine.x2)).div(2) @@ -1245,8 +1253,14 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { y2: oppositeMidY.plus(addOppositeY2).toNumber(), } - const intersection1 = findRoofIntersection(roof, checkHip1, { x: oppositeMidX.plus(addOppositeX1), y: oppositeMidY.plus(addOppositeY1) }) - const intersection2 = findRoofIntersection(roof, checkHip2, { x: oppositeMidX.plus(addOppositeX2), y: oppositeMidY.plus(addOppositeY2) }) + const intersection1 = findRoofIntersection(roof, checkHip1, { + x: oppositeMidX.plus(addOppositeX1), + y: oppositeMidY.plus(addOppositeY1), + }) + const intersection2 = findRoofIntersection(roof, checkHip2, { + x: oppositeMidX.plus(addOppositeX2), + y: oppositeMidY.plus(addOppositeY2), + }) const afterNextDegree = afterNextLine.attributes.pitch > 0 ? getDegreeByChon(afterNextLine.attributes.pitch) : afterNextLine.attributes.degree @@ -1296,6 +1310,76 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const vectorOppositeY = Math.sign(currentMidY.minus(oppositeMidY)) if (vectorMidX === vectorOppositeX && vectorMidY === vectorOppositeY && baseRidgeCount < getMaxRidge(baseLines.length)) { + if (!roof.inPolygon({ x: currentMidX.toNumber(), y: currentMidY.toNumber() })) { + const checkEdge = { + vertex1: { x: currentMidX.toNumber(), y: currentMidY.toNumber() }, + vertex2: { x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() }, + } + const intersectionPoints = [] + + roof.lines + .filter((line) => (currentVectorX === 0 ? line.x1 === line.x2 : line.y1 === line.y2)) + .forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(checkEdge, lineEdge) + if ( + intersection && + ((line.x1 <= intersection.x && line.x2 >= intersection.x && line.y1 <= intersection.y && line.y2 >= intersection.y) || + (line.x2 <= intersection.x && line.x1 >= intersection.x && line.y2 <= intersection.y && line.y1 >= intersection.y)) + ) { + const size = Big(intersection.x) + .minus(currentMidX) + .abs() + .pow(2) + .plus(Big(intersection.y).minus(currentMidY).abs().pow(2)) + .sqrt() + .toNumber() + intersectionPoints.push({ intersection, size }) + } + }) + + if (intersectionPoints.length > 0) { + const intersection = intersectionPoints.sort((a, b) => a.size - b.size)[0].intersection + currentMidX = Big(intersection.x) + currentMidY = Big(intersection.y) + } + } + + if (!roof.inPolygon({ x: oppositeMidX.toNumber(), y: oppositeMidX.toNumber() })) { + const checkEdge = { + vertex1: { x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() }, + vertex2: { x: currentMidX.toNumber(), y: currentMidY.toNumber() }, + } + const intersectionPoints = [] + + roof.lines + .filter((line) => (currentVectorX === 0 ? line.x1 === line.x2 : line.y1 === line.y2)) + .forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(checkEdge, lineEdge) + if ( + intersection && + ((line.x1 <= intersection.x && line.x2 >= intersection.x && line.y1 <= intersection.y && line.y2 >= intersection.y) || + (line.x2 <= intersection.x && line.x1 >= intersection.x && line.y2 <= intersection.y && line.y1 >= intersection.y)) + ) { + const size = Big(intersection.x) + .minus(oppositeMidX) + .abs() + .pow(2) + .plus(Big(intersection.y).minus(oppositeMidY).abs().pow(2)) + .sqrt() + .toNumber() + intersectionPoints.push({ intersection, size }) + } + }) + + if (intersectionPoints.length > 0) { + const intersection = intersectionPoints.sort((a, b) => a.size - b.size)[0].intersection + oppositeMidX = Big(intersection.x) + oppositeMidY = Big(intersection.y) + } + } + const ridge = drawRidgeLine( [currentMidX.toNumber(), currentMidY.toNumber(), oppositeMidX.toNumber(), oppositeMidY.toNumber()], canvas, @@ -1436,7 +1520,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const currentLine = line.line const nextLine = drawBaseLines[(index + 1) % drawBaseLines.length].line const prevLine = drawBaseLines[(index - 1 + drawBaseLines.length) % drawBaseLines.length].line - console.log('line.startPoint :', currentLine.startPoint, 'line.endPoint :', currentLine.endPoint) const angle = calculateAngle(currentLine.startPoint, currentLine.endPoint) const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint) const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint) @@ -1479,7 +1562,10 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { if (oppositeLines.length > 0) { const oppositePoint = oppositeLines.sort((a, b) => a.size - b.size)[0].intersection - const checkEdge = { vertex1: { x: currentMidX.toNumber(), y: currentMidY.toNumber() }, vertex2: { x: oppositePoint.x, y: oppositePoint.y } } + const checkEdge = { + vertex1: { x: currentMidX.toNumber(), y: currentMidY.toNumber() }, + vertex2: { x: oppositePoint.x, y: oppositePoint.y }, + } const oppositeRoofPoints = [] roof.lines .filter((line) => { @@ -1686,6 +1772,77 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } } if (baseRidgeCount < getMaxRidge(baseLines.length)) { + /** 포인트가 지붕 밖에 있는 경우 조정 */ + if (!roof.inPolygon({ x: currentMidX.toNumber(), y: currentMidY.toNumber() })) { + const checkEdge = { + vertex1: { x: currentMidX.toNumber(), y: currentMidY.toNumber() }, + vertex2: { x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() }, + } + const intersectionPoints = [] + + roof.lines + .filter((line) => (currentVectorX === 0 ? line.x1 === line.x2 : line.y1 === line.y2)) + .forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(checkEdge, lineEdge) + if ( + intersection && + ((line.x1 <= intersection.x && line.x2 >= intersection.x && line.y1 <= intersection.y && line.y2 >= intersection.y) || + (line.x2 <= intersection.x && line.x1 >= intersection.x && line.y2 <= intersection.y && line.y1 >= intersection.y)) + ) { + const size = Big(intersection.x) + .minus(currentMidX) + .abs() + .pow(2) + .plus(Big(intersection.y).minus(currentMidY).abs().pow(2)) + .sqrt() + .toNumber() + intersectionPoints.push({ intersection, size }) + } + }) + + if (intersectionPoints.length > 0) { + const intersection = intersectionPoints.sort((a, b) => a.size - b.size)[0].intersection + currentMidX = Big(intersection.x) + currentMidY = Big(intersection.y) + } + } + + if (!roof.inPolygon({ x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() })) { + const checkEdge = { + vertex1: { x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() }, + vertex2: { x: currentMidX.toNumber(), y: currentMidY.toNumber() }, + } + const intersectionPoints = [] + + roof.lines + .filter((line) => (currentVectorX === 0 ? line.x1 === line.x2 : line.y1 === line.y2)) + .forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(checkEdge, lineEdge) + if ( + intersection && + ((line.x1 <= intersection.x && line.x2 >= intersection.x && line.y1 <= intersection.y && line.y2 >= intersection.y) || + (line.x2 <= intersection.x && line.x1 >= intersection.x && line.y2 <= intersection.y && line.y1 >= intersection.y)) + ) { + const size = Big(intersection.x) + .minus(oppositeMidX) + .abs() + .pow(2) + .plus(Big(intersection.y).minus(oppositeMidY).abs().pow(2)) + .sqrt() + .toNumber() + intersectionPoints.push({ intersection, size }) + } + }) + + if (intersectionPoints.length > 0) { + const intersection = intersectionPoints.sort((a, b) => a.size - b.size)[0].intersection + oppositeMidX = Big(intersection.x) + oppositeMidY = Big(intersection.y) + } + } + /** 마루가 맞은편 외벽선에 닿는 경우 해당 부분까지로 한정한다. */ const ridgeEdge = { vertex1: { x: currentMidX.toNumber(), y: currentMidY.toNumber() }, @@ -1714,7 +1871,12 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } } }) - const ridgeLine = drawRidgeLine([currentMidX, currentMidY, oppositeMidX, oppositeMidY], canvas, roof, textMode) + const ridgeLine = drawRidgeLine( + [currentMidX.toNumber(), currentMidY.toNumber(), oppositeMidX.toNumber(), oppositeMidY.toNumber()], + canvas, + roof, + textMode, + ) baseGableRidgeLines.push(ridgeLine) baseRidgeCount++ } @@ -1728,9 +1890,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const prevLine = prevBaseLine.line const nextLine = nextBaseLine.line - let { x1, x2, y1, y2, size } = currentBaseLine - let beforePrevBaseLine, afterNextBaseLine - /** 이전 라인의 경사 */ const prevDegree = prevLine.attributes.pitch > 0 ? getDegreeByChon(prevLine.attributes.pitch) : prevLine.attributes.degree /** 다음 라인의 경사 */ @@ -1923,7 +2082,10 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { x2: ridgeEndPoint.x.toNumber(), y2: ridgeEndPoint.y.toNumber(), } - const ridgeIntersection = findRoofIntersection(roof, ridgePoints, { x: Big(ridgePoints.x2), y: Big(ridgePoints.y2) }) + const ridgeIntersection = findRoofIntersection(roof, ridgePoints, { + x: Big(ridgePoints.x2), + y: Big(ridgePoints.y2), + }) if (ridgeIntersection) { points = [ridgeIntersection.intersection.x, ridgeIntersection.intersection.y, ridgeEndPoint.x, ridgeEndPoint.y] } @@ -1941,12 +2103,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { baseGableRidgeLines.push(ridgeLine) baseRidgeCount++ } - - canvas - .getObjects() - .filter((obj) => obj.name === 'checkLine' || obj.name === 'checkPoint') - .forEach((obj) => canvas.remove(obj)) - canvas.renderAll() } else { const oppositeLines = baseLines.filter((line) => { const lineAngle = calculateAngle(line.startPoint, line.endPoint) @@ -2002,11 +2158,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { baseRidgeCount++ } } - canvas - .getObjects() - .filter((obj) => obj.name === 'checkLine' || obj.name === 'checkPoint') - .forEach((obj) => canvas.remove(obj)) - canvas.renderAll() }) const uniqueRidgeLines = [] @@ -2028,8 +2179,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { baseGableRidgeLines = uniqueRidgeLines - console.log('baseGableRidgeLines : ', baseGableRidgeLines) - /** 박공지붕 polygon 생성 */ drawGablePolygonFirst.forEach((current) => { const { currentBaseLine, prevBaseLine, nextBaseLine } = current @@ -2042,59 +2191,127 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const currentVectorX = Math.sign(currentLine.x2 - currentLine.x1) const currentVectorY = Math.sign(currentLine.y2 - currentLine.y1) - const prevVectorX = Math.sign(prevLine.x2 - prevLine.x1) - const prevVectorY = Math.sign(prevLine.y2 - prevLine.y1) const nextVectorX = Math.sign(nextLine.x1 - nextLine.x2) const nextVectorY = Math.sign(nextLine.y1 - nextLine.y2) const currentDegree = currentLine.attributes.pitch > 0 ? getDegreeByChon(currentLine.attributes.pitch) : currentLine.attributes.degree - const currentOffset = currentLine.attributes.offset - const prevOffset = prevLine.attributes.offset - const nextOffset = nextLine.attributes.offset let roofX1, roofY1, roofX2, roofY2 + const currentMidX = Big(currentLine.x1).plus(Big(currentLine.x2)).div(2) + const currentMidY = Big(currentLine.y1).plus(Big(currentLine.y2)).div(2) + const intersectionRoofs = [] if (currentVectorX === 0) { - roofX1 = Big(x1).plus(Big(currentLine.attributes.offset).times(prevVectorX)) - roofX2 = roofX1 - roofY1 = Big(y1).plus(Big(prevLine.attributes.offset).times(currentVectorY * -1)) - roofY2 = Big(y2).plus(Big(nextLine.attributes.offset).times(currentVectorY)) + const checkEdge = { + vertex1: { x: prevLine.x1, y: currentMidY.toNumber() }, + vertex2: { x: currentMidX.toNumber(), y: currentMidY.toNumber() }, + } + roof.lines + .filter((line) => Math.sign(line.x2 - line.x1) === currentVectorX && Math.sign(line.y2 - line.y1) === currentVectorY) + .forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(checkEdge, lineEdge) + if (intersection) { + intersection.x = Math.round(intersection.x) + intersection.y = Math.round(intersection.y) + if ( + (line.x1 <= intersection.x && intersection.x <= line.x2 && line.y1 <= intersection.y && intersection.y <= line.y2) || + (line.x2 <= intersection.x && intersection.x <= line.x1 && line.y2 <= intersection.y && intersection.y <= line.y1) + ) { + intersectionRoofs.push({ + line, + intersection, + size: Big(intersection.x).minus(currentMidX).abs().pow(2).plus(Big(intersection.y).minus(currentMidY).abs().pow(2)).sqrt(), + }) + } + } + }) } else { - roofX1 = Big(x1).plus(Big(prevLine.attributes.offset).times(currentVectorX * -1)) - roofX2 = Big(x2).plus(Big(nextLine.attributes.offset).times(currentVectorX)) - roofY1 = Big(y1).plus(Big(currentLine.attributes.offset).times(prevVectorY)) - roofY2 = roofY1 + const checkEdge = { + vertex1: { x: currentMidX.toNumber(), y: prevLine.y1 }, + vertex2: { x: currentMidX.toNumber(), y: currentMidY.toNumber() }, + } + roof.lines + .filter((line) => Math.sign(line.x2 - line.x1) === currentVectorX && Math.sign(line.y2 - line.y1) === currentVectorY) + .forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(checkEdge, lineEdge) + if (intersection) { + intersection.x = Math.round(intersection.x) + intersection.y = Math.round(intersection.y) + if ( + (line.x1 <= intersection.x && intersection.x <= line.x2 && line.y1 <= intersection.y && intersection.y <= line.y2) || + (line.x2 <= intersection.x && intersection.x <= line.x1 && line.y2 <= intersection.y && intersection.y <= line.y1) + ) { + intersectionRoofs.push({ + line, + intersection, + size: Big(intersection.x).minus(currentMidX).abs().pow(2).plus(Big(intersection.y).minus(currentMidY).abs().pow(2)).sqrt(), + }) + } + } + }) + } + if (intersectionRoofs.length > 0) { + const currentRoof = intersectionRoofs.sort((a, b) => a.size - b.size)[0].line + roofX1 = Big(currentRoof.x1) + roofY1 = Big(currentRoof.y1) + roofX2 = Big(currentRoof.x2) + roofY2 = Big(currentRoof.y2) } let prevRoofLine, nextRoofLine - const roofEdge = { vertex1: { x: roofX1.toNumber(), y: roofY1.toNumber() }, vertex2: { x: roofX2.toNumber(), y: roofY2.toNumber() } } + const roofEdge = { + vertex1: { x: roofX1.toNumber(), y: roofY1.toNumber() }, + vertex2: { x: roofX2.toNumber(), y: roofY2.toNumber() }, + } roof.lines .filter((line) => (currentVectorX === 0 ? line.y1 === line.y2 : line.x1 === line.x2)) .forEach((line) => { const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } const intersection = edgesIntersection(roofEdge, lineEdge) if (intersection) { - if (roofX1.eq(intersection.x) && roofY1.eq(intersection.y)) { - prevRoofLine = line - } - if (roofX2.eq(intersection.x) && roofY2.eq(intersection.y)) { - nextRoofLine = line + intersection.x = Math.round(intersection.x) + intersection.y = Math.round(intersection.y) + if ( + (line.x1 <= intersection.x && intersection.x <= line.x2 && line.y1 <= intersection.y && intersection.y <= line.y2) || + (line.x2 <= intersection.x && intersection.x <= line.x1 && line.y2 <= intersection.y && intersection.y <= line.y1) + ) { + if (roofX1.eq(intersection.x) && roofY1.eq(intersection.y)) { + prevRoofLine = line + } + if (roofX2.eq(intersection.x) && roofY2.eq(intersection.y)) { + nextRoofLine = line + } } } }) - - const prevRoofEdge = { vertex1: { x: prevRoofLine.x2, y: prevRoofLine.y2 }, vertex2: { x: prevRoofLine.x1, y: prevRoofLine.y1 } } - const nextRoofEdge = { vertex1: { x: nextRoofLine.x1, y: nextRoofLine.y1 }, vertex2: { x: nextRoofLine.x2, y: nextRoofLine.y2 } } + const prevRoofEdge = { + vertex1: { x: prevRoofLine.x2, y: prevRoofLine.y2 }, + vertex2: { x: prevRoofLine.x1, y: prevRoofLine.y1 }, + } + const nextRoofEdge = { + vertex1: { x: nextRoofLine.x1, y: nextRoofLine.y1 }, + vertex2: { x: nextRoofLine.x2, y: nextRoofLine.y2 }, + } baseGableRidgeLines.forEach((ridge) => { const ridgeEdge = { vertex1: { x: ridge.x1, y: ridge.y1 }, vertex2: { x: ridge.x2, y: ridge.y2 } } const prevIs = edgesIntersection(prevRoofEdge, ridgeEdge) const nextIs = edgesIntersection(nextRoofEdge, ridgeEdge) - if (prevIs) { + if ( + prevIs && + ((ridgeEdge.vertex1.x === prevIs.x && ridgeEdge.vertex1.y === prevIs.y) || + (ridgeEdge.vertex2.x === prevIs.x && ridgeEdge.vertex2.y === prevIs.y)) + ) { prevLineRidges.push({ ridge, size: calcLinePlaneSize({ x1: roofX1, y1: roofY1, x2: prevIs.x, y2: prevIs.y }), }) } - if (nextIs) { + if ( + nextIs && + ((ridgeEdge.vertex1.x === nextIs.x && ridgeEdge.vertex1.y === nextIs.y) || + (ridgeEdge.vertex2.x === nextIs.x && ridgeEdge.vertex2.y === nextIs.y)) + ) { nextLineRidges.push({ ridge, size: calcLinePlaneSize({ x1: roofX2, y1: roofY2, x2: nextIs.x, y2: nextIs.y }), @@ -2131,308 +2348,466 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { .sort((a, b) => a.size - b.size) nextLineRidge = nextLineRidges[0].ridge } + const ridgeLine = prevLineRidge === undefined ? nextLineRidge : prevLineRidge - /** 마루에서 현재라인으로 향하는 외벽선까지의 포인트를 확인 하여 처리*/ - let checkEdge - if (currentVectorX === 0) { - checkEdge = { vertex1: { x: prevLineRidge.x1, y: roofY1.toNumber() }, vertex2: { x: roofX1.toNumber(), y: roofY1.toNumber() } } - } else { - checkEdge = { vertex1: { x: roofX1.toNumber(), y: prevLineRidge.y1 }, vertex2: { x: roofX1.toNumber(), y: roofY1.toNumber() } } - } - const checkVectorX = Math.sign(checkEdge.vertex1.x - checkEdge.vertex2.x) - const checkVectorY = Math.sign(checkEdge.vertex1.y - checkEdge.vertex2.y) - const intersectPoints = [] - roof.lines - .filter((line) => (currentVectorX === 0 ? line.x1 === line.x2 : line.y1 === line.y2)) - .forEach((line) => { - const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } - const is = edgesIntersection(checkEdge, lineEdge) - if (is) { - const isVectorX = Math.sign(checkEdge.vertex1.x - is.x) - const isVectorY = Math.sign(checkEdge.vertex1.y - is.y) - const isLineOtherPoint = is.x === line.x1 && is.y === line.y1 ? { x: line.x2, y: line.y2 } : { x: line.x1, y: line.y1 } - const lineVectorX = Math.sign(isLineOtherPoint.x - is.x) - const lineVectorY = Math.sign(isLineOtherPoint.y - is.y) - if (checkVectorX === isVectorX && checkVectorY === isVectorY && lineVectorX === currentVectorX && lineVectorY === currentVectorY) { - intersectPoints.push(is) + /** 마루선이 한쪽만 존재 연결될 경우 다각형을 그린다.*/ + if (prevLineRidge === undefined || nextLineRidge === undefined) { + const points = [ + { x: roofX1.toNumber(), y: roofY1.toNumber() }, + { x: roofX2.toNumber(), y: roofY2.toNumber() }, + { x: ridgeLine.x1, y: ridgeLine.y1 }, + { x: ridgeLine.x2, y: ridgeLine.y2 }, + ] + const minX = Math.min(ridgeLine.x1, ridgeLine.x2, roofX1.toNumber(), roofX2.toNumber()) + const minY = Math.min(ridgeLine.y1, ridgeLine.y2, roofY1.toNumber(), roofY2.toNumber()) + + let polygonPoints = [] + + polygonPoints.push(points.find((point) => point.x === minX && point.y === minY)) + + let i = 0 + let length = points.length + while (i < length - 1) { + const currentPoint = polygonPoints[i] + if (i === 0) { + polygonPoints.push(points.find((point) => point.x === currentPoint.x && point.y > currentPoint.y)) + i++ + } else { + const prevPoint = polygonPoints[(i - 1 + polygonPoints.length) % polygonPoints.length] + const vectorX = Math.sign(prevPoint.x - currentPoint.x) + const vectorY = Math.sign(prevPoint.y - currentPoint.y) + if (i % 2 === 1) { + /** 가로선 찾기 */ + const nextPoint = points.find((point) => point.y === currentPoint.y && point.x !== currentPoint.x) + if (nextPoint === undefined) { + const connectLine = roof.lines.find((line) => line.y1 === line.y2 && line.x1 === currentPoint.x && line.y1 === currentPoint.y) + if (connectLine) { + polygonPoints.push( + connectLine.x1 === currentPoint.x && connectLine.y1 === currentPoint.y + ? { x: connectLine.x2, y: connectLine.y2 } + : { x: connectLine.x1, y: connectLine.y1 }, + ) + } else { + const intersectLine = roof.lines.find( + (line) => + line.y1 === currentPoint.y && + line.y2 === currentPoint.y && + ((line.x1 <= currentPoint.x && line.x2 >= currentPoint.x) || (line.x2 <= currentPoint.x && line.x1 >= currentPoint.x)), + ) + /** 이전 라인이 위에서 아래로 항하면 오른쪽 포인트 찾기*/ + if (vectorY === 1) { + polygonPoints.push({ x: intersectLine.x1, y: intersectLine.y1 }) + } + /** 이전 라인이 아래에서 위로 항하면 왼쪽 포인트 찾기*/ + if (vectorY === -1) { + polygonPoints.push({ x: intersectLine.x2, y: intersectLine.y2 }) + } + } + length = length + 1 + } else { + polygonPoints.push(nextPoint) + } + i++ + } else { + /** 세로선 찾기*/ + const nextPoint = points.find((point) => point.x === currentPoint.x && point.y !== currentPoint.y) + if (nextPoint === undefined) { + const connectLine = roof.lines.find((line) => line.x1 === line.x2 && line.x1 === currentPoint.x && line.y1 === currentPoint.y) + if (connectLine) { + polygonPoints.push( + connectLine.x1 === currentPoint.x && connectLine.y1 === currentPoint.y + ? { x: connectLine.x2, y: connectLine.y2 } + : { x: connectLine.x1, y: connectLine.y1 }, + ) + } else { + const intersectLine = roof.lines.find( + (line) => + line.x1 === currentPoint.x && + line.x2 === currentPoint.x && + ((line.y1 <= currentPoint.y && line.y2 >= currentPoint.y) || (line.y2 <= currentPoint.y && line.y1 >= currentPoint.y)), + ) + /** 이전 라인이 왼쪽에서 오른쪽로 항하면 위쪽 포인트 찾기*/ + if (vectorX === -1) { + polygonPoints.push({ x: intersectLine.x1, y: intersectLine.y1 }) + } + /** 이전 라인이 오른에서 왼쪽으로 항하면 아래쪽 포인트 찾기*/ + if (vectorX === 1) { + polygonPoints.push({ x: intersectLine.x2, y: intersectLine.y2 }) + } + } + length = length + 1 + } else { + polygonPoints.push(nextPoint) + } + i++ } } - }) - let intersect = intersectPoints[0] - if (currentVectorX === 0) { - polygonPoints.push({ x: intersect.x, y: roofY1.toNumber() }, { x: intersect.x, y: roofY2.toNumber() }) - } else { - polygonPoints.push({ x: roofX1.toNumber(), y: intersect.y }, { x: roofX2.toNumber(), y: intersect.y }) - } + } + polygonPoints = getSortedPoint(polygonPoints) + polygonPoints.forEach((currentPoint, index) => { + const nextPoint = polygonPoints[(index + 1) % polygonPoints.length] + const points = [currentPoint.x, currentPoint.y, nextPoint.x, nextPoint.y] + const isParallel = ridgeLine.x1 === ridgeLine.x2 ? currentPoint.x === nextPoint.x : currentPoint.y === nextPoint.y - if (prevLineRidge === nextLineRidge) { - /** 4각*/ - polygonPoints.push({ x: prevLineRidge.x1, y: prevLineRidge.y1 }, { x: prevLineRidge.x2, y: prevLineRidge.y2 }) - } else { - /** 6각이상*/ - let isOverLap = - currentVectorX === 0 - ? (prevLineRidge.y1 <= nextLineRidge.y1 && prevLineRidge.y2 >= nextLineRidge.y1) || - (prevLineRidge.y1 >= nextLineRidge.y1 && prevLineRidge.y2 <= nextLineRidge.y1) || - (prevLineRidge.y1 <= nextLineRidge.y2 && prevLineRidge.y2 >= nextLineRidge.y2) || - (prevLineRidge.y1 >= nextLineRidge.y2 && prevLineRidge.y2 <= nextLineRidge.y2) - : (prevLineRidge.x1 <= nextLineRidge.x1 && prevLineRidge.x2 >= nextLineRidge.x1) || - (prevLineRidge.x1 >= nextLineRidge.x1 && prevLineRidge.x2 <= nextLineRidge.x1) || - (prevLineRidge.x1 <= nextLineRidge.x2 && prevLineRidge.x2 >= nextLineRidge.x2) || - (prevLineRidge.x1 >= nextLineRidge.x2 && prevLineRidge.x2 <= nextLineRidge.x2) - if (isOverLap) { - const prevDistance = currentVectorX === 0 ? Math.abs(prevLineRidge.x1 - roofX1.toNumber()) : Math.abs(prevLineRidge.y1 - roofY1.toNumber()) - const nextDistance = currentVectorX === 0 ? Math.abs(nextLineRidge.x1 - roofX1.toNumber()) : Math.abs(nextLineRidge.y1 - roofY1.toNumber()) - /** 현재 지붕 라인과 먼 라인의 포인트를 온전히 사용한다. */ - if (prevDistance === nextDistance) { - console.log('prevDistance === nextDistance') - } else if (prevDistance < nextDistance) { - polygonPoints.push({ x: nextLineRidge.x1, y: nextLineRidge.y1 }, { x: nextLineRidge.x2, y: nextLineRidge.y2 }) - /** 이전라인과 교차한 마루의 포인트*/ - const prevRidgePoint1 = - currentVectorX === 0 - ? roofY1.toNumber() === prevLineRidge.y1 - ? { x: prevLineRidge.x1, y: prevLineRidge.y1 } - : { x: prevLineRidge.x2, y: prevLineRidge.y2 } - : roofX1.toNumber() === prevLineRidge.x1 - ? { x: prevLineRidge.x1, y: prevLineRidge.y1 } - : { x: prevLineRidge.x2, y: prevLineRidge.y2 } - - polygonPoints.push(prevRidgePoint1) - - /** 다음 라인과 교차한 마루의 포인트 중 라인과 접하지 않은 포인트*/ - const checkRidgePoint = - currentVectorX === 0 - ? roofY2.toNumber() !== nextLineRidge.y1 - ? { x: nextLineRidge.x1, y: nextLineRidge.y1 } - : { x: nextLineRidge.x2, y: nextLineRidge.y2 } - : roofX2.toNumber() !== nextLineRidge.x1 - ? { x: nextLineRidge.x1, y: nextLineRidge.y1 } - : { x: nextLineRidge.x2, y: nextLineRidge.y2 } - - const prevRidgePoint2 = - currentVectorX === 0 ? { x: prevRidgePoint1.x, y: checkRidgePoint.y } : { x: checkRidgePoint.x, y: prevRidgePoint1.y } - polygonPoints.push(prevRidgePoint2) + let line + if (isParallel) { + line = drawRoofLine(points, canvas, roof, textMode) + baseGableLines.push(line) } else { - polygonPoints.push({ x: prevLineRidge.x1, y: prevLineRidge.y1 }, { x: prevLineRidge.x2, y: prevLineRidge.y2 }) - - /** 다음라인과 교차한 마루의 포인트*/ - const nextRidgePoint1 = - currentVectorX === 0 - ? roofY2.toNumber() === nextLineRidge.y1 - ? { x: nextLineRidge.x1, y: nextLineRidge.y1 } - : { x: nextLineRidge.x2, y: nextLineRidge.y2 } - : roofX2.toNumber() === nextLineRidge.x1 - ? { x: nextLineRidge.x1, y: nextLineRidge.y1 } - : { x: nextLineRidge.x2, y: nextLineRidge.y2 } - polygonPoints.push(nextRidgePoint1) - - /** 이전 라인과 교차한 마루의 포인트 중 라인과 접하지 않은 포인트*/ - const checkRidgePoint = - currentVectorX === 0 - ? roofY1.toNumber() !== prevLineRidge.y1 - ? { x: prevLineRidge.x1, y: prevLineRidge.y1 } - : { x: prevLineRidge.x2, y: prevLineRidge.y2 } - : roofX1.toNumber() !== prevLineRidge.x1 - ? { x: prevLineRidge.x1, y: prevLineRidge.y1 } - : { x: prevLineRidge.x2, y: prevLineRidge.y2 } - const nextRidgePoint2 = - currentVectorX === 0 ? { x: nextRidgePoint1.x, y: checkRidgePoint.y } : { x: checkRidgePoint.x, y: nextRidgePoint1.y } - polygonPoints.push(nextRidgePoint2) + line = drawHipLine(points, canvas, roof, textMode, null, currentDegree, currentDegree) + baseHipLines.push({ x1: line.x1, y1: line.y1, x2: line.x2, y2: line.y2, line }) + } + }) + } else { + /** 마루에서 현재라인으로 향하는 외벽선까지의 포인트를 확인 하여 처리*/ + let checkEdge + if (currentVectorX === 0) { + checkEdge = { + vertex1: { x: ridgeLine.x1, y: roofY1.toNumber() }, + vertex2: { x: roofX1.toNumber(), y: roofY1.toNumber() }, } } else { - /** 마루가 겹치지 않을때 */ - const otherRidgeLines = [] + checkEdge = { + vertex1: { x: roofX1.toNumber(), y: ridgeLine.y1 }, + vertex2: { x: roofX1.toNumber(), y: roofY1.toNumber() }, + } + } + const checkVectorX = Math.sign(checkEdge.vertex1.x - checkEdge.vertex2.x) + const checkVectorY = Math.sign(checkEdge.vertex1.y - checkEdge.vertex2.y) + const intersectPoints = [] + roof.lines + .filter((line) => (currentVectorX === 0 ? line.x1 === line.x2 : line.y1 === line.y2)) + .forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const is = edgesIntersection(checkEdge, lineEdge) + if (is) { + const isVectorX = Math.sign(checkEdge.vertex1.x - is.x) + const isVectorY = Math.sign(checkEdge.vertex1.y - is.y) + const isLineOtherPoint = + is.x === line.x1 && is.y === line.y1 + ? { x: line.x2, y: line.y2 } + : { + x: line.x1, + y: line.y1, + } + const lineVectorX = Math.sign(isLineOtherPoint.x - is.x) + const lineVectorY = Math.sign(isLineOtherPoint.y - is.y) + if (checkVectorX === isVectorX && checkVectorY === isVectorY && lineVectorX === currentVectorX && lineVectorY === currentVectorY) { + intersectPoints.push(is) + } + } + }) - baseGableRidgeLines - .filter((ridge) => ridge !== prevLineRidge && ridge !== nextLineRidge) - .filter((ridge) => (currentVectorX === 0 ? ridge.x1 === ridge.x2 : ridge.y1 === ridge.y2)) - .filter((ridge) => - currentVectorX === 0 ? nextVectorX === Math.sign(nextLine.x1 - ridge.x1) : nextVectorY === Math.sign(nextLine.y1 - ridge.y1), - ) - .forEach((ridge) => { - const size = currentVectorX === 0 ? Math.abs(nextLine.x1 - ridge.x1) : Math.abs(nextLine.y1 - ridge.y1) - otherRidgeLines.push({ ridge, size }) - }) - if (otherRidgeLines.length > 0) { - const otherRidge = otherRidgeLines.sort((a, b) => a.size - b.size)[0].ridge - /** - * otherRidge이 prevRidgeLine, nextRidgeLine 과 currentLine의 사이에 있는지 확인해서 분할하여 작업 - * 지붕의 덮힘이 다르기 때문 - */ - const isInside = - currentVectorX === 0 - ? Math.abs(currentLine.x1 - otherRidge.x1) < Math.abs(currentLine.x1 - prevLineRidge.x1) && - Math.abs(currentLine.x1 - otherRidge.x1) < Math.abs(currentLine.x1 - nextLineRidge.x1) - : Math.abs(currentLine.y1 - otherRidge.y1) < Math.abs(currentLine.y1 - prevLineRidge.y1) && - Math.abs(currentLine.y1 - otherRidge.y1) < Math.abs(currentLine.y1 - nextLineRidge.y1) + let intersect = intersectPoints[0] + if (currentVectorX === 0) { + polygonPoints.push({ x: intersect.x, y: roofY1.toNumber() }, { x: intersect.x, y: roofY2.toNumber() }) + } else { + polygonPoints.push({ x: roofX1.toNumber(), y: intersect.y }, { x: roofX2.toNumber(), y: intersect.y }) + } - if (isInside) { + if (prevLineRidge === nextLineRidge) { + /** 4각*/ + polygonPoints.push({ x: prevLineRidge.x1, y: prevLineRidge.y1 }, { x: prevLineRidge.x2, y: prevLineRidge.y2 }) + } else { + /** 6각이상*/ + let isOverLap = + currentVectorX === 0 + ? (prevLineRidge.y1 <= nextLineRidge.y1 && prevLineRidge.y2 >= nextLineRidge.y1) || + (prevLineRidge.y1 >= nextLineRidge.y1 && prevLineRidge.y2 <= nextLineRidge.y1) || + (prevLineRidge.y1 <= nextLineRidge.y2 && prevLineRidge.y2 >= nextLineRidge.y2) || + (prevLineRidge.y1 >= nextLineRidge.y2 && prevLineRidge.y2 <= nextLineRidge.y2) + : (prevLineRidge.x1 <= nextLineRidge.x1 && prevLineRidge.x2 >= nextLineRidge.x1) || + (prevLineRidge.x1 >= nextLineRidge.x1 && prevLineRidge.x2 <= nextLineRidge.x1) || + (prevLineRidge.x1 <= nextLineRidge.x2 && prevLineRidge.x2 >= nextLineRidge.x2) || + (prevLineRidge.x1 >= nextLineRidge.x2 && prevLineRidge.x2 <= nextLineRidge.x2) + if (isOverLap) { + const prevDistance = currentVectorX === 0 ? Math.abs(prevLineRidge.x1 - roofX1.toNumber()) : Math.abs(prevLineRidge.y1 - roofY1.toNumber()) + const nextDistance = currentVectorX === 0 ? Math.abs(nextLineRidge.x1 - roofX1.toNumber()) : Math.abs(nextLineRidge.y1 - roofY1.toNumber()) + /** 현재 지붕 라인과 먼 라인의 포인트를 온전히 사용한다. */ + if (prevDistance <= nextDistance) { + polygonPoints.push( + { x: nextLineRidge.x1, y: nextLineRidge.y1 }, + { + x: nextLineRidge.x2, + y: nextLineRidge.y2, + }, + ) + /** 이전라인과 교차한 마루의 포인트*/ + const prevRidgePoint1 = + currentVectorX === 0 + ? roofY1.toNumber() === prevLineRidge.y1 + ? { x: prevLineRidge.x1, y: prevLineRidge.y1 } + : { x: prevLineRidge.x2, y: prevLineRidge.y2 } + : roofX1.toNumber() === prevLineRidge.x1 + ? { x: prevLineRidge.x1, y: prevLineRidge.y1 } + : { x: prevLineRidge.x2, y: prevLineRidge.y2 } + + polygonPoints.push(prevRidgePoint1) + + /** 다음 라인과 교차한 마루의 포인트 중 라인과 접하지 않은 포인트*/ + const checkRidgePoint = + currentVectorX === 0 + ? roofY2.toNumber() !== nextLineRidge.y1 + ? { x: nextLineRidge.x1, y: nextLineRidge.y1 } + : { x: nextLineRidge.x2, y: nextLineRidge.y2 } + : roofX2.toNumber() !== nextLineRidge.x1 + ? { x: nextLineRidge.x1, y: nextLineRidge.y1 } + : { x: nextLineRidge.x2, y: nextLineRidge.y2 } + + const prevRidgePoint2 = + currentVectorX === 0 + ? { x: prevRidgePoint1.x, y: checkRidgePoint.y } + : { + x: checkRidgePoint.x, + y: prevRidgePoint1.y, + } + polygonPoints.push(prevRidgePoint2) + } else { polygonPoints.push( { x: prevLineRidge.x1, y: prevLineRidge.y1 }, - { x: prevLineRidge.x2, y: prevLineRidge.y2 }, - { x: nextLineRidge.x1, y: nextLineRidge.y1 }, - { x: nextLineRidge.x2, y: nextLineRidge.y2 }, + { + x: prevLineRidge.x2, + y: prevLineRidge.y2, + }, ) - let ridgeAllPoints = [ - { x: prevLineRidge.x1, y: prevLineRidge.y1 }, - { x: prevLineRidge.x2, y: prevLineRidge.y2 }, - { x: nextLineRidge.x1, y: nextLineRidge.y1 }, - { x: nextLineRidge.x2, y: nextLineRidge.y2 }, - ] - let ridgePoints = [] - ridgeAllPoints.forEach((point) => { - let isOnLine = false - roof.lines.forEach((line) => { - if (isPointOnLine({ x1: line.x1, y1: line.y1, x2: line.x2, y2: line.y2 }, point)) { - isOnLine = true + /** 다음라인과 교차한 마루의 포인트*/ + const nextRidgePoint1 = + currentVectorX === 0 + ? roofY2.toNumber() === nextLineRidge.y1 + ? { x: nextLineRidge.x1, y: nextLineRidge.y1 } + : { x: nextLineRidge.x2, y: nextLineRidge.y2 } + : roofX2.toNumber() === nextLineRidge.x1 + ? { x: nextLineRidge.x1, y: nextLineRidge.y1 } + : { x: nextLineRidge.x2, y: nextLineRidge.y2 } + polygonPoints.push(nextRidgePoint1) + + /** 이전 라인과 교차한 마루의 포인트 중 라인과 접하지 않은 포인트*/ + const checkRidgePoint = + currentVectorX === 0 + ? roofY1.toNumber() !== prevLineRidge.y1 + ? { x: prevLineRidge.x1, y: prevLineRidge.y1 } + : { x: prevLineRidge.x2, y: prevLineRidge.y2 } + : roofX1.toNumber() !== prevLineRidge.x1 + ? { x: prevLineRidge.x1, y: prevLineRidge.y1 } + : { x: prevLineRidge.x2, y: prevLineRidge.y2 } + const nextRidgePoint2 = + currentVectorX === 0 + ? { x: nextRidgePoint1.x, y: checkRidgePoint.y } + : { + x: checkRidgePoint.x, + y: nextRidgePoint1.y, + } + polygonPoints.push(nextRidgePoint2) + } + } else { + /** 마루가 겹치지 않을때 */ + const otherRidgeLines = [] + + baseGableRidgeLines + .filter((ridge) => ridge !== prevLineRidge && ridge !== nextLineRidge) + .filter((ridge) => (currentVectorX === 0 ? ridge.x1 === ridge.x2 : ridge.y1 === ridge.y2)) + .filter((ridge) => + currentVectorX === 0 ? nextVectorX === Math.sign(nextLine.x1 - ridge.x1) : nextVectorY === Math.sign(nextLine.y1 - ridge.y1), + ) + .forEach((ridge) => { + const size = currentVectorX === 0 ? Math.abs(nextLine.x1 - ridge.x1) : Math.abs(nextLine.y1 - ridge.y1) + otherRidgeLines.push({ ridge, size }) + }) + if (otherRidgeLines.length > 0) { + const otherRidge = otherRidgeLines.sort((a, b) => a.size - b.size)[0].ridge + /** + * otherRidge이 prevRidgeLine, nextRidgeLine 과 currentLine의 사이에 있는지 확인해서 분할하여 작업 + * 지붕의 덮힘이 다르기 때문 + */ + const isInside = + currentVectorX === 0 + ? Math.abs(currentLine.x1 - otherRidge.x1) < Math.abs(currentLine.x1 - prevLineRidge.x1) && + Math.abs(currentLine.x1 - otherRidge.x1) < Math.abs(currentLine.x1 - nextLineRidge.x1) + : Math.abs(currentLine.y1 - otherRidge.y1) < Math.abs(currentLine.y1 - prevLineRidge.y1) && + Math.abs(currentLine.y1 - otherRidge.y1) < Math.abs(currentLine.y1 - nextLineRidge.y1) + + if (isInside) { + polygonPoints.push( + { x: prevLineRidge.x1, y: prevLineRidge.y1 }, + { x: prevLineRidge.x2, y: prevLineRidge.y2 }, + { x: nextLineRidge.x1, y: nextLineRidge.y1 }, + { x: nextLineRidge.x2, y: nextLineRidge.y2 }, + ) + + let ridgeAllPoints = [ + { x: prevLineRidge.x1, y: prevLineRidge.y1 }, + { x: prevLineRidge.x2, y: prevLineRidge.y2 }, + { x: nextLineRidge.x1, y: nextLineRidge.y1 }, + { x: nextLineRidge.x2, y: nextLineRidge.y2 }, + ] + let ridgePoints = [] + ridgeAllPoints.forEach((point) => { + let isOnLine = false + roof.lines.forEach((line) => { + if (isPointOnLine({ x1: line.x1, y1: line.y1, x2: line.x2, y2: line.y2 }, point)) { + isOnLine = true + } + }) + if (!isOnLine) { + ridgePoints.push(point) } }) - if (!isOnLine) { - ridgePoints.push(point) + if (ridgePoints.length === 2) { + if (Math.sign(otherRidge.x1 - otherRidge.x2) === 0) { + polygonPoints.push( + { x: otherRidge.x1, y: ridgePoints[0].y }, + { + x: otherRidge.x1, + y: ridgePoints[1].y, + }, + ) + } else { + polygonPoints.push( + { x: ridgePoints[0].x, y: otherRidge.y1 }, + { + x: ridgePoints[1].x, + y: otherRidge.y1, + }, + ) + } } - }) - if (ridgePoints.length === 2) { + } else { + polygonPoints.push({ x: otherRidge.x1, y: otherRidge.y1 }, { x: otherRidge.x2, y: otherRidge.y2 }) + + let ridgePoints = [ + { x: prevLineRidge.x1, y: prevLineRidge.y1 }, + { x: prevLineRidge.x2, y: prevLineRidge.y2 }, + { x: nextLineRidge.x1, y: nextLineRidge.y1 }, + { x: nextLineRidge.x2, y: nextLineRidge.y2 }, + ] + + ridgePoints.forEach((point) => { + let isOnLine = false + roof.lines.forEach((line) => { + if (isPointOnLine({ x1: line.x1, y1: line.y1, x2: line.x2, y2: line.y2 }, point)) { + isOnLine = true + } + }) + if (isOnLine) { + polygonPoints.push(point) + } + }) + if (Math.sign(otherRidge.x1 - otherRidge.x2) === 0) { - polygonPoints.push({ x: otherRidge.x1, y: ridgePoints[0].y }, { x: otherRidge.x1, y: ridgePoints[1].y }) + const prevY = + (prevLineRidge.y1 <= otherRidge.y1 && otherRidge.y1 <= prevLineRidge.y2) || + (prevLineRidge.y1 >= otherRidge.y1 && otherRidge.y1 >= prevLineRidge.y2) + ? otherRidge.y1 + : otherRidge.y2 + const nextY = + (nextLineRidge.y1 <= otherRidge.y1 && otherRidge.y1 <= nextLineRidge.y2) || + (nextLineRidge.y1 >= otherRidge.y1 && otherRidge.y1 >= nextLineRidge.y2) + ? otherRidge.y1 + : otherRidge.y2 + polygonPoints.push({ x: prevLineRidge.x1, y: prevY }, { x: nextLineRidge.x1, y: nextY }) } else { - polygonPoints.push({ x: ridgePoints[0].x, y: otherRidge.y1 }, { x: ridgePoints[1].x, y: otherRidge.y1 }) + const prevX = + (prevLineRidge.x1 <= otherRidge.x1 && otherRidge.x1 <= prevLineRidge.x2) || + (prevLineRidge.x1 >= otherRidge.x1 && otherRidge.x1 >= prevLineRidge.x2) + ? otherRidge.x1 + : otherRidge.x2 + const nextX = + (nextLineRidge.x1 <= otherRidge.x1 && otherRidge.x1 <= nextLineRidge.x2) || + (nextLineRidge.x1 >= otherRidge.x1 && otherRidge.x1 >= nextLineRidge.x2) + ? otherRidge.x1 + : otherRidge.x2 + polygonPoints.push({ x: prevX, y: prevLineRidge.y1 }, { x: nextX, y: nextLineRidge.y1 }) } } - } else { - polygonPoints.push({ x: otherRidge.x1, y: otherRidge.y1 }, { x: otherRidge.x2, y: otherRidge.y2 }) + } + } + } + const sortedPolygonPoints = getSortedPoint(polygonPoints) - let ridgePoints = [ - { x: prevLineRidge.x1, y: prevLineRidge.y1 }, - { x: prevLineRidge.x2, y: prevLineRidge.y2 }, - { x: nextLineRidge.x1, y: nextLineRidge.y1 }, - { x: nextLineRidge.x2, y: nextLineRidge.y2 }, - ] + /** 외벽선 밖으로 나가있는 포인트*/ + const outsidePoints = polygonPoints.filter((point) => { + let isOutside = true + roof.lines.forEach((line) => { + if ( + (line.x1 <= point.x && line.x2 >= point.x && line.y1 <= point.y && line.y2 >= point.y) || + (line.x1 >= point.x && line.x2 <= point.x && line.y1 >= point.y && line.y2 <= point.y) + ) { + isOutside = false + } + }) + baseGableRidgeLines.forEach((line) => { + if ( + (line.x1 <= point.x && line.x2 >= point.x && line.y1 <= point.y && line.y2 >= point.y) || + (line.x1 >= point.x && line.x2 <= point.x && line.y1 >= point.y && line.y2 <= point.y) + ) { + isOutside = false + } + }) + return isOutside + }) - ridgePoints.forEach((point) => { - let isOnLine = false - roof.lines.forEach((line) => { - if (isPointOnLine({ x1: line.x1, y1: line.y1, x2: line.x2, y2: line.y2 }, point)) { - isOnLine = true + if (outsidePoints.length > 0) { + sortedPolygonPoints.forEach((currentPoint, index) => { + if (outsidePoints.includes(currentPoint)) { + const prevPoint = sortedPolygonPoints[(index - 1 + sortedPolygonPoints.length) % sortedPolygonPoints.length] + const nextPoint = sortedPolygonPoints[(index + 1) % sortedPolygonPoints.length] + const vectorX = Math.sign(currentPoint.x - prevPoint.x) + const vectorY = Math.sign(currentPoint.y - prevPoint.y) + + const checkEdge = { + vertex1: { x: prevPoint.x, y: prevPoint.y }, + vertex2: { x: currentPoint.x, y: currentPoint.y }, + } + const intersectPoints = [] + roof.lines + .filter((line) => (vectorX === 0 ? line.x1 !== line.x2 : line.y1 !== line.y2)) + .forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const is = edgesIntersection(checkEdge, lineEdge) + if (is && !is.isIntersectionOutside) { + const isVectorX = Math.sign(is.x - prevPoint.x) + const isVectorY = Math.sign(is.y - prevPoint.y) + if ((vectorX === 0 && vectorY === isVectorY) || (vectorY === 0 && vectorX === isVectorX)) { + intersectPoints.push(is) + } } }) - if (isOnLine) { - polygonPoints.push(point) + if (intersectPoints.length > 0) { + const intersection = intersectPoints[0] + if (vectorX === 0) { + currentPoint.y = intersection.y + nextPoint.y = intersection.y + } else { + currentPoint.x = intersection.x + nextPoint.x = intersection.x } - }) - - if (Math.sign(otherRidge.x1 - otherRidge.x2) === 0) { - const prevY = - (prevLineRidge.y1 <= otherRidge.y1 && otherRidge.y1 <= prevLineRidge.y2) || - (prevLineRidge.y1 >= otherRidge.y1 && otherRidge.y1 >= prevLineRidge.y2) - ? otherRidge.y1 - : otherRidge.y2 - const nextY = - (nextLineRidge.y1 <= otherRidge.y1 && otherRidge.y1 <= nextLineRidge.y2) || - (nextLineRidge.y1 >= otherRidge.y1 && otherRidge.y1 >= nextLineRidge.y2) - ? otherRidge.y1 - : otherRidge.y2 - polygonPoints.push({ x: prevLineRidge.x1, y: prevY }, { x: nextLineRidge.x1, y: nextY }) - } else { - const prevX = - (prevLineRidge.x1 <= otherRidge.x1 && otherRidge.x1 <= prevLineRidge.x2) || - (prevLineRidge.x1 >= otherRidge.x1 && otherRidge.x1 >= prevLineRidge.x2) - ? otherRidge.x1 - : otherRidge.x2 - const nextX = - (nextLineRidge.x1 <= otherRidge.x1 && otherRidge.x1 <= nextLineRidge.x2) || - (nextLineRidge.x1 >= otherRidge.x1 && otherRidge.x1 >= nextLineRidge.x2) - ? otherRidge.x1 - : otherRidge.x2 - polygonPoints.push({ x: prevX, y: prevLineRidge.y1 }, { x: nextX, y: nextLineRidge.y1 }) } } - } + }) } - } - const sortedPolygonPoints = getSortedPoint(polygonPoints) - - /** 외벽선 밖으로 나가있는 포인트*/ - const outsidePoints = polygonPoints.filter((point) => { - let isOutside = true - roof.lines.forEach((line) => { - if ( - (line.x1 <= point.x && line.x2 >= point.x && line.y1 <= point.y && line.y2 >= point.y) || - (line.x1 >= point.x && line.x2 <= point.x && line.y1 >= point.y && line.y2 <= point.y) - ) { - isOutside = false + sortedPolygonPoints.forEach((startPoint, index) => { + let endPoint + if (index === sortedPolygonPoints.length - 1) { + endPoint = sortedPolygonPoints[0] + } else { + endPoint = sortedPolygonPoints[index + 1] } - }) - baseGableRidgeLines.forEach((line) => { - if ( - (line.x1 <= point.x && line.x2 >= point.x && line.y1 <= point.y && line.y2 >= point.y) || - (line.x1 >= point.x && line.x2 <= point.x && line.y1 >= point.y && line.y2 <= point.y) - ) { - isOutside = false - } - }) - return isOutside - }) - if (outsidePoints.length > 0) { - sortedPolygonPoints.forEach((currentPoint, index) => { - if (outsidePoints.includes(currentPoint)) { - const prevPoint = sortedPolygonPoints[(index - 1 + sortedPolygonPoints.length) % sortedPolygonPoints.length] - const nextPoint = sortedPolygonPoints[(index + 1) % sortedPolygonPoints.length] - const vectorX = Math.sign(currentPoint.x - prevPoint.x) - const vectorY = Math.sign(currentPoint.y - prevPoint.y) - - const checkEdge = { vertex1: { x: prevPoint.x, y: prevPoint.y }, vertex2: { x: currentPoint.x, y: currentPoint.y } } - const intersectPoints = [] - roof.lines - .filter((line) => (vectorX === 0 ? line.x1 !== line.x2 : line.y1 !== line.y2)) - .forEach((line) => { - const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } - const is = edgesIntersection(checkEdge, lineEdge) - if (is && !is.isIntersectionOutside) { - const isVectorX = Math.sign(is.x - prevPoint.x) - const isVectorY = Math.sign(is.y - prevPoint.y) - if ((vectorX === 0 && vectorY === isVectorY) || (vectorY === 0 && vectorX === isVectorX)) { - intersectPoints.push(is) - } - } - }) - if (intersectPoints.length > 0) { - const intersection = intersectPoints[0] - if (vectorX === 0) { - currentPoint.y = intersection.y - nextPoint.y = intersection.y - } else { - currentPoint.x = intersection.x - nextPoint.x = intersection.x - } + const hipLine = drawHipLine([startPoint.x, startPoint.y, endPoint.x, endPoint.y], canvas, roof, textMode, null, currentDegree, currentDegree) + if (currentVectorX === 0) { + if (Math.sign(startPoint.x - endPoint.x) === 0) { + hipLine.attributes.actualSize = hipLine.attributes.planeSize + } + } else { + if (Math.sign(startPoint.y - endPoint.y) === 0) { + hipLine.attributes.actualSize = hipLine.attributes.planeSize } } + baseHipLines.push({ x1: hipLine.x1, y1: hipLine.y1, x2: hipLine.x2, y2: hipLine.y2, line: hipLine }) }) } - - sortedPolygonPoints.forEach((startPoint, index) => { - let endPoint - if (index === sortedPolygonPoints.length - 1) { - endPoint = sortedPolygonPoints[0] - } else { - endPoint = sortedPolygonPoints[index + 1] - } - - const hipLine = drawHipLine([startPoint.x, startPoint.y, endPoint.x, endPoint.y], canvas, roof, textMode, null, currentDegree, currentDegree) - if (currentVectorX === 0) { - if (Math.sign(startPoint.x - endPoint.x) === 0) { - hipLine.attributes.actualSize = hipLine.attributes.planeSize - } - } else { - if (Math.sign(startPoint.y - endPoint.y) === 0) { - hipLine.attributes.actualSize = hipLine.attributes.planeSize - } - } - baseHipLines.push({ x1: hipLine.x1, y1: hipLine.y1, x2: hipLine.x2, y2: hipLine.y2, line: hipLine }) - }) }) drawGablePolygonSecond.forEach((current) => { @@ -2441,28 +2816,10 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const prevLine = prevBaseLine.line const nextLine = nextBaseLine.line let { x1, x2, y1, y2 } = currentBaseLine - let prevLineRidges = [], - nextLineRidges = [] - - const checkLine = new fabric.Line([x1, y1, x2, y2], { - stroke: 'red', - strokeWidth: 4, - parentId: roofId, - name: 'checkLine', - }) - canvas.add(checkLine) - canvas.renderAll() const currentVectorX = Math.sign(currentLine.x2 - currentLine.x1) const currentVectorY = Math.sign(currentLine.y2 - currentLine.y1) - const prevVectorX = Math.sign(prevLine.x2 - prevLine.x1) - const prevVectorY = Math.sign(prevLine.y2 - prevLine.y1) - const nextVectorX = Math.sign(nextLine.x1 - nextLine.x2) - const nextVectorY = Math.sign(nextLine.y1 - nextLine.y2) const currentDegree = currentLine.attributes.pitch > 0 ? getDegreeByChon(currentLine.attributes.pitch) : currentLine.attributes.degree - const currentOffset = currentLine.attributes.offset - const prevOffset = prevLine.attributes.offset - const nextOffset = nextLine.attributes.offset const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint) const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint) @@ -2480,9 +2837,15 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { let checkEdge if (currentVectorX === 0) { - checkEdge = { vertex1: { x: currentRidge.x1, y: currentRidge.y1 }, vertex2: { x: currentLine.x1, y: currentRidge.y1 } } + checkEdge = { + vertex1: { x: currentRidge.x1, y: currentRidge.y1 }, + vertex2: { x: currentLine.x1, y: currentRidge.y1 }, + } } else { - checkEdge = { vertex1: { x: currentRidge.x1, y: currentRidge.y1 }, vertex2: { x: currentRidge.x1, y: currentLine.y1 } } + checkEdge = { + vertex1: { x: currentRidge.x1, y: currentRidge.y1 }, + vertex2: { x: currentRidge.x1, y: currentLine.y1 }, + } } const isRoofLines = [] roof.lines @@ -2642,6 +3005,274 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { }) }) + /** 케라바 지붕에 연결된 마루 중 처마라인이 그려지지 않은 경우 확*/ + baseGableRidgeLines.forEach((ridge) => { + const ridgeVectorX = Math.sign(ridge.x1 - ridge.x2) + const ridgeVectorY = Math.sign(ridge.y1 - ridge.y2) + + const firstGableLines = [] + const secondGableLines = [] + + baseGableLines + .filter((line) => (ridgeVectorX === 0 ? line.x1 !== line.x2 : line.y1 !== line.y2)) + .filter((line) => (line.x1 === ridge.x1 && line.y1 === ridge.y1) || (line.x2 === ridge.x1 && line.y2 === ridge.y1)) + .forEach((line) => firstGableLines.push(line)) + baseGableLines + .filter((line) => (ridgeVectorX === 0 ? line.x1 !== line.x2 : line.y1 !== line.y2)) + .filter((line) => (line.x1 === ridge.x2 && line.y1 === ridge.y2) || (line.x2 === ridge.x2 && line.y2 === ridge.y2)) + .forEach((line) => secondGableLines.push(line)) + + baseHipLines + .filter((line) => (ridgeVectorX === 0 ? line.x1 !== line.x2 : line.y1 !== line.y2)) + .filter((line) => (line.x1 === ridge.x1 && line.y1 === ridge.y1) || (line.x2 === ridge.x1 && line.y2 === ridge.y1)) + .forEach((line) => firstGableLines.push(line)) + baseHipLines + .filter((line) => (ridgeVectorX === 0 ? line.x1 !== line.x2 : line.y1 !== line.y2)) + .filter((line) => (line.x1 === ridge.x2 && line.y1 === ridge.y2) || (line.x2 === ridge.x2 && line.y2 === ridge.y2)) + .forEach((line) => secondGableLines.push(line)) + + let degree1, degree2 + if (firstGableLines.length < 2 || secondGableLines.length < 2) { + drawBaseLines.forEach((currentBaseLine, index) => { + let prevBaseLine = drawBaseLines[(index - 1 + drawBaseLines.length) % drawBaseLines.length] + let nextBaseLine = drawBaseLines[(index + 1) % drawBaseLines.length] + const currentLine = currentBaseLine.line + const prevLine = prevBaseLine.line + const nextLine = nextBaseLine.line + const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint) + const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint) + + if ( + gableType.includes(currentLine.attributes?.type) && + eavesType.includes(prevLine.attributes?.type) && + eavesType.includes(nextLine.attributes?.type) && + Big(prevAngle).minus(Big(nextAngle)).abs().eq(180) + ) { + if ( + ridgeVectorX === 0 && + currentLine.x1 !== currentLine.x2 && + ((currentLine.x1 <= ridge.x1 && ridge.x1 <= currentLine.x2) || (currentLine.x2 <= ridge.x1 && ridge.x1 <= currentLine.x1)) + ) { + degree1 = prevLine.attributes.pitch > 0 ? getDegreeByChon(prevLine.attributes.pitch) : prevLine.attributes.degree + degree2 = nextLine.attributes.pitch > 0 ? getDegreeByChon(nextLine.attributes.pitch) : nextLine.attributes.degree + } + + if ( + ridgeVectorY === 0 && + currentLine.y1 !== currentLine.y2 && + ((currentLine.y1 <= ridge.y1 && ridge.y1 <= currentLine.y2) || (currentLine.y2 <= ridge.y1 && ridge.y1 <= currentLine.y1)) + ) { + degree1 = prevLine.attributes.pitch > 0 ? getDegreeByChon(prevLine.attributes.pitch) : prevLine.attributes.degree + degree2 = nextLine.attributes.pitch > 0 ? getDegreeByChon(nextLine.attributes.pitch) : nextLine.attributes.degree + } + } + }) + } + + if (firstGableLines.length < 2) { + const connectRoof = roof.lines.find( + (line) => + (line.x1 <= ridge.x1 && line.x2 >= ridge.x1 && line.y1 <= ridge.y1 && line.y2 >= ridge.y1) || + (line.x2 <= ridge.x1 && line.x1 >= ridge.x1 && line.y2 <= ridge.y1 && line.y1 >= ridge.y1), + ) + if (connectRoof) { + let hipPoint1 = [connectRoof.x1, connectRoof.y1, ridge.x1, ridge.y1] + let hipPoint2 = [connectRoof.x2, connectRoof.y2, ridge.x1, ridge.y1] + let intersectPoints1 + let intersectPoints2 + baseHipLines + .filter((line) => line.x1 !== line.x2 && line.y1 !== line.y2) + .forEach((hip) => { + const line = hip.line + if ( + (hipPoint1[0] <= line.x1 && line.x1 <= hipPoint1[2] && hipPoint1[1] <= line.y1 && line.y1 <= hipPoint1[3]) || + (hipPoint1[2] <= line.x1 && line.x1 <= hipPoint1[0] && hipPoint1[3] <= line.y1 && line.y1 <= hipPoint1[1]) + ) { + intersectPoints1 = { x: line.x1, y: line.y1 } + } + if ( + (hipPoint1[0] <= line.x2 && line.x2 <= hipPoint1[2] && hipPoint1[1] <= line.y2 && line.y2 <= hipPoint1[3]) || + (hipPoint1[2] <= line.x2 && line.x2 <= hipPoint1[0] && hipPoint1[3] <= line.y2 && line.y2 <= hipPoint1[1]) + ) { + intersectPoints1 = { x: line.x2, y: line.y2 } + } + if ( + (hipPoint2[0] <= line.x1 && line.x1 <= hipPoint2[2] && hipPoint2[1] <= line.y1 && line.y1 <= hipPoint2[3]) || + (hipPoint2[2] <= line.x1 && line.x1 <= hipPoint2[0] && hipPoint2[3] <= line.y1 && line.y1 <= hipPoint2[1]) + ) { + intersectPoints2 = { x: line.x1, y: line.y1 } + } + if ( + (hipPoint2[0] <= line.x2 && line.x2 <= hipPoint2[2] && hipPoint2[1] <= line.y2 && line.y2 <= hipPoint2[3]) || + (hipPoint2[2] <= line.x2 && line.x2 <= hipPoint2[0] && hipPoint2[3] <= line.y2 && line.y2 <= hipPoint2[1]) + ) { + intersectPoints2 = { x: line.x2, y: line.y2 } + } + }) + if (intersectPoints1) { + hipPoint1 = [intersectPoints1.x, intersectPoints1.y, ridge.x1, ridge.y1] + } + if (intersectPoints2) { + hipPoint2 = [intersectPoints2.x, intersectPoints2.y, ridge.x1, ridge.y1] + } + + if (hipPoint1) { + let alreadyHip = false + baseHipLines + .filter( + (line) => + (line.line.x1 === hipPoint1[0] && line.line.y1 === hipPoint1[1] && line.line.x2 === hipPoint1[2] && line.line.y2 === hipPoint1[3]) || + (line.line.x2 === hipPoint1[0] && line.line.y2 === hipPoint1[1] && line.line.x1 === hipPoint1[2] && line.line.y1 === hipPoint1[3]), + ) + .forEach((line) => { + alreadyHip = true + }) + baseGableLines + .filter( + (line) => + (line.x1 === hipPoint1[0] && line.y1 === hipPoint1[1] && line.x2 === hipPoint1[2] && line.y2 === hipPoint1[3]) || + (line.x2 === hipPoint1[0] && line.y2 === hipPoint1[1] && line.x1 === hipPoint1[2] && line.y1 === hipPoint1[3]), + ) + .forEach((line) => { + alreadyHip = true + }) + + if (!alreadyHip) { + const hipLine1 = drawHipLine(hipPoint1, canvas, roof, textMode, null, degree1, degree1) + baseHipLines.push({ x1: hipLine1.x1, y1: hipLine1.y1, x2: hipLine1.x2, y2: hipLine1.y2, line: hipLine1 }) + } + } + if (hipPoint2) { + let alreadyHip = false + baseHipLines + .filter( + (line) => + (line.line.x1 === hipPoint2[0] && line.line.y1 === hipPoint2[1] && line.line.x2 === hipPoint2[2] && line.line.y2 === hipPoint2[3]) || + (line.line.x2 === hipPoint2[0] && line.line.y2 === hipPoint2[1] && line.line.x1 === hipPoint2[2] && line.line.y1 === hipPoint2[3]), + ) + .forEach((line) => { + alreadyHip = true + }) + baseGableLines + .filter( + (line) => + (line.x1 === hipPoint2[0] && line.y1 === hipPoint2[1] && line.x2 === hipPoint2[2] && line.y2 === hipPoint2[3]) || + (line.x2 === hipPoint2[0] && line.y2 === hipPoint2[1] && line.x1 === hipPoint2[2] && line.y1 === hipPoint2[3]), + ) + .forEach((line) => { + alreadyHip = true + }) + if (!alreadyHip) { + const hipLine2 = drawHipLine(hipPoint2, canvas, roof, textMode, null, degree2, degree2) + baseHipLines.push({ x1: hipLine2.x1, y1: hipLine2.y1, x2: hipLine2.x2, y2: hipLine2.y2, line: hipLine2 }) + } + } + } + } + if (secondGableLines.length < 2) { + const connectRoof = roof.lines.find( + (line) => + (line.x1 <= ridge.x2 && line.x2 >= ridge.x2 && line.y1 <= ridge.y2 && line.y2 >= ridge.y2) || + (line.x2 <= ridge.x2 && line.x1 >= ridge.x2 && line.y2 <= ridge.y2 && line.y1 >= ridge.y2), + ) + if (connectRoof) { + let hipPoint1 = [connectRoof.x1, connectRoof.y1, ridge.x2, ridge.y2] + let hipPoint2 = [connectRoof.x2, connectRoof.y2, ridge.x2, ridge.y2] + let intersectPoints1 + let intersectPoints2 + + baseHipLines + .filter((line) => line.x1 !== line.x2 && line.y1 !== line.y2) + .forEach((hip) => { + const line = hip.line + if ( + (hipPoint1[0] <= line.x1 && line.x1 <= hipPoint1[2] && hipPoint1[1] <= line.y1 && line.y1 <= hipPoint1[3]) || + (hipPoint1[2] <= line.x1 && line.x1 <= hipPoint1[0] && hipPoint1[3] <= line.y1 && line.y1 <= hipPoint1[1]) + ) { + intersectPoints1 = { x: line.x1, y: line.y1 } + } + if ( + (hipPoint1[0] <= line.x2 && line.x2 <= hipPoint1[2] && hipPoint1[1] <= line.y2 && line.y2 <= hipPoint1[3]) || + (hipPoint1[2] <= line.x2 && line.x2 <= hipPoint1[0] && hipPoint1[3] <= line.y2 && line.y2 <= hipPoint1[1]) + ) { + intersectPoints1 = { x: line.x2, y: line.y2 } + } + if ( + (hipPoint2[0] <= line.x1 && line.x1 <= hipPoint2[2] && hipPoint2[1] <= line.y1 && line.y1 <= hipPoint2[3]) || + (hipPoint2[2] <= line.x1 && line.x1 <= hipPoint2[0] && hipPoint2[3] <= line.y1 && line.y1 <= hipPoint2[1]) + ) { + intersectPoints2 = { x: line.x1, y: line.y1 } + } + if ( + (hipPoint2[0] <= line.x2 && line.x2 <= hipPoint2[2] && hipPoint2[1] <= line.y2 && line.y2 <= hipPoint2[3]) || + (hipPoint2[2] <= line.x2 && line.x2 <= hipPoint2[0] && hipPoint2[3] <= line.y2 && line.y2 <= hipPoint2[1]) + ) { + intersectPoints2 = { x: line.x2, y: line.y2 } + } + }) + + if (intersectPoints1) { + hipPoint1 = [intersectPoints1.x, intersectPoints1.y, ridge.x2, ridge.y2] + } + if (intersectPoints2) { + hipPoint2 = [intersectPoints2.x, intersectPoints2.y, ridge.x2, ridge.y2] + } + + if (hipPoint1) { + let alreadyHip = false + baseHipLines + .filter( + (line) => + (line.line.x1 === hipPoint1[0] && line.line.y1 === hipPoint1[1] && line.line.x2 === hipPoint1[2] && line.line.y2 === hipPoint1[3]) || + (line.line.x2 === hipPoint1[0] && line.line.y2 === hipPoint1[1] && line.line.x1 === hipPoint1[2] && line.line.y1 === hipPoint1[3]), + ) + .forEach((line) => { + alreadyHip = true + }) + baseGableLines + .filter( + (line) => + (line.x1 === hipPoint1[0] && line.y1 === hipPoint1[1] && line.x2 === hipPoint1[2] && line.y2 === hipPoint1[3]) || + (line.x2 === hipPoint1[0] && line.y2 === hipPoint1[1] && line.x1 === hipPoint1[2] && line.y1 === hipPoint1[3]), + ) + .forEach((line) => { + alreadyHip = true + }) + + if (!alreadyHip) { + const hipLine1 = drawHipLine(hipPoint1, canvas, roof, textMode, null, degree1, degree1) + baseHipLines.push({ x1: hipLine1.x1, y1: hipLine1.y1, x2: hipLine1.x2, y2: hipLine1.y2, line: hipLine1 }) + } + } + if (hipPoint2) { + let alreadyHip = false + baseHipLines + .filter( + (line) => + (line.line.x1 === hipPoint2[0] && line.line.y1 === hipPoint2[1] && line.line.x2 === hipPoint2[2] && line.line.y2 === hipPoint2[3]) || + (line.line.x2 === hipPoint2[0] && line.line.y2 === hipPoint2[1] && line.line.x1 === hipPoint2[2] && line.line.y1 === hipPoint2[3]), + ) + .forEach((line) => { + alreadyHip = true + }) + baseGableLines + .filter( + (line) => + (line.x1 === hipPoint2[0] && line.y1 === hipPoint2[1] && line.x2 === hipPoint2[2] && line.y2 === hipPoint2[3]) || + (line.x2 === hipPoint2[0] && line.y2 === hipPoint2[1] && line.x1 === hipPoint2[2] && line.y1 === hipPoint2[3]), + ) + .forEach((line) => { + alreadyHip = true + }) + if (!alreadyHip) { + const hipLine2 = drawHipLine(hipPoint2, canvas, roof, textMode, null, degree2, degree2) + baseHipLines.push({ x1: hipLine2.x1, y1: hipLine2.y1, x2: hipLine2.x2, y2: hipLine2.y2, line: hipLine2 }) + } + } + } + } + }) + /** ⨆ 모양 처마에 추녀마루를 그린다. */ drawEavesFirstLines.forEach((current) => { // 확인용 라인, 포인트 제거 @@ -2743,7 +3374,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { hipLength = currentSize.div(2).pow(2).plus(currentSize.div(2).pow(2)).sqrt() } else { if (currentAngle !== beforePrevAngle && currentAngle !== afterNextAngle && beforePrevLine !== afterNextLine) { - console.log('4각 아님') const beforePrevX1 = beforePrevLine.x1, beforePrevY1 = beforePrevLine.y1, beforePrevX2 = beforePrevLine.x2, @@ -2899,7 +3529,10 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const intersectPoints = [] rightAngleLine.forEach((line) => { const intersection = edgesIntersection( - { vertex1: { x: checkHipPoints[0], y: checkHipPoints[1] }, vertex2: { x: checkHipPoints[2], y: checkHipPoints[3] } }, + { + vertex1: { x: checkHipPoints[0], y: checkHipPoints[1] }, + vertex2: { x: checkHipPoints[2], y: checkHipPoints[3] }, + }, { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }, ) if (intersection) { @@ -2916,7 +3549,10 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { oppositeCurrentLine.forEach((line) => { const intersection = edgesIntersection( - { vertex1: { x: checkHipPoints[0], y: checkHipPoints[1] }, vertex2: { x: checkHipPoints[2], y: checkHipPoints[3] } }, + { + vertex1: { x: checkHipPoints[0], y: checkHipPoints[1] }, + vertex2: { x: checkHipPoints[2], y: checkHipPoints[3] }, + }, { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }, ) if (intersection) { @@ -3014,12 +3650,8 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { return prev.size < current.size ? prev : current }, intersectPoints[0]) - // console.log('intersectPoints', intersectPoints) - /** 겹치는 외벽선이 있을때 추녀마루를 외벽선 까지 늘려서 수정*/ if (intersectPoints && intersectPoints.intersection) { - console.log('prevDegree', prevDegree, 'currentDegree', currentDegree) - prevHipLine = drawHipLine( [intersectPoints.intersection.x, intersectPoints.intersection.y, prevEndPoint.x.toNumber(), prevEndPoint.y.toNumber()], canvas, @@ -3112,30 +3744,28 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { prevDegree, currentDegree, ) - baseHipLines.push({ x1: x2, y1: y2, x2: nextEndPoint.x.toNumber(), y2: nextEndPoint.y.toNumber(), line: nextHipLine }) + baseHipLines.push({ + x1: x2, + y1: y2, + x2: nextEndPoint.x.toNumber(), + y2: nextEndPoint.y.toNumber(), + line: nextHipLine, + }) } } - console.log('prevHipLine : ', prevHipLine) - console.log('nextHipLine : ', nextHipLine) /** 두 선이 교차하면 해당 포인트까지로 선 조정*/ if (prevHipLine !== undefined && nextHipLine !== undefined) { - const prevEdge = { vertex1: { x: prevHipLine.x1, y: prevHipLine.y1 }, vertex2: { x: prevHipLine.x2, y: prevHipLine.y2 } } - const nextEdge = { vertex1: { x: nextHipLine.x1, y: nextHipLine.y1 }, vertex2: { x: nextHipLine.x2, y: nextHipLine.y2 } } + const prevEdge = { + vertex1: { x: prevHipLine.x1, y: prevHipLine.y1 }, + vertex2: { x: prevHipLine.x2, y: prevHipLine.y2 }, + } + const nextEdge = { + vertex1: { x: nextHipLine.x1, y: nextHipLine.y1 }, + vertex2: { x: nextHipLine.x2, y: nextHipLine.y2 }, + } const intersection = edgesIntersection(prevEdge, nextEdge) - console.log('intersection : ', intersection) if (intersection) { - const checkPoint = new fabric.Circle({ - left: intersection.x, - top: intersection.y, - radius: 4, - fill: 'yellow', - parentId: roofId, - name: 'checkPoint', - }) - canvas.add(checkPoint) - canvas.renderAll() - /** 포인트 조정*/ baseHipLines .filter((line) => line.line === prevHipLine || line.line === nextHipLine) @@ -3179,12 +3809,9 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const xVector = Big(currentMidX).minus(Big(startPoint.x)).round(0, Big.roundDown) const yVector = Big(currentMidY).minus(Big(startPoint.y)).round(0, Big.roundDown) - console.log('beforePrevLine', beforePrevLine) if (gableType.includes(beforePrevLine.line.attributes.type) || gableType.includes(afterNextLine.line.attributes.type)) { - console.log('박공지붕 확인 후 마루 작성 ') const oppositeLine = gableType.includes(beforePrevLine.line.attributes.type) ? beforePrevLine.line : afterNextLine.line const oppositeAngle = calculateAngle(oppositeLine.startPoint, oppositeLine.endPoint) - console.log('currentAngle', currentAngle, 'oppositeAngle', oppositeAngle) let checkEdge if (Math.sign(oppositeLine.x1 - oppositeLine.x2) === 0) { checkEdge = { vertex1: startPoint, vertex2: { x: oppositeLine.x1, y: startPoint.y } } @@ -3192,9 +3819,11 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { checkEdge = { vertex1: startPoint, vertex2: { x: startPoint.x, y: oppositeLine.y1 } } } if (currentAngle === oppositeAngle) { - const oppositeEdge = { vertex1: { x: oppositeLine.x1, y: oppositeLine.y1 }, vertex2: { x: oppositeLine.x2, y: oppositeLine.y2 } } + const oppositeEdge = { + vertex1: { x: oppositeLine.x1, y: oppositeLine.y1 }, + vertex2: { x: oppositeLine.x2, y: oppositeLine.y2 }, + } const intersection = edgesIntersection(oppositeEdge, checkEdge) - console.log('intersection', intersection) if (intersection) { ridgeSize = Big(intersection.x) .minus(Big(startPoint.x)) @@ -3202,7 +3831,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { .plus(Big(intersection.y).minus(Big(startPoint.y)).pow(2)) .sqrt() } - console.log('aaa') } else { const intersectPoints = [] roof.lines @@ -3214,18 +3842,7 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { .forEach((line) => { const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } const intersection = edgesIntersection(lineEdge, checkEdge) - console.log('intersection', intersection) if (intersection) { - const checkPoint = new fabric.Circle({ - left: intersection.x, - top: intersection.y, - radius: 4, - fill: 'red', - parentId: roofId, - name: 'checkPoint', - }) - canvas.add(checkPoint) - canvas.renderAll() const size = Big(startPoint.x) .minus(Big(intersection.x)) .pow(2) @@ -3236,15 +3853,12 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } }) intersectPoints.sort((a, b) => a.size - b.size) - console.log('intersectPoints', intersectPoints) if (intersectPoints.length > 0) { ridgeSize = Big(intersectPoints[0].size) } - console.log('bbb') } } else { - console.log('마루 작성') - // baseLines에서 가장 작은 x1과 가장 큰 x1, 가장 작은 y1과 가장 큰 y1을 계산 + /** baseLines에서 가장 작은 x1과 가장 큰 x1, 가장 작은 y1과 가장 큰 y1을 계산*/ let minX = Infinity let maxX = -Infinity let minY = Infinity @@ -3363,7 +3977,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { ridgeSize = Big(Math.min(oppositeSize, lineMinSize)) } - console.log('ridgeSize', ridgeSize) if (ridgeSize.gt(0) && baseRidgeCount < getMaxRidge(baseLines.length)) { const points = [ startPoint.x, @@ -3508,7 +4121,13 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const intersectRidge = intersections[0].ridge const oppositeRoofPoints = [ridgeLine.x2, ridgeLine.y2, intersection.x, intersection.y] const oppositeRoofLine = drawHipLine(oppositeRoofPoints, canvas, roof, textMode, null, secondDegree, secondDegree) - baseHipLines.push({ x1: oppositeLine.x1, y1: oppositeLine.y1, x2: oppositeLine.x2, y2: oppositeLine.y2, line: oppositeRoofLine }) + baseHipLines.push({ + x1: oppositeLine.x1, + y1: oppositeLine.y1, + x2: oppositeLine.x2, + y2: oppositeLine.y2, + line: oppositeRoofLine, + }) const ridgeVector = Math.sign(ridgeLine.y1 - ridgeLine.y2) const connectRoofPoints = [oppositeRoofLine.x2, oppositeRoofLine.y2] @@ -3611,7 +4230,13 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const intersectRidge = intersections[0].ridge const oppositeRoofPoints = [ridgeLine.x2, ridgeLine.y2, intersection.x, intersection.y] const oppositeRoofLine = drawHipLine(oppositeRoofPoints, canvas, roof, textMode, null, secondDegree, secondDegree) - baseHipLines.push({ x1: oppositeLine.x1, y1: oppositeLine.y1, x2: oppositeLine.x2, y2: oppositeLine.y2, line: oppositeRoofLine }) + baseHipLines.push({ + x1: oppositeLine.x1, + y1: oppositeLine.y1, + x2: oppositeLine.x2, + y2: oppositeLine.y2, + line: oppositeRoofLine, + }) const ridgeVector = Math.sign(ridgeLine.x1 - ridgeLine.x2) const connectRoofPoints = [oppositeRoofLine.x2, oppositeRoofLine.y2] @@ -3634,12 +4259,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } } } - - canvas - .getObjects() - .filter((obj) => obj.name === 'checkPoint' || obj.name === 'checkLine') - .forEach((obj) => canvas.remove(obj)) - canvas.renderAll() }) /** 중복제거 */ @@ -3667,7 +4286,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { }) }) - // console.log('drawEavesSecondLines : ', drawEavesSecondLines) /** ㄴ 모양 처마에 추녀마루를 그린다. */ drawEavesSecondLines.forEach((current) => { const { currentBaseLine, prevBaseLine, nextBaseLine } = current @@ -3676,31 +4294,11 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const nextLine = nextBaseLine.line let { x1, x2, y1, y2, size } = currentBaseLine - // const checkLine = new fabric.Line([x1, y1, x2, y2], { - // stroke: 'yellow', - // strokeWidth: 4, - // parentId: roof.id, - // name: 'checkLine', - // }) - // canvas.add(checkLine) - // canvas.renderAll() - // checkLine.bringToFront() - /** 이전 라인의 경사 */ const prevDegree = prevLine.attributes.pitch > 0 ? getDegreeByChon(prevLine.attributes.pitch) : prevLine.attributes.degree /** 다음 라인의 경사 */ const currentDegree = currentLine.attributes.pitch > 0 ? getDegreeByChon(currentLine.attributes.pitch) : currentLine.attributes.degree - //라인 확인용 - /*const checkCurrentLine = new fabric.Line([x1, y1, x2, y2], { - stroke: 'red', - strokeWidth: 4, - parentId: roof.id, - }) - canvas.add(checkCurrentLine) - canvas.renderAll()*/ - // console.log('currentLine : ', currentLine.attributes.planeSize) - /** 이전, 다음라인의 사잇각의 vector를 구한다. */ let prevVector = getHalfAngleVector(prevLine, currentLine) let nextVector = getHalfAngleVector(currentLine, nextLine) @@ -3710,8 +4308,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { /** 각 라인의 흐름 방향을 확인한다. */ const currentAngle = calculateAngle(currentLine.startPoint, currentLine.endPoint) - /*const beforePrevAngle = calculateAngle(beforePrevLine.startPoint, beforePrevLine.endPoint) - const afterNextAngle = calculateAngle(afterNextLine.startPoint, afterNextLine.endPoint)*/ /** 현재 라인의 길이 추녀마루 길이의 기준이 된다. */ let hipLength = Big(x2) @@ -3719,8 +4315,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { .plus(Big(y2).minus(Big(y1))) .abs() - // console.log('currentLine : ', currentLine.attributes.planeSize) - /** 이전 라인과의 사이 추녀마루의 각도를 확인한다, 각도가 지붕안쪽으로 향하지 않을때 반대로 처리한다.*/ const prevCheckPoint = { x: Big(x1).plus(Big(prevHipVector.x).times(10)), @@ -3741,7 +4335,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { let prevHipLine, nextHipLine /** 이전라인과의 연결지점에 추녀마루를 그린다. */ - // console.log('이전라인 : ', baseHipLines.filter((line) => line.x1 === x1 && line.y1 === y1).length === 0) if (baseHipLines.filter((line) => line.x1 === x1 && line.y1 === y1).length === 0 && eavesType.includes(prevLine.attributes.type)) { let prevEndPoint = { x: Big(x1).plus(Big(prevHipVector.x).times(hipLength)).round(2), @@ -3750,8 +4343,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const prevEndEdge = { vertex1: { x: x1, y: y1 }, vertex2: prevEndPoint } - // console.log('prevHipVector : ', prevHipVector.x.toNumber(), prevHipVector.x.s, prevHipVector.y.toNumber(), prevHipVector.y.s) - const intersectBaseLine = [] baseLines .filter((line) => line !== currentLine && line !== prevLine && line !== nextLine) @@ -3763,20 +4354,10 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } }) .forEach((line) => { - // console.log('line : ', line.attributes.planeSize) const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } const intersection = edgesIntersection(prevEndEdge, lineEdge) if (intersection && Big(intersection.x - x1).s === nextHipVector.x.s && Big(intersection.y - y1).s === nextHipVector.y.s) { - /* const circle = new fabric.Circle({ - left: intersection.x - 2, - top: intersection.y - 2, - radius: 4, - fill: 'red', - parentId: roof.id, - }) - canvas.add(circle) - canvas.renderAll()*/ const size = Big(intersection.x - x1) .abs() .pow(2) @@ -3816,27 +4397,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { { vertex1: { x: x1, y: y1 }, vertex2: prevEndPoint }, { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }, ) - /*const checkLine = new fabric.Line([x1, y1, prevEndPoint.x, prevEndPoint.y], { - stroke: 'red', - strokeWidth: 2, - parentId: roof.id, - name: 'checkLine', - }) - canvas.add(checkLine) - canvas.renderAll()*/ - - /* if (intersection) { - const intersectCircle = new fabric.Circle({ - left: intersection.x - 2, - top: intersection.y - 2, - radius: 4, - fill: 'red', - parentId: roof.id, - name: 'checkPoint', - }) - canvas.add(intersectCircle) - canvas.renderAll() - }*/ if (intersection && !intersection.isIntersectionOutside) { intersectRidgeLine.push({ intersection, @@ -3869,15 +4429,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { vertex2: prevEndPoint, } - /* const checkLine = new fabric.Line([hipEdge.vertex1.x, hipEdge.vertex1.y, hipEdge.vertex2.x, hipEdge.vertex2.y], { - stroke: 'yellow', - strokeWidth: 2, - parentId: roof.id, - name: 'checkLine', - }) - canvas.add(checkLine) - canvas.renderAll()*/ - let intersectPoints = [] /** 외벽선에서 추녀 마루가 겹치는 경우에 대한 확인*/ roof.lines.forEach((line) => { @@ -3889,16 +4440,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { Math.sign(prevEndPoint.x - x1) === Math.sign(prevEndPoint.x - intersection.x) && Math.sign(prevEndPoint.y - y1) === Math.sign(prevEndPoint.y - intersection.y) ) { - /*const intersectPoint = new fabric.Circle({ - left: intersection.x - 2, - top: intersection.y - 2, - radius: 4, - fill: 'cyan', - parentId: roof.id, - }) - canvas.add(intersectPoint) - canvas.renderAll()*/ - const intersectSize = prevEndPoint.x .minus(Big(intersection.x)) .abs() @@ -3918,8 +4459,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { return prev.size < current.size ? prev : current }, intersectPoints[0]) - // console.log('intersectPoints', intersectPoints) - /** 겹치는 외벽선이 있을때 추녀마루를 외벽선 까지 늘려서 수정*/ if (intersectPoints && intersectPoints.intersection) { prevHipLine = drawHipLine( @@ -3935,21 +4474,12 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } } /** 다음라인과의 연결지점에 추녀마루를 그린다. */ - // console.log('다음라인 : ', baseHipLines.filter((line) => line.x1 === x2 && line.y1 === y2).length === 0) if (baseHipLines.filter((line) => line.x1 === x2 && line.y1 === y2).length === 0 && eavesType.includes(nextLine.attributes.type)) { let nextEndPoint = { x: Big(x2).plus(Big(nextHipVector.x).times(hipLength)).round(2), y: Big(y2).plus(Big(nextHipVector.y).times(hipLength)).round(2), } - /*const nextEndLine = new fabric.Line([x2, y2, nextEndPoint.x, nextEndPoint.y], { - stroke: 'red', - strokeWidth: 2, - parentId: roof.id, - }) - canvas.add(nextEndLine) - canvas.renderAll()*/ - const nextEndEdge = { vertex1: { x: x2, y: y2 }, vertex2: nextEndPoint, @@ -3966,21 +4496,10 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } }) .forEach((line) => { - // console.log('line : ', line.attributes.planeSize) const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } const intersection = edgesIntersection(nextEndEdge, lineEdge) if (intersection && Big(intersection.x - x2).s === nextHipVector.x.s && Big(intersection.y - y2).s === nextHipVector.y.s) { - /*console.log('next intersection ============') - const circle = new fabric.Circle({ - left: intersection.x - 2, - top: intersection.y - 2, - radius: 4, - fill: 'red', - parentId: roof.id, - }) - canvas.add(circle) - canvas.renderAll()*/ const size = Big(intersection.x - x2) .abs() .pow(2) @@ -4016,8 +4535,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const intersectRidgeLine = [] baseRidgeLines.forEach((line) => { - // console.log('nextEndPoint : ', nextEndPoint.x.toNumber(), nextEndPoint.y.toNumber()) - // console.log('intersection : ', 'x : ', line.x1, 'y : ', line.y1, 'x : ', line.x2, 'y : ', line.y2) const intersection = edgesIntersection( { vertex1: { x: x2, y: y2 }, vertex2: nextEndPoint }, { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }, @@ -4035,7 +4552,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } }) const intersectRidge = intersectRidgeLine.reduce((prev, current) => (prev.distance < current.distance ? prev : current), intersectRidgeLine[0]) - // console.log('intersectRidge : ', intersectRidge) if (intersectRidge) { nextEndPoint.x = Big(intersectRidge.intersection.x).round(1) @@ -4052,38 +4568,16 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { vertex2: nextEndPoint, } - /* const checkLine = new fabric.Line([hipEdge.vertex1.x, hipEdge.vertex1.y, hipEdge.vertex2.x, hipEdge.vertex2.y], { - stroke: 'yellow', - strokeWidth: 2, - parentId: roof.id, - }) - canvas.add(checkLine) - canvas.renderAll()*/ - let intersectPoints = [] roof.lines.forEach((line) => { const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } const intersection = edgesIntersection(hipEdge, lineEdge) - // console.log('intersection', intersection) if ( intersection && !intersection.isIntersectionOutside && Math.sign(nextEndPoint.x - x2) === Math.sign(nextEndPoint.x - intersection.x) && Math.sign(nextEndPoint.y - y2) === Math.sign(nextEndPoint.y - intersection.y) ) { - /*const intersectPoint = new fabric.Circle({ - left: intersection.x - 2, - top: intersection.y - 2, - radius: 4, - fill: 'blue', - parentId: roof.id, - }) - canvas.add(intersectPoint) - canvas.renderAll()*/ - - // console.log('nextEndPoint', nextEndPoint.x.toNumber(), nextEndPoint.y.toNumber()) - // console.log('intersection', intersection.x, intersection.y) - const intersectSize = nextEndPoint.x .minus(Big(intersection.x)) .pow(2) @@ -4103,8 +4597,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { return prev.size < current.size ? prev : current }, intersectPoints[0]) - // console.log('intersectPoints : ', intersectPoints) - if (intersectPoints && intersectPoints.intersection) { nextHipLine = drawHipLine( [intersectPoints.intersection.x, intersectPoints.intersection.y, nextEndPoint.x.toNumber(), nextEndPoint.y.toNumber()], @@ -4115,7 +4607,13 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { prevDegree, currentDegree, ) - baseHipLines.push({ x1: x2, y1: y2, x2: nextEndPoint.x.toNumber(), y2: nextEndPoint.y.toNumber(), line: nextHipLine }) + baseHipLines.push({ + x1: x2, + y1: y2, + x2: nextEndPoint.x.toNumber(), + y2: nextEndPoint.y.toNumber(), + line: nextHipLine, + }) } } }) @@ -4134,7 +4632,10 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const hipLineEdge = { vertex1: { x: hipLine.x1, y: hipLine.y1 }, vertex2: { x: hipLine.x2, y: hipLine.y2 } } baseRidgeLines.forEach((ridgeLine) => { - const ridgeLineEdge = { vertex1: { x: ridgeLine.x1, y: ridgeLine.y1 }, vertex2: { x: ridgeLine.x2, y: ridgeLine.y2 } } + const ridgeLineEdge = { + vertex1: { x: ridgeLine.x1, y: ridgeLine.y1 }, + vertex2: { x: ridgeLine.x2, y: ridgeLine.y2 }, + } const intersection = edgesIntersection(hipLineEdge, ridgeLineEdge) if (intersection) { @@ -4171,7 +4672,12 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const theta = Big(Math.acos(Big(oldPlaneSize).div(oldActualSize))) .times(180) .div(Math.PI) - const planeSize = calcLinePlaneSize({ x1: hipLine.line.x1, y1: hipLine.line.y1, x2: hipLine.line.x2, y2: hipLine.line.y2 }) + const planeSize = calcLinePlaneSize({ + x1: hipLine.line.x1, + y1: hipLine.line.y1, + x2: hipLine.line.x2, + y2: hipLine.line.y2, + }) hipLine.x2 = intersectRidgePoints[0].x hipLine.y2 = intersectRidgePoints[0].y hipLine.line.set({ x2: intersectRidgePoints[0].x, y2: intersectRidgePoints[0].y }) @@ -4182,8 +4688,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } }) - console.log('baseHipLines : ', baseHipLines) - console.log('baseRidgeLines : ', baseRidgeLines) /** 지붕선에 맞닫지 않은 부분을 확인하여 처리 한다.*/ /** 1. 그려진 마루 중 추녀마루가 부재하는 경우를 판단. 부재는 연결된 라인이 홀수인 경우로 판단한다.*/ let unFinishedRidge = [] @@ -4300,8 +4804,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { prevDegree = degreeAllLine[1] } - console.log('unFinishedRidge : ', unFinishedRidge) - console.log('unFinishedPoint : ', unFinishedPoint) /** 라인이 부재한 마루에 라인을 찾아 그린다.*/ unFinishedRidge.forEach((current) => { let checkPoints = [] @@ -4351,7 +4853,13 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { baseHipLines.forEach((line) => { const checkAngel1 = calculateAngle({ x: point[0], y: point[1] }, { x: point[2], y: point[3] }) - const checkAngel2 = calculateAngle({ x: line.line.x1, y: line.line.y1 }, { x: line.line.x2, y: line.line.y2 }) + const checkAngel2 = calculateAngle( + { x: line.line.x1, y: line.line.y1 }, + { + x: line.line.x2, + y: line.line.y2, + }, + ) const isConnectLine = ((line.line.x1 === point[0] && line.line.y1 === point[1]) || (line.line.x2 === point[0] && line.line.y2 === point[1])) && @@ -4378,7 +4886,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { .times(180) .div(Math.PI) .round(1) - console.log('theta : ', theta.toNumber()) prevDegree = theta.toNumber() currentDegree = theta.toNumber() canvas.remove(line.line) @@ -4458,15 +4965,25 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { basePoints.sort((a, b) => a.line.attributes.planeSize - b.line.attributes.planeSize) hipSize = Big(basePoints[0].line.attributes.planeSize) } - hipSize = hipSize.pow(2).div(2).sqrt().round().div(10).toNumber() - // console.log('hipSize : ', hipSize.toNumber()) /** 현재 라인을 기준으로 45, 135, 225, 315 방향을 확인하기 위한 좌표, hip은 45도 방향으로만 그린다. */ - const checkEdge45 = { vertex1: { x: current.x, y: current.y }, vertex2: { x: current.x + hipSize, y: current.y - hipSize } } - const checkEdge135 = { vertex1: { x: current.x, y: current.y }, vertex2: { x: current.x + hipSize, y: current.y + hipSize } } - const checkEdge225 = { vertex1: { x: current.x, y: current.y }, vertex2: { x: current.x - hipSize, y: current.y + hipSize } } - const checkEdge315 = { vertex1: { x: current.x, y: current.y }, vertex2: { x: current.x - hipSize, y: current.y - hipSize } } + const checkEdge45 = { + vertex1: { x: current.x, y: current.y }, + vertex2: { x: current.x + hipSize, y: current.y - hipSize }, + } + const checkEdge135 = { + vertex1: { x: current.x, y: current.y }, + vertex2: { x: current.x + hipSize, y: current.y + hipSize }, + } + const checkEdge225 = { + vertex1: { x: current.x, y: current.y }, + vertex2: { x: current.x - hipSize, y: current.y + hipSize }, + } + const checkEdge315 = { + vertex1: { x: current.x, y: current.y }, + vertex2: { x: current.x - hipSize, y: current.y - hipSize }, + } let intersectPoints = [] let notIntersect45 = true, @@ -4590,9 +5107,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } }) - // canvas.remove(checkPoint) - // canvas.renderAll() - /** 직교 하는 포인트가 존재 할때 마루를 그린다. */ if (orthogonalPoints.length > 0 && baseRidgeCount < getMaxRidge(baseLines.length)) { baseRidgeCount = baseRidgeCount + 1 @@ -4663,7 +5177,10 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const yVector = Math.sign(Big(line.y2).minus(Big(line.y1))) let lineIntersectPoints = [] checkEdgeLines.forEach((edge) => { - const intersectEdge = edgesIntersection(edge, { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }) + const intersectEdge = edgesIntersection(edge, { + vertex1: { x: line.x1, y: line.y1 }, + vertex2: { x: line.x2, y: line.y2 }, + }) if (intersectEdge) { const isXVector = Math.sign(Big(intersectEdge.x).minus(Big(line.x1))) === xVector const isYVector = Math.sign(Big(intersectEdge.y).minus(Big(line.y1))) === yVector @@ -4692,18 +5209,8 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } }) - console.log('noRidgeHipPoints : ', noRidgeHipPoints) /** 마루를 그릴수 있는지 찾는다. */ noRidgeHipPoints.forEach((hipPoint) => { - const checkLine = new fabric.Line([hipPoint.x2, hipPoint.y2, hipPoint.x1, hipPoint.y1], { - stroke: 'red', - strokeWidth: 4, - parentId: roofId, - name: 'checkLine', - }) - canvas.add(checkLine) - canvas.renderAll() - const ridgePoints = [] intersectPoints .filter( @@ -4776,7 +5283,10 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const checkEdge = { vertex1: { x: intersection.x, y: intersection.y }, - vertex2: { x: Big(intersection.x).plus(Big(xVector).times(10)).toNumber(), y: Big(intersection.y).plus(Big(yVector).times(10)).toNumber() }, + vertex2: { + x: Big(intersection.x).plus(Big(xVector).times(10)).toNumber(), + y: Big(intersection.y).plus(Big(yVector).times(10)).toNumber(), + }, } const intersectX = edgesIntersection( { @@ -4825,7 +5335,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } }) - console.log('linePoints : ', linePoints) const linePoint = linePoints.reduce((prev, current) => { const prevDistance = Big(prev.intersection.x) .minus(Big(hipPoint.x2)) @@ -4848,29 +5357,17 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { if (!linePoint) return const hipStartPoint = [hipPoint.x2, hipPoint.y2, linePoint.intersection.x, linePoint.intersection.y] - // console.log('hipStartPoint : ', hipStartPoint) /** 직선인 경우 마루를 그린다.*/ if ( ((hipStartPoint[0] === hipStartPoint[2] && hipStartPoint[1] !== hipStartPoint[3]) || (hipStartPoint[0] !== hipStartPoint[2] && hipStartPoint[1] === hipStartPoint[3])) && baseRidgeCount < getMaxRidge(baseLines.length) ) { - // console.log('릿지1') const ridgeLine = drawRidgeLine(hipStartPoint, canvas, roof, textMode) baseRidgeCount = baseRidgeCount + 1 baseRidgeLines.push(ridgeLine) noRidgeHipPoints = noRidgeHipPoints.filter((hip) => hip !== hipPoint) } - // console.log( - // Big(hipStartPoint[0]).minus(Big(hipStartPoint[2])).abs().toNumber(), - // Big(hipStartPoint[1]).minus(Big(hipStartPoint[3])).abs().toNumber(), - // Big(hipStartPoint[0]) - // .minus(Big(hipStartPoint[2])) - // .abs() - // .minus(Big(hipStartPoint[1]).minus(Big(hipStartPoint[3])).abs()) - // .abs() - // .lt(1), - // ) /** 대각선인경우 hip을 그린다. */ if ( Big(hipStartPoint[0]) @@ -4882,7 +5379,13 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { ) { // console.log('힙1') const hipLine = drawHipLine(hipStartPoint, canvas, roof, textMode, null, prevDegree, currentDegree) - baseHipLines.push({ x1: hipStartPoint[0], y1: hipStartPoint[1], x2: hipStartPoint[2], y2: hipStartPoint[3], line: hipLine }) + baseHipLines.push({ + x1: hipStartPoint[0], + y1: hipStartPoint[1], + x2: hipStartPoint[2], + y2: hipStartPoint[3], + line: hipLine, + }) noRidgeHipPoints = noRidgeHipPoints.filter((hip) => hip !== hipPoint) } @@ -4901,9 +5404,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { baseRidgeCount = baseRidgeCount + 1 baseRidgeLines.push(ridgeLine) } - - // console.log('isStartPoint : ', isStartPoint) - // console.log(Big(isStartPoint[0]).minus(Big(isStartPoint[2])).toNumber()) if ( Big(isStartPoint[0]) .minus(Big(isStartPoint[2])) @@ -4913,7 +5413,13 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { .lt(1) ) { const hipLine = drawHipLine(isStartPoint, canvas, roof, textMode, null, prevDegree, currentDegree) - baseHipLines.push({ x1: isStartPoint[0], y1: isStartPoint[1], x2: isStartPoint[2], y2: isStartPoint[3], line: hipLine }) + baseHipLines.push({ + x1: isStartPoint[0], + y1: isStartPoint[1], + x2: isStartPoint[2], + y2: isStartPoint[3], + line: hipLine, + }) } } }) @@ -4921,9 +5427,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const ridgeAllPoints = [] baseRidgeLines.forEach((line) => ridgeAllPoints.push({ x: line.x1, y: line.y1 }, { x: line.x2, y: line.y2 })) - // console.log('ridge count', baseRidgeCount, getMaxRidge(baseLines.length)) - - console.log('baseLinePoints : ', baseLinePoints) /** hip 중에 지붕의 라인과 만나지 않은 선을 확인.*/ baseHipLines .filter( @@ -4948,15 +5451,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { return !isIntersect }) .forEach((hip) => { - console.log('hip : ', hip) - const checkLine = new fabric.Line([hip.line.x1, hip.line.y1, hip.line.x2, hip.line.y2], { - stroke: 'red', - strokeWidth: 4, - parentId: roofId, - name: 'checkLine', - }) - canvas.add(checkLine) - canvas.renderAll() const hipLine = hip.line if (hipLine) { const hipVectorX = Big(hipLine.x2).plus(Big(hipLine.attributes.planeSize).times(Big(hipLine.x1).minus(Big(hipLine.x2)).neg().s)) @@ -4969,7 +5463,10 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const checkLineEdge = { vertex1: { x: minX, y: hipLine.y2 }, vertex2: { x: maxX, y: hipLine.y2 } } let isIntersect = false baseHipLines.forEach((baseHipLine) => { - const edge = { vertex1: { x: baseHipLine.x1, y: baseHipLine.y1 }, vertex2: { x: baseHipLine.x2, y: baseHipLine.y2 } } + const edge = { + vertex1: { x: baseHipLine.x1, y: baseHipLine.y1 }, + vertex2: { x: baseHipLine.x2, y: baseHipLine.y2 }, + } const intersection = edgesIntersection(edge, checkLineEdge) if (intersection && !intersection.isIntersectionOutside) { const isVectorX = Math.sign(Big(intersection.x).minus(Big(baseHipLine.x2))) @@ -4980,7 +5477,10 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } }) baseRidgeLines.forEach((baseRidgeLine) => { - const edge = { vertex1: { x: baseRidgeLine.x1, y: baseRidgeLine.y1 }, vertex2: { x: baseRidgeLine.x2, y: baseRidgeLine.y2 } } + const edge = { + vertex1: { x: baseRidgeLine.x1, y: baseRidgeLine.y1 }, + vertex2: { x: baseRidgeLine.x2, y: baseRidgeLine.y2 }, + } const intersection = edgesIntersection(edge, checkLineEdge) if (intersection && !intersection.isIntersectionOutside) { const isVectorX = Math.sign(Big(intersection.x).minus(Big(hipLine.x2))) @@ -5000,7 +5500,10 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const checkLineEdge = { vertex1: { x: hipLine.x2, y: minY }, vertex2: { x: hipLine.x2, y: maxY } } let isIntersect = false baseHipLines.forEach((baseHipLine) => { - const edge = { vertex1: { x: baseHipLine.x1, y: baseHipLine.y1 }, vertex2: { x: baseHipLine.x2, y: baseHipLine.y2 } } + const edge = { + vertex1: { x: baseHipLine.x1, y: baseHipLine.y1 }, + vertex2: { x: baseHipLine.x2, y: baseHipLine.y2 }, + } const intersection = edgesIntersection(edge, checkLineEdge) if (intersection && !intersection.isIntersectionOutside) { const isVectorX = Math.sign(Big(intersection.x).minus(Big(hipLine.x2))) @@ -5011,7 +5514,10 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } }) baseRidgeLines.forEach((baseRidgeLine) => { - const edge = { vertex1: { x: baseRidgeLine.x1, y: baseRidgeLine.y1 }, vertex2: { x: baseRidgeLine.x2, y: baseRidgeLine.y2 } } + const edge = { + vertex1: { x: baseRidgeLine.x1, y: baseRidgeLine.y1 }, + vertex2: { x: baseRidgeLine.x2, y: baseRidgeLine.y2 }, + } const intersection = edgesIntersection(edge, checkLineEdge) if (intersection && !intersection.isIntersectionOutside) { const isVectorX = Math.sign(Big(intersection.x).minus(Big(hipLine.x2))) @@ -5074,7 +5580,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } const modifiedBaseLine = baseLines.filter((line) => (hip.x2 === line.x1 && hip.y2 === line.y1) || (hip.x2 === line.x2 && hip.y2 === line.y2)) - console.log('modifiedBaseLine : ', modifiedBaseLine) if (modifiedBaseLine.length === 0) return const verticalLine = modifiedBaseLine.find( (line) => @@ -5083,8 +5588,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { ) const horizonLine = modifiedBaseLine.find((line) => line !== verticalLine) - console.log('verticalLine', verticalLine) - console.log('horizonLine : ', horizonLine) const horizonRoof = roof.lines.find((line) => { const originPoint = horizonLine.attributes.originPoint if (originPoint.y1 === originPoint.y2) { @@ -5118,10 +5621,15 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { (hipLine) => hipLine.x1 === horizonPoint[0] && hipLine.y1 === horizonPoint[1] && hipLine.x2 === horizonPoint[2] && hipLine.y2 === horizonPoint[3], ) - console.log('alreadyHorizonLines : ', alreadyHorizonLines) if (!alreadyHorizonLines) { addLine = drawHipLine(horizonPoint, canvas, roof, textMode, null, prevDegree, currentDegree) - baseHipLines.push({ x1: horizonPoint[0], y1: horizonPoint[1], x2: horizonPoint[2], y2: horizonPoint[3], line: addLine }) + baseHipLines.push({ + x1: horizonPoint[0], + y1: horizonPoint[1], + x2: horizonPoint[2], + y2: horizonPoint[3], + line: addLine, + }) } else { addLine = alreadyHorizonLines } @@ -5138,7 +5646,13 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { ) if (!alreadyVerticalLine) { addLine = drawHipLine(verticalPoint, canvas, roof, textMode, null, prevDegree, currentDegree) - baseHipLines.push({ x1: verticalPoint[0], y1: verticalPoint[1], x2: verticalPoint[2], y2: verticalPoint[3], line: addLine }) + baseHipLines.push({ + x1: verticalPoint[0], + y1: verticalPoint[1], + x2: verticalPoint[2], + y2: verticalPoint[3], + line: addLine, + }) } } }) @@ -5167,21 +5681,33 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { let baseIntersection = false const ridgeInterSection = [] - const ridgeEdge = { vertex1: { x: ridgePoints[0], y: ridgePoints[1] }, vertex2: { x: ridgePoints[2], y: ridgePoints[3] } } + const ridgeEdge = { + vertex1: { x: ridgePoints[0], y: ridgePoints[1] }, + vertex2: { x: ridgePoints[2], y: ridgePoints[3] }, + } baseLines.forEach((line) => { - const intersection = edgesIntersection(ridgeEdge, { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }) + const intersection = edgesIntersection(ridgeEdge, { + vertex1: { x: line.x1, y: line.y1 }, + vertex2: { x: line.x2, y: line.y2 }, + }) if (intersection && !intersection.isIntersectionOutside) { ridgeInterSection.push(intersection) } }) baseRidgeLines.forEach((line) => { - const intersection = edgesIntersection(ridgeEdge, { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }) + const intersection = edgesIntersection(ridgeEdge, { + vertex1: { x: line.x1, y: line.y1 }, + vertex2: { x: line.x2, y: line.y2 }, + }) if (intersection && !intersection.isIntersectionOutside) { ridgeInterSection.push(intersection) } }) baseHipLines.forEach((line) => { - const intersection = edgesIntersection(ridgeEdge, { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }) + const intersection = edgesIntersection(ridgeEdge, { + vertex1: { x: line.x1, y: line.y1 }, + vertex2: { x: line.x2, y: line.y2 }, + }) if (intersection && !intersection.isIntersectionOutside) { ridgeInterSection.push(intersection) } @@ -5215,21 +5741,33 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { let baseIntersection = false let hipInterSection = [] - const hipEdge = { vertex1: { x: hipPoints[0], y: hipPoints[1] }, vertex2: { x: hipPoints[2], y: hipPoints[3] } } + const hipEdge = { + vertex1: { x: hipPoints[0], y: hipPoints[1] }, + vertex2: { x: hipPoints[2], y: hipPoints[3] }, + } baseLines.forEach((line) => { - const intersection = edgesIntersection(hipEdge, { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }) + const intersection = edgesIntersection(hipEdge, { + vertex1: { x: line.x1, y: line.y1 }, + vertex2: { x: line.x2, y: line.y2 }, + }) if (intersection && !intersection.isIntersectionOutside && eavesType.includes(line.attributes.type)) { baseIntersection = true } }) baseRidgeLines.forEach((line) => { - const intersection = edgesIntersection(hipEdge, { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }) + const intersection = edgesIntersection(hipEdge, { + vertex1: { x: line.x1, y: line.y1 }, + vertex2: { x: line.x2, y: line.y2 }, + }) if (intersection && !intersection.isIntersectionOutside) { hipInterSection.push(intersection) } }) baseHipLines.forEach((line) => { - const intersection = edgesIntersection(hipEdge, { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }) + const intersection = edgesIntersection(hipEdge, { + vertex1: { x: line.x1, y: line.y1 }, + vertex2: { x: line.x2, y: line.y2 }, + }) if (intersection && !intersection.isIntersectionOutside) { hipInterSection.push(intersection) } @@ -5260,33 +5798,29 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { baseHipLines.filter((hipLine2) => hipLine !== hipLine2).forEach((hipLine2) => {}) }) - const innerLines = [...baseRidgeLines, ...baseGableRidgeLines, ...baseHipLines.map((line) => line.line)] - roof.innerLines = innerLines - /*const uniqueInnerLines = [] + const innerLines = [...baseRidgeLines, ...baseGableRidgeLines, ...baseGableLines, ...baseHipLines.map((line) => line.line)] + const uniqueInnerLines = [] innerLines.forEach((currentLine) => { - const sameLines = uniqueInnerLines.filter( - (line) => - line !== currentLine && - ((line.x1 === currentLine.x1 && line.y1 === currentLine.y1 && line.x2 === currentLine.x2 && line.y2 === currentLine.y2) || - (line.x1 === currentLine.x2 && line.y1 === currentLine.y2 && line.x2 === currentLine.x1 && line.y2 === currentLine.y1)), - ) - - if (sameLines.length === 0) { - uniqueInnerLines.push(currentLine) + if (currentLine.length === 0) { + canvas.remove(currentLine) } else { - sameLines.forEach((line) => canvas.remove(line)) + const sameLines = uniqueInnerLines.filter( + (line) => + line !== currentLine && + ((line.x1 === currentLine.x1 && line.y1 === currentLine.y1 && line.x2 === currentLine.x2 && line.y2 === currentLine.y2) || + (line.x1 === currentLine.x2 && line.y1 === currentLine.y2 && line.x2 === currentLine.x1 && line.y2 === currentLine.y1)), + ) + + if (sameLines.length === 0) { + uniqueInnerLines.push(currentLine) + } else { + canvas.remove(currentLine) + } } }) canvas.renderAll() - roof.innerLines = uniqueInnerLines*/ - - /** 확인용 라인, 포인트 제거 */ - canvas - .getObjects() - .filter((obj) => obj.name === 'checkPoint' || obj.name === 'checkLine') - .forEach((obj) => canvas.remove(obj)) - canvas.renderAll() + roof.innerLines = uniqueInnerLines /*drawRidge(roof, canvas, textMode) drawHips(roof, canvas, textMode) @@ -5500,367 +6034,15 @@ const getHalfAngleVector = (line1, line2) => { * 두 벡터를 더합니다 * @type {{x: *, y: *}} */ - const summedVector = { x: Big(unitV1.x).plus(Big(unitV2.x)).toNumber(), y: Big(unitV1.y).plus(Big(unitV2.y)).toNumber() } + const summedVector = { + x: Big(unitV1.x).plus(Big(unitV2.x)).toNumber(), + y: Big(unitV1.y).plus(Big(unitV2.y)).toNumber(), + } /** 결과 벡터를 정규화하여 사잇각 벡터를 반환합니다 */ return normalizeVector(summedVector) } -/** - * 마루가 존재하면 그린다. 마루는 지붕의 중간에 위치한다. - * - * @param roof - * @param canvas - * @param textMode - */ -const drawRidges = (roof, canvas, textMode) => { - console.log('roof.lines : ', roof.lines) - const wallLines = canvas?.getObjects().find((object) => object.name === POLYGON_TYPE.WALL && object.attributes.roofId === roof.id).lines // 외벽의 라인 - const roofLines = roof.lines // 지붕의 라인 - let ridgeRoof = [] - - roofLines.forEach((currentRoof, index) => { - let prevRoof, - nextRoof, - currentWall = wallLines[index] - - prevRoof = index === 0 ? wallLines[wallLines.length - 1] : wallLines[index - 1] - nextRoof = index === wallLines.length - 1 ? wallLines[0] : index === wallLines.length ? wallLines[1] : wallLines[index + 1] - - const angle1 = Big(calculateAngle(prevRoof.startPoint, prevRoof.endPoint)) - const angle2 = Big(calculateAngle(nextRoof.startPoint, nextRoof.endPoint)) - if ( - angle1.minus(angle2).abs().round(Big.roundHalfUp).toNumber() === 180 && - currentWall.attributes.planeSize <= currentRoof.attributes.planeSize - ) { - ridgeRoof.push({ index: index, roof: currentRoof, length: currentRoof.attributes.planeSize }) - } - }) - - // 지붕의 길이가 짧은 순으로 정렬 - ridgeRoof.sort((a, b) => a.length - b.length) - - ridgeRoof.forEach((item) => { - if (getMaxRidge(roofLines.length) > roof.ridges.length) { - const index = item.index, - currentRoof = item.roof - const prevRoof = index === 0 ? roofLines[wallLines.length - 1] : roofLines[index - 1] - const nextRoof = index === roofLines.length - 1 ? roofLines[0] : index === roofLines.length ? roofLines[1] : roofLines[index + 1] - let startXPoint, startYPoint, endXPoint, endYPoint - - let wallLine = wallLines.filter((w) => w.id === currentRoof.attributes.wallLine) - if (wallLine.length > 0) { - wallLine = wallLine[0] - } - - const anotherRoof = roofLines.filter((roof) => roof !== currentRoof && roof !== prevRoof && roof !== nextRoof) - - let currentX1 = Big(currentRoof.x1), - currentY1 = Big(currentRoof.y1), - currentX2 = Big(currentRoof.x2), - currentY2 = Big(currentRoof.y2) - let ridgeMaxLength = Big(Math.max(prevRoof.attributes.planeSize, nextRoof.attributes.planeSize)).div(10) - let ridgeMinLength = Big(Math.min(prevRoof.attributes.planeSize, nextRoof.attributes.planeSize)).div(10) - - const currentAngle = Math.atan2(currentY2.minus(currentY1).toNumber(), currentX2.minus(currentX1).toNumber()) * (180 / Math.PI) - anotherRoof - .filter((roof) => isInnerLine(prevRoof, currentRoof, nextRoof, roof)) - .forEach((innerRoof) => { - const vector1 = { x: currentX2.minus(currentX1), y: currentY2.minus(currentY1) } - const vector2 = { - x: Big(innerRoof.x2).minus(Big(innerRoof.x1)), - y: Big(innerRoof.y2).minus(Big(innerRoof.y1)), - } - - const dotProduct = vector1.x.times(vector2.x).plus(vector1.y.times(vector2.y)) - const magnitude1 = vector1.x.pow(2).plus(vector1.y.pow(2)).sqrt() - const magnitude2 = vector2.x.pow(2).plus(vector2.y.pow(2)).sqrt() - const angle = (Math.acos(dotProduct.div(magnitude1.times(magnitude2).toNumber())) * 180) / Math.PI - - // 현재 지붕선과 직각인 선 - if (Math.abs(angle) === 90) { - const innerBefore = roofLines.find((line) => innerRoof.x1 === line.x2 && innerRoof.y1 === line.y2) - const ibAngle = - Math.atan2(Big(innerBefore.y2).minus(Big(innerBefore.y1)).toNumber(), Big(innerBefore.x2).minus(Big(innerBefore.x1)).toNumber()) * - (180 / Math.PI) - if (Math.abs(Big(currentAngle).minus(Big(ibAngle))) === 180) { - if (currentAngle === 0 || currentAngle === 180) { - currentX2 = innerRoof.x1 - ridgeMinLength = Big(nextRoof.x1) - .minus(Big(nextRoof.x2)) - .times(10) - .round() - .pow(2) - .plus(Big(nextRoof.y1).minus(Big(nextRoof.y2)).times(10).round().pow(2)) - .sqrt() - .div(10) - } - if (currentAngle === 90 || currentAngle === 270) { - currentY2 = innerRoof.y1 - ridgeMinLength = Big(nextRoof.x1) - .minus(Big(innerRoof.x2)) - .times(10) - .round() - .pow(2) - .plus(Big(nextRoof.y1).minus(Big(nextRoof.y2)).times(10).round().pow(2)) - .sqrt() - .div(10) - } - } - if (Math.abs(currentAngle - ibAngle) === 0) { - if (currentAngle === 0 || currentAngle === 180) { - currentX1 = innerRoof.x2 - ridgeMinLength = Big(prevRoof.x1) - .minus(Big(prevRoof.x2)) - .times(10) - .round() - .pow(2) - .plus(Big(prevRoof.y1).minus(Big(innerRoof.y1)).times(10).round().pow(2)) - .sqrt() - .div(10) - } - if (currentAngle === 90 || currentAngle === 270) { - currentY1 = innerRoof.y2 - ridgeMinLength = Big(prevRoof.x1) - .minus(innerRoof.x1) - .times(10) - .round() - .pow(2) - .plus(Big(prevRoof.y2).minus(prevRoof.y2).times(10).round().pow(2)) - .sqrt() - .div(10) - } - } - } - // 현재 지붕선과 반대인 선 - if (Math.abs(angle) === 180) { - if (currentAngle === 0 || currentAngle === 180) { - } - if (currentAngle === 90 || currentAngle === 270) { - } - } - }) - - // 지붕선의 X 중심 - const midX = currentX1.plus(currentX2).div(2) - // 지붕선의 Y 중심 - const midY = currentY1.plus(currentY2).div(2) - - // 외선벽과 지붕선의 X 거리 - const alpha = Big(currentRoof.x1) - .plus(Big(currentRoof.x2)) - .div(2) - .minus(Big(wallLine.x1).plus(Big(wallLine.x2)).div(2)) - // 외벽벽과 지붕선의 Y 거리 - const beta = Big(currentRoof.y1) - .plus(Big(currentRoof.y2)) - .div(2) - .minus(Big(wallLine.y1).plus(Big(wallLine.y2)).div(2)) - // 외벽벽과 지붕선의 거리 - const hypotenuse = alpha.pow(2).plus(beta.pow(2)).sqrt() - - // 지붕선의 평면길이 - const currentPlaneSize = Big(calcLinePlaneSize({ x1: currentX1, y1: currentY1, x2: currentX2, y2: currentY2 })) - // 용마루의 기반길이(지붕선에서 떨어진 정도) - let ridgeBaseLength = currentPlaneSize.div(2).round().div(10) - - // 시작점은 현재 지붕선을 대각선으로 한 직각이등변삼각형으로 판단하고 현재 지붕선의 가운데에서 지붕에서 90도방향으로 지붕선의 절반만큼 떨어진 방향으로 설정한다. - startXPoint = midX.plus(alpha.div(hypotenuse).times(currentPlaneSize.div(2)).neg().div(10)) - startYPoint = midY.plus(beta.div(hypotenuse).times(currentPlaneSize.div(2)).neg().div(10)) - - // 종료점은 시작점에서 용마루의 최대길이 + 기반길이 만큼 그려진것으로 판단하여 늘린다. - const checkEndXPoint = startXPoint.plus(Big(Math.sign(alpha.toNumber())).times(ridgeMaxLength.plus(ridgeBaseLength)).neg()) - const checkEndYPoint = startYPoint.plus(Big(Math.sign(beta.toNumber())).times(ridgeMaxLength.plus(ridgeBaseLength)).neg()) - - // 맞은편 외벽선을 확인하기 위한 라인을 생성 - const checkLine = new QLine([startXPoint.toNumber(), startYPoint.toNumber(), checkEndXPoint.toNumber(), checkEndYPoint.toNumber()], { - parentId: roof.id, - fontSize: roof.fontSize, - stroke: 'red', - strokeWidth: 2, - name: LINE_TYPE.SUBLINE.HIP, - attributes: { roofId: roof.id, currentRoofId: currentRoof.id, actualSize: 0 }, - }) - // 디버그시 주석제거해서 라인 확인 - // canvas.add(checkLine) - // canvas.renderAll() - - // checkLine 과 마주치는 지붕선을 모두 찾아낸다. - const intersectLines = [] - roofLines.forEach((line) => { - const intersection = edgesIntersection( - { vertex1: { x: checkLine.x1, y: checkLine.y1 }, vertex2: { x: checkLine.x2, y: checkLine.y2 } }, - { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }, - ) - if (intersection && !intersection.isIntersectionOutside) { - intersectLines.push({ x: intersection.x, y: intersection.y, line: line }) - } - }) - // intersectLines 중 현재 지붕선과 가장 가까운 지붕선을 찾는다. - if (intersectLines.length > 0) { - intersectLines.reduce((prev, current) => { - if (prev !== undefined) { - const prevDistance = Big(prev.x).minus(startXPoint).pow(2).plus(Big(prev.y).minus(startYPoint).pow(2)).sqrt() - const currentDistance = Big(current.x).minus(startXPoint).pow(2).plus(Big(current.y).minus(startYPoint).pow(2)).sqrt() - return prevDistance > currentDistance ? current : prev - } else { - return current - } - }, undefined) - } - // 현재 지붕선과 마주하는 지붕선이 있는 경우 - if (intersectLines.length > 0) { - // 마주하는 지붕선 - const intersectLine = intersectLines[0] - - //지붕선에서 마주하는 지붕선까지의 x,y 좌표의 차이 - - const diffX = Big(intersectLine.x).minus(startXPoint).round(1).abs().lt(1) ? Big(0) : Big(intersectLine.x).minus(startXPoint).round(1) - const diffY = Big(intersectLine.y).minus(startYPoint).round(1).abs().lt(1) ? Big(0) : Big(intersectLine.y).minus(startYPoint).round(1) - // 시작점에서 ridgeBaseLength 만큼 떨어진 지점까지 용마루 길이로 본다. - endXPoint = startXPoint.plus(Big(Math.sign(diffX.toNumber())).times(diffX.abs().minus(ridgeBaseLength))) - endYPoint = startYPoint.plus(Big(Math.sign(diffY.toNumber())).times(diffY.abs().minus(ridgeBaseLength))) - - // 시작점에서 종료점까지의 평면길이 - //startXPoint.minus(endXPoint).abs().pow(2).plus(startYPoint.minus(endYPoint).abs().pow(2)).sqrt() - const hypo = Big( - calcLinePlaneSize({ - x1: startXPoint.toNumber(), - y1: startYPoint.toNumber(), - x2: endXPoint.toNumber(), - y2: endYPoint.toNumber(), - }), - ).div(10) - // 현재 지붕선과 마주하는 지붕선을 잇는 선분의 교차점까지의 길이 - const intersectLength = Big( - calcLinePlaneSize({ - x1: midX.toNumber(), - y1: midY.toNumber(), - x2: intersectLine.x, - y2: intersectLine.y, - }), - ).div(10) - //마주하는 지붕선까지의 길이가 현재 지붕선의 이전, 다음 지붕선의 길이보다 작을때 - if (intersectLength.lt(Big(prevRoof.attributes.planeSize).div(10)) && intersectLength.lt(Big(nextRoof.attributes.planeSize).div(10))) { - endXPoint = startXPoint - endYPoint = startYPoint - } else { - if (ridgeMinLength.lt(hypo)) { - endXPoint = startXPoint.plus(Big(Math.sign(diffX.toNumber())).times(ridgeMinLength)) - endYPoint = startYPoint.plus(Big(Math.sign(diffY.toNumber())).times(ridgeMinLength)) - } - } - } else { - endXPoint = startXPoint.plus(Big(Math.sign(alpha.toNumber())).times(ridgeMinLength).neg()) - endYPoint = startYPoint.plus(Big(Math.sign(beta.toNumber())).times(ridgeMinLength).neg()) - } - - // 용마루 선을 그린다. - const ridge = new QLine([startXPoint.toNumber(), startYPoint.toNumber(), endXPoint.toNumber(), endYPoint.toNumber()], { - parentId: roof.id, - fontSize: roof.fontSize, - stroke: '#1083E3', - strokeWidth: 2, - name: LINE_TYPE.SUBLINE.RIDGE, - textMode: textMode, - attributes: { - roofId: roof.id, - planeSize: calcLinePlaneSize({ - x1: startXPoint.toNumber(), - y1: startYPoint.toNumber(), - x2: endXPoint.toNumber(), - y2: endYPoint.toNumber(), - }), - actualSize: calcLinePlaneSize({ - x1: startXPoint.toNumber(), - y1: startYPoint.toNumber(), - x2: endXPoint.toNumber(), - y2: endYPoint.toNumber(), - }), - }, - }) - - // 용마루의 길이가 0보다 클때만 canvas 에 추가 한다. - if (ridge.attributes.planeSize > 0) { - canvas.add(ridge) - roof.ridges.push(ridge) - roof.innerLines.push(ridge) - - const distance = (x1, y1, x2, y2) => x2.minus(x1).pow(2).plus(y2.minus(y1).pow(2)).sqrt() - const dist1 = distance(startXPoint, startYPoint, Big(currentRoof.x1), Big(currentRoof.y1)) - const dist2 = distance(endXPoint, endYPoint, Big(currentRoof.x1), Big(currentRoof.y1)) - - currentRoof.attributes.ridgeCoordinate = { - x1: dist1 < dist2 ? startXPoint : endXPoint, - y1: dist1 < dist2 ? startYPoint : endYPoint, - } - } - } - }) - - //겹쳐지는 마루는 하나로 합침 - roof.ridges.forEach((ridge) => { - roof.ridges - .filter((ridge2) => !(ridge.x1 === ridge2.x1 && ridge.y1 === ridge2.y1 && ridge.x2 === ridge2.x2 && ridge.y2 === ridge2.y2)) - .forEach((ridge2) => { - let overlap = segmentsOverlap(ridge, ridge2) - if (overlap) { - let x1 = Math.min(ridge.x1, ridge2.x1, ridge.x2, ridge2.x2) - let x2 = Math.max(ridge.x1, ridge2.x1, ridge.x2, ridge2.x2) - let y1 = Math.min(ridge.y1, ridge2.y1, ridge.y2, ridge2.y2) - let y2 = Math.max(ridge.y1, ridge2.y1, ridge.y2, ridge2.y2) - //겹치는 용마루의 좌표를 합친다. - const newRidge = new QLine([x1, y1, x2, y2], { - parentId: roof.id, - fontSize: roof.fontSize, - stroke: '#1083E3', - strokeWidth: 2, - name: LINE_TYPE.SUBLINE.RIDGE, - textMode: textMode, - attributes: { - roofId: roof.id, - planeSize: calcLinePlaneSize({ x1, y1, x2, y2 }), - actualSize: calcLinePlaneSize({ x1, y1, x2, y2 }), - }, - }) - //겹치는 용마루를 제거한다. - roof.canvas.remove(ridge) - roof.canvas.remove(ridge2) - - roof.ridges = roof.ridges.filter((r) => !(ridge.x1 === r.x1 && ridge.y1 === r.y1 && ridge.x2 === r.x2 && ridge.y2 === r.y2)) - roof.ridges = roof.ridges.filter((r) => !(ridge2.x1 === r.x1 && ridge2.y1 === r.y1 && ridge2.x2 === r.x2 && ridge2.y2 === r.y2)) - roof.innerLines = roof.innerLines.filter((r) => !(ridge.x1 === r.x1 && ridge.y1 === r.y1 && ridge.x2 === r.x2 && ridge.y2 === r.y2)) - roof.innerLines = roof.innerLines.filter((r) => !(ridge2.x1 === r.x1 && ridge2.y1 === r.y1 && ridge2.x2 === r.x2 && ridge2.y2 === r.y2)) - canvas.add(newRidge) - roof.ridges.push(newRidge) - roof.innerLines.push(newRidge) - } - }) - }) - canvas?.renderAll() -} - -/** - * line 이 세 라인 사이에 존재하는지 확인한다. - * @param prevLine - * @param currentLine - * @param nextLine - * @param line - */ -const isInnerLine = (prevLine, currentLine, nextLine, line) => { - let inside = false - let minX = Math.min(currentLine.x1, currentLine.x2, prevLine.x1, nextLine.x2) - let maxX = Math.max(currentLine.x1, currentLine.x2, prevLine.x1, nextLine.x2) - let minY = Math.min(currentLine.y1, currentLine.y2, prevLine.y1, nextLine.y2) - let maxY = Math.max(currentLine.y1, currentLine.y2, prevLine.y1, nextLine.y2) - - if (minX < line.x1 && line.x1 < maxX && minY < line.y1 && line.y1 < maxY && minX < line.x2 && line.x2 < maxX && minY < line.y2 && line.y2 < maxY) { - inside = true - } - - return inside -} - /** * 두 선분이 겹치는지 확인 * @param line1 @@ -5881,446 +6063,6 @@ export const segmentsOverlap = (line1, line2) => { return false } -/** - * 추녀마루를 그린다. - * @param roof - * @param canvas - * @param textMode - */ -const drawHips = (roof, canvas, textMode) => { - const roofLines = roof.lines - const ridgeLines = canvas?.getObjects().filter((object) => object.name === LINE_TYPE.SUBLINE.RIDGE && object.attributes.roofId === roof.id) - - //마루에서 시작되는 hip 을 먼저 그립니다. - roofLines - .filter((roof) => roof.attributes.type === LINE_TYPE.WALLLINE.EAVES && roof.attributes.ridgeCoordinate !== undefined) - .forEach((currentRoof, index) => { - const prevRoof = roof.lines[index === 0 ? roof.lines.length - 1 : index - 1] - const nextRoof = roof.lines[index === roof.lines.length - 1 ? 0 : index + 1] - const prevDegree = prevRoof.attributes.pitch > 0 ? getDegreeByChon(prevRoof.attributes.pitch) : prevRoof.attributes.degree - const currentDegree = currentRoof.attributes.pitch > 0 ? getDegreeByChon(currentRoof.attributes.pitch) : currentRoof.attributes.degree - const nextDegree = nextRoof.attributes.pitch > 0 ? getDegreeByChon(nextRoof.attributes.pitch) : nextRoof.attributes.degree - - //용마루 작성시 입력된 용마루 좌표가 있는 경우 처리 - const ridgeCoordinate = currentRoof.attributes.ridgeCoordinate - - const vectorX1 = Big(ridgeCoordinate.x1).minus(Big(currentRoof.x1)) - const vectorY1 = Big(ridgeCoordinate.y1).minus(Big(currentRoof.y1)) - //현재 지붕선의 좌표와 용마루 좌표의 각도를 구한다. - const angle1 = Big(Math.atan2(vectorY1.toNumber(), vectorX1.toNumber())).times(Big(180).div(Math.PI)) - - // 용마루 까지의 각도가 45도 인 경우 작성. - if (Big(angle1.abs().toNumber()).mod(45).eq(0)) { - const hip1 = new QLine([currentRoof.x1, currentRoof.y1, ridgeCoordinate.x1, ridgeCoordinate.y1], { - parentId: roof.id, - fontSize: roof.fontSize, - stroke: '#1083E3', - strokeWidth: 2, - name: LINE_TYPE.SUBLINE.HIP, - attributes: { - roofId: roof.id, - currentRoofId: currentRoof.id, - planeSize: calcLinePlaneSize({ - x1: currentRoof.x1, - y1: currentRoof.y1, - x2: ridgeCoordinate.x1, - y2: ridgeCoordinate.y1, - }), - actualSize: - prevDegree === currentDegree - ? calcLineActualSize( - { - x1: currentRoof.x1, - y1: currentRoof.y1, - x2: ridgeCoordinate.x1, - y2: ridgeCoordinate.y1, - }, - currentDegree, - ) - : 0, - }, - }) - canvas.add(hip1) - roof.hips.push(hip1) - roof.innerLines.push(hip1) - } - - const vectorX2 = Big(ridgeCoordinate.x1).minus(Big(currentRoof.x2)) - const vectorY2 = Big(ridgeCoordinate.y1).minus(Big(currentRoof.y2)) - const angle2 = Big(Math.atan2(vectorY2.toNumber(), vectorX2.toNumber())).times(Big(180).div(Math.PI)) - if (Big(angle2.abs().toNumber()).mod(45).eq(0)) { - const hip2 = new QLine([currentRoof.x2, currentRoof.y2, ridgeCoordinate.x1, ridgeCoordinate.y1], { - parentId: roof.id, - fontSize: roof.fontSize, - stroke: '#1083E3', - strokeWidth: 2, - name: LINE_TYPE.SUBLINE.HIP, - attributes: { - roofId: roof.id, - currentRoofId: currentRoof.id, - planeSize: calcLinePlaneSize({ - x1: currentRoof.x2, - y1: currentRoof.y2, - x2: ridgeCoordinate.x1, - y2: ridgeCoordinate.y1, - }), - actualSize: - prevDegree === currentDegree - ? calcLineActualSize( - { - x1: currentRoof.x2, - y1: currentRoof.y2, - x2: ridgeCoordinate.x1, - y2: ridgeCoordinate.y1, - }, - currentDegree, - ) - : 0, - }, - }) - canvas.add(hip2) - roof.hips.push(hip2) - roof.innerLines.push(hip2) - } - }) - - const hipLines = canvas?.getObjects().filter((object) => object.name === LINE_TYPE.SUBLINE.HIP && object.attributes.roofId === roof.id) - - //마루에서 시작되지 않는 hip 을 그립니다. - roofLines - .filter((roof) => { - let isHip = false - if (hipLines.some((hip) => hip.x1 === roof.x1 && hip.y1 === roof.y1)) { - isHip = true - } - return !isHip - }) - .forEach((currentRoof) => { - let prevRoof - roofLines.forEach((roof, index) => { - if (roof === currentRoof) { - prevRoof = index === 0 ? roofLines[roofLines.length - 1] : roofLines[index - 1] - } - }) - const prevDegree = prevRoof.attributes.pitch > 0 ? getDegreeByChon(prevRoof.attributes.pitch) : prevRoof.attributes.degree - const currentDegree = currentRoof.attributes.pitch > 0 ? getDegreeByChon(currentRoof.attributes.pitch) : currentRoof.attributes.degree - - let ridgePoints = [] - ridgeLines.forEach((ridge) => { - const deltaX1 = Big(ridge.x1).minus(Big(currentRoof.x1)) - const deltaY1 = Big(ridge.y1).minus(Big(currentRoof.y1)) - const deltaX2 = Big(ridge.x2).minus(Big(currentRoof.x1)) - const deltaY2 = Big(ridge.y2).minus(Big(currentRoof.y1)) - - if (deltaY1.div(deltaX1).abs().round(1).eq(1)) { - ridgePoints.push({ x: ridge.x1, y: ridge.y1 }) - } - if (deltaY2.div(deltaX2).abs().round(1).eq(1)) { - ridgePoints.push({ x: ridge.x2, y: ridge.y2 }) - } - }) - - ridgePoints = ridgePoints.reduce((prev, current) => { - if (prev !== undefined) { - // Math.abs(prev.x - currentRoof.x1) - const deltaPrevX = Big(prev.x).minus(Big(currentRoof.x1)).abs() - const deltaPrevY = Big(prev.y).minus(Big(currentRoof.y1)).abs() - const deltaCurrentX = Big(current.x).minus(Big(currentRoof.x1)).abs() - const deltaCurrentY = Big(current.y).minus(Big(currentRoof.y1)).abs() - if (deltaPrevX.lt(deltaCurrentX) && deltaPrevY.lt(deltaCurrentY)) { - return prev - } else { - return current - } - } else { - return current - } - }, undefined) - - if (ridgePoints !== undefined) { - const hip = new QLine([currentRoof.x1, currentRoof.y1, ridgePoints.x, ridgePoints.y], { - parentId: roof.id, - fontSize: roof.fontSize, - stroke: '#1083E3', - strokeWidth: 2, - name: LINE_TYPE.SUBLINE.HIP, - attributes: { - roofId: roof.id, - currentRoofId: currentRoof.id, - planeSize: calcLinePlaneSize({ - x1: currentRoof.x1, - y1: currentRoof.y1, - x2: ridgePoints.x, - y2: ridgePoints.y, - }), - actualSize: - prevDegree === currentDegree - ? calcLineActualSize( - { - x1: currentRoof.x1, - y1: currentRoof.y1, - x2: ridgePoints.x, - y2: ridgePoints.y, - }, - currentDegree, - ) - : 0, - }, - }) - canvas.add(hip) - roof.hips.push(hip) - roof.innerLines.push(hip) - } - }) - canvas?.renderAll() -} - -/** - * 3개 이상 이어지지 않은 라인 포인트 계산 - * 모임지붕에서 point 는 3개 이상의 라인과 접해야 함. - * @param polygon - * @param canvas - * @param textMode - */ -const connectLinePoint = (polygon, canvas, textMode) => { - // 연결되지 않은 모든 라인의 포인트를 구한다. - let missedPoints = [] - - const lineDegrees = [ - ...new Set(polygon.lines.map((line) => (line.attributes.pitch > 0 ? getDegreeByChon(line.attributes.pitch) : line.attributes.degree))), - ] - - //마루 - polygon.ridges.forEach((ridge) => { - if (ridge.x1 === ridge.x2) { - if ( - polygon.lines - .filter((roof) => roof.y1 === roof.y2) - .filter((roof) => roof.y1 === ridge.y1 || roof.y1 === ridge.y2 || roof.y2 === ridge.y1 || roof.y2 === ridge.y2).length > 0 - ) { - return - } - } - if (ridge.y1 === ridge.y2) { - if ( - polygon.lines - .filter((roof) => roof.x1 === roof.x2) - .filter((roof) => roof.x1 === ridge.x1 || roof.x1 === ridge.x2 || roof.x2 === ridge.x1 || roof.x2 === ridge.x2).length > 0 - ) { - return - } - } - if (polygon.hips.filter((hip) => hip.x2 === ridge.x1 && hip.y2 === ridge.y1).length < 2) { - missedPoints.push({ x: ridge.x1, y: ridge.y1 }) - } - if (polygon.hips.filter((hip) => hip.x2 === ridge.x2 && hip.y2 === ridge.y2).length < 2) { - missedPoints.push({ x: ridge.x2, y: ridge.y2 }) - } - }) - - //추녀마루 - polygon.hips.forEach((hip) => { - let count = 0 - count += polygon.ridges.filter((ridge) => (ridge.x1 === hip.x2 && ridge.y1 === hip.y2) || (ridge.x2 === hip.x2 && ridge.y2 === hip.y2)).length - - count += polygon.hips.filter((hip2) => (hip2.x1 === hip.x2 && hip2.y1 === hip.y2) || (hip2.x2 === hip.x2 && hip2.y2 === hip.y2)).length - if (count < 3) { - missedPoints.push({ x: hip.x2, y: hip.y2 }) - } - }) - - let missedLine = [] - - //중복포인트제거 - missedPoints = [...new Set(missedPoints.map((line) => JSON.stringify(line)))].map((line) => JSON.parse(line)) - - missedPoints.forEach((p1) => { - let p2 = missedPoints - .filter((p) => p.x !== p1.x && p.y !== p1.y) - .reduce((prev, current) => { - if (prev !== undefined) { - return Math.sqrt(Math.pow(Math.abs(current.x - p1.x), 2) + Math.pow(Math.abs(current.y - p1.y), 2)) < - Math.sqrt(Math.pow(Math.abs(prev.x - p1.x), 2) + Math.pow(Math.abs(prev.y - p1.y), 2)) - ? current - : prev - } else { - return current - } - }, undefined) - if (p2 !== undefined) { - if (p1.x < p2.x && p1.y < p2.y) { - missedLine.push({ x1: p1.x, y1: p1.y, x2: p2.x, y2: p2.y }) - } - if (p1.x > p2.x && p1.y < p2.y) { - missedLine.push({ x1: p2.x, y1: p2.y, x2: p1.x, y2: p1.y }) - } - if (p1.x > p2.x && p1.y > p2.y) { - missedLine.push({ x1: p2.x, y1: p2.y, x2: p1.x, y2: p1.y }) - } - if (p1.x < p2.x && p1.y > p2.y) { - missedLine.push({ x1: p1.x, y1: p1.y, x2: p2.x, y2: p2.y }) - } - } - }) - - //중복라인제거 - missedLine = [...new Set(missedLine.map((line) => JSON.stringify(line)))].map((line) => JSON.parse(line)) - - missedLine.forEach((p) => { - const line = new QLine([p.x1, p.y1, p.x2, p.y2], { - parentId: polygon.id, - attributes: { - roofId: polygon.id, - planeSize: calcLinePlaneSize(p), - actualSize: lineDegrees.length === 0 ? 0 : calcLineActualSize(p, lineDegrees[0]), - }, - fontSize: polygon.fontSize, - stroke: '#1083E3', - strokeWidth: 2, - textMode: textMode, - }) - polygon.canvas.add(line) - polygon.innerLines.push(line) - }) - - missedPoints = [] - missedLine = [] - - polygon.innerLines.forEach((line) => { - if ( - polygon.innerLines.filter( - (innerLine) => (line.x2 === innerLine.x1 && line.y2 === innerLine.y1) || (line.x2 === innerLine.x2 && line.y2 === innerLine.y2), - ).length < 3 - ) { - missedPoints.push({ x: line.x2, y: line.y2 }) - } - }) - - missedPoints = [...new Set(missedPoints.map((line) => JSON.stringify(line)))].map((line) => JSON.parse(line)) - - missedPoints.forEach((p1) => { - let p2 = missedPoints - .filter((p) => !(p.x === p1.x && p.y === p1.y)) - .reduce((prev, current) => { - if (prev !== undefined) { - return Math.abs(current.x - p1.x) + Math.abs(current.y - p1.y) < Math.abs(prev.x - p1.x) + Math.abs(prev.y - p1.y) ? current : prev - } else { - return current - } - }, undefined) - - if (p2 !== undefined) { - if (p1.x === p2.x && p1.y < p2.y) { - missedLine.push({ x1: p1.x, y1: p1.y, x2: p2.x, y2: p2.y }) - } - if (p1.x === p2.x && p1.y > p2.y) { - missedLine.push({ x1: p1.x, y1: p2.y, x2: p2.x, y2: p1.y }) - } - if (p1.x < p2.x && p1.y === p2.y) { - missedLine.push({ x1: p1.x, y1: p1.y, x2: p2.x, y2: p2.y }) - } - if (p1.x > p2.x && p1.y === p2.y) { - missedLine.push({ x1: p2.x, y1: p1.y, x2: p1.x, y2: p2.y }) - } - } - }) - - //중복라인제거 - missedLine = [...new Set(missedLine.map((line) => JSON.stringify(line)))].map((line) => JSON.parse(line)) - - missedLine.forEach((p) => { - const line = new QLine([p.x1, p.y1, p.x2, p.y2], { - attributes: { - roofId: polygon.id, - planeSize: calcLinePlaneSize(p), - actualSize: lineDegrees.length === 0 ? 0 : calcLineActualSize(p, lineDegrees[0]), - }, - fontSize: polygon.fontSize, - stroke: '#1083E3', - strokeWidth: 2, - textMode: textMode, - }) - line.attributes.planeSize = Math.round(Math.sqrt(Math.pow(line.x1 - line.x2, 2) + Math.pow(line.y1 - line.y2, 2)) * 10) - line.attributes.actualSize = Math.round(Math.sqrt(Math.pow(line.x1 - line.x2, 2) + Math.pow(line.y1 - line.y2, 2)) * 10) - polygon.canvas.add(line) - polygon.innerLines.push(line) - }) - - //마지막으로 연결되지 않고 떨어져있는 마루를 확인한다. - let missedRidge = [] - polygon.ridges.forEach((ridge) => { - let lineCheck1 = polygon.innerLines.filter((line) => { - if ( - !(line.x1 === ridge.x1 && line.y1 === ridge.y1 && line.x2 === ridge.x2 && line.y2 === ridge.y2) && - ((line.x1 === ridge.x1 && line.y1 === ridge.y1) || (line.x2 === ridge.x1 && line.y2 === ridge.y1)) - ) { - return line - } - }) - - let lineCheck2 = polygon.innerLines.filter((line) => { - if ( - !(line.x1 === ridge.x1 && line.y1 === ridge.y1 && line.x2 === ridge.x2 && line.y2 === ridge.y2) && - ((line.x1 === ridge.x2 && line.y1 === ridge.y2) || (line.x2 === ridge.x2 && line.y2 === ridge.y2)) - ) { - return line - } - }) - if (lineCheck1.length === 0 || lineCheck2.length === 0) { - missedRidge.push(ridge) - } - }) - - missedRidge.forEach((ridge) => { - let missedRidge2 = missedRidge.filter( - (ridge2) => !(ridge.x1 === ridge2.x1 && ridge.y1 === ridge2.y1 && ridge.x2 === ridge2.x2 && ridge.y2 === ridge2.y2), - ) - - missedRidge2.forEach((ridge2) => { - let overlap = false - if (ridge.x1 === ridge.x2 && ridge2.x1 === ridge2.x2 && ridge2.x1 === ridge.x1) { - overlap = true - } - if (ridge.y1 === ridge.y2 && ridge2.y1 === ridge2.y2 && ridge2.y1 === ridge.y1) { - overlap = true - } - if (overlap) { - let x1 = Math.min(ridge.x1, ridge2.x1, ridge.x2, ridge2.x2) - let x2 = Math.max(ridge.x1, ridge2.x1, ridge.x2, ridge2.x2) - let y1 = Math.min(ridge.y1, ridge2.y1, ridge.y2, ridge2.y2) - let y2 = Math.max(ridge.y1, ridge2.y1, ridge.y2, ridge2.y2) - const newRidge = new QLine([x1, y1, x2, y2], { - fontSize: polygon.fontSize, - stroke: '#1083E3', - strokeWidth: 2, - name: LINE_TYPE.SUBLINE.RIDGE, - textMode: textMode, - attributes: { - roofId: polygon.id, - planeSize: calcLinePlaneSize(ridge), - actualSize: lineDegrees.length > 0 ? 0 : calcLineActualSize(ridge, lineDegrees[0]), - }, - }) - if (polygon.ridges.filter((r) => newRidge.x1 === r.x1 && newRidge.y1 === r.y1 && newRidge.x2 === r.x2 && newRidge.y2 === r.y2).length === 0) { - polygon.canvas.remove(ridge) - polygon.canvas.remove(ridge2) - polygon.ridges = polygon.ridges.filter((r) => !(ridge.x1 === r.x1 && ridge.y1 === r.y1 && ridge.x2 === r.x2 && ridge.y2 === r.y2)) - polygon.ridges = polygon.ridges.filter((r) => !(ridge2.x1 === r.x1 && ridge2.y1 === r.y1 && ridge2.x2 === r.x2 && ridge2.y2 === r.y2)) - polygon.innerLines = polygon.innerLines.filter((r) => !(ridge.x1 === r.x1 && ridge.y1 === r.y1 && ridge.x2 === r.x2 && ridge.y2 === r.y2)) - polygon.innerLines = polygon.innerLines.filter( - (r) => !(ridge2.x1 === r.x1 && ridge2.y1 === r.y1 && ridge2.x2 === r.x2 && ridge2.y2 === r.y2), - ) - - polygon.canvas.add(newRidge) - polygon.ridges.push(newRidge) - polygon.innerLines.push(newRidge) - } - } - }) - }) - polygon.canvas.renderAll() -} - /** * 외벽선 속성에 따라서 모양을 수정한다. * @param roof @@ -6816,7 +6558,15 @@ const changeGableRoof = (currentRoof, canvas, textMode) => { x2: midX.toNumber(), y2: midY.toNumber(), }), - actualSize: calcLineActualSize({ x1: currentRoof.x2, y1: currentRoof.y2, x2: midX.toNumber(), y2: midY.toNumber() }, nextDegree), + actualSize: calcLineActualSize( + { + x1: currentRoof.x2, + y1: currentRoof.y2, + x2: midX.toNumber(), + y2: midY.toNumber(), + }, + nextDegree, + ), }, }) canvas?.add(hip2) @@ -7761,7 +7511,15 @@ const changeWallRoof = (currentRoof, canvas, textMode) => { x2: wallMidX.toNumber(), y2: wallMidY.toNumber(), }), - actualSize: calcLineActualSize({ x1: currentRoof.x1, y1: currentRoof.y1, x2: wallMidX.toNumber(), y2: wallMidY.toNumber() }, prevDegree), + actualSize: calcLineActualSize( + { + x1: currentRoof.x1, + y1: currentRoof.y1, + x2: wallMidX.toNumber(), + y2: wallMidY.toNumber(), + }, + prevDegree, + ), }, }) // const hip1Base = ((Math.abs(hip1.x1 - hip1.x2) + Math.abs(hip1.y1 - hip1.y2)) / 2) * 10 @@ -7784,7 +7542,15 @@ const changeWallRoof = (currentRoof, canvas, textMode) => { x2: wallMidX.toNumber(), y2: wallMidY.toNumber(), }), - actualSize: calcLineActualSize({ x1: currentRoof.x2, y1: currentRoof.y2, x2: wallMidX.toNumber(), y2: wallMidY.toNumber() }, nextDegree), + actualSize: calcLineActualSize( + { + x1: currentRoof.x2, + y1: currentRoof.y2, + x2: wallMidX.toNumber(), + y2: wallMidY.toNumber(), + }, + nextDegree, + ), }, }) // const hip2Base = ((Math.abs(hip2.x1 - hip2.x2) + Math.abs(hip2.y1 - hip2.y2)) / 2) * 10 @@ -7915,9 +7681,6 @@ const reDrawPolygon = (polygon, canvas) => { line.attributes = l.attributes } }) - // const lineLength = Math.sqrt( - // Math.pow(Math.round(Math.abs(line.x1 - line.x2) * 10), 2) + Math.pow(Math.round(Math.abs(line.y1 - line.y2) * 10), 2), - // ) const lineLength = calcLinePlaneSize({ x1: line.x1, y1: line.y1, x2: line.x2, y2: line.y2 }) if (line.attributes !== undefined) { line.attributes.planeSize = lineLength