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 } }