From d6d295867efee445c653048e6a9d9f4a2ba80b16 Mon Sep 17 00:00:00 2001 From: Jaeyoung Lee Date: Fri, 20 Sep 2024 09:44:36 +0900 Subject: [PATCH] =?UTF-8?q?=EC=A7=80=EB=B6=95=20=EB=9D=BC=EC=9D=B8?= =?UTF-8?q?=EB=B3=84=20offset=20=EC=A0=81=EC=9A=A9=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Roof2.jsx | 4 +- src/hooks/useMode.js | 414 ++++++++++++++++++------------------- src/util/qpolygon-utils.js | 4 - 3 files changed, 198 insertions(+), 224 deletions(-) diff --git a/src/components/Roof2.jsx b/src/components/Roof2.jsx index e9da0e7a..f57f62ae 100644 --- a/src/components/Roof2.jsx +++ b/src/components/Roof2.jsx @@ -140,10 +140,10 @@ export default function Roof2(props) { useEffect(() => { get({ url: `/api/canvas-management/canvas-statuses/by-object/test123240822001` }).then((res) => { - console.log(res) + // console.log(res) const arrangeData = res.map((item) => { - console.log(item.canvasStatus.replace(/##/g, '"').replace(/\\/g, '')) + // console.log(item.canvasStatus.replace(/##/g, '"').replace(/\\/g, '')) const test = item.canvasStatus.replace(/##/g, '"').replace(/\\/g, '') const test2 = test.substring(1, test.length - 1) return { diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index bc650216..0ea0c065 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -1472,11 +1472,203 @@ export function useMode() { *벽 지붕 외곽선 생성 polygon을 입력받아 만들기 */ const handleOuterlinesTest2 = (polygon, offset = 50) => { - drawRoofPolygon(polygon, offset) - /*const offsetPoints = offsetPolygon(polygon.points, offset) + const roof = drawRoofPolygon(polygon) + roof.drawHelpLine() + roof.divideLine() + } + + function inwardEdgeNormal(vertex1, vertex2) { + // Assuming that polygon vertices are in clockwise order + const dx = vertex2.x - vertex1.x + const dy = vertex2.y - vertex1.y + const edgeLength = Math.sqrt(dx * dx + dy * dy) + + return { + x: -dy / edgeLength, + y: dx / edgeLength, + } + } + + function outwardEdgeNormal(vertex1, vertex2) { + const n = inwardEdgeNormal(vertex1, vertex2) + + return { + x: -n.x, + y: -n.y, + } + } + + function createRoofPolygon(vertices) { + const edges = [] + let minX = vertices.length > 0 ? vertices[0].x : undefined + let minY = vertices.length > 0 ? vertices[0].y : undefined + let maxX = minX + let maxY = minY + + for (let i = 0; i < vertices.length; i++) { + const vertex1 = vertices[i] + const vertex2 = vertices[(i + 1) % vertices.length] + + const outwardNormal = outwardEdgeNormal(vertex1, vertex2) + + const inwardNormal = inwardEdgeNormal(vertex1, vertex2) + + const edge = { + vertex1, + vertex2, + index: i, + outwardNormal, + inwardNormal, + } + + edges.push(edge) + + const x = vertices[i].x + const y = vertices[i].y + minX = Math.min(x, minX) + minY = Math.min(y, minY) + maxX = Math.max(x, maxX) + maxY = Math.max(y, maxY) + } + + return { + vertices, + edges, + minX, + minY, + maxX, + maxY, + } + } + + function createOffsetEdge(edge, dx, dy) { + return { + vertex1: { + x: edge.vertex1.x + dx, + y: edge.vertex1.y + dy, + }, + vertex2: { + x: edge.vertex2.x + dx, + y: edge.vertex2.y + dy, + }, + } + } + + 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, + } + } + + function createMarginPolygon(polygon, lines, arcSegments = 0) { + const offsetEdges = [] + + polygon.edges.forEach((edge, i) => { + 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)) + }) + + const vertices = [] + + offsetEdges.forEach((thisEdge, i) => { + const prevEdge = offsetEdges[(i + offsetEdges.length - 1) % offsetEdges.length] + const vertex = edgesIntersection(prevEdge, thisEdge) + if (vertex && (!vertex.isIntersectionOutside || arcSegments < 1)) { + vertices.push({ + x: vertex.x, + y: vertex.y, + }) + } + }) + + const marginPolygon = createRoofPolygon(vertices) + marginPolygon.offsetEdges = offsetEdges + return marginPolygon + } + + function createPaddingPolygon(polygon, lines, arcSegments = 0) { + const offsetEdges = [] + + polygon.edges.forEach((edge, i) => { + 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)) + }) + + const vertices = [] + + offsetEdges.forEach((thisEdge, i) => { + const prevEdge = offsetEdges[(i + offsetEdges.length - 1) % offsetEdges.length] + const vertex = edgesIntersection(prevEdge, thisEdge) + if (vertex && (!vertex.isIntersectionOutside || arcSegments < 1)) { + vertices.push({ + x: vertex.x, + y: vertex.y, + }) + } + }) + + const paddingPolygon = createRoofPolygon(vertices) + paddingPolygon.offsetEdges = offsetEdges + return paddingPolygon + } + + const drawRoofPolygon = (wall) => { + let walls = wall.lines + walls.forEach((wall, index) => { + if (index === walls.length - 1 || index === 3) { + wall.attributes = { + type: 'gable', + offset: 30, + } + } else { + wall.attributes = { + type: 'hip', + offset: 50, + } + } + }) + const polygon = createRoofPolygon(wall.points) + const originPolygon = new QPolygon(wall.points, { fontSize: 0 }) + let offsetPolygon + + let result = createMarginPolygon(polygon, wall.lines).vertices + const allPointsOutside = result.every((point) => !originPolygon.inPolygon(point)) + + if (allPointsOutside) { + offsetPolygon = createMarginPolygon(polygon, wall.lines).vertices + } else { + offsetPolygon = createPaddingPolygon(polygon, wall.lines).vertices + } const roof = makePolygon( - offsetPoints.map((point) => { + offsetPolygon.map((point) => { return { x1: point.x, y1: point.y } }), ) @@ -1485,177 +1677,7 @@ export function useMode() { setRoof(roof) setWall(polygon) - roof.drawHelpLine() - roof.divideLine()*/ - } - - const drawRoofPolygon = (wall, offset = 50) => { - let walls = wall.lines - walls.forEach((wall, index) => { - if (index === 0) { - wall.attributes = { - type: 'gable', - width: 30, - } - } else { - wall.attributes = { - type: 'hip', - width: 50, - } - } - }) - - console.log('walls', walls) - - walls.forEach((currentWall, index) => { - let prevWall, nextWall - if (index === 0) { - prevWall = walls[walls.length - 1] - nextWall = walls[index + 1] - } else if (index === walls.length - 1) { - prevWall = walls[index - 1] - nextWall = walls[0] - } else { - prevWall = walls[index - 1] - nextWall = walls[index + 1] - } - - let p1 = [ - { x: currentWall.x1, y: currentWall.y1 }, - { x: currentWall.x2, y: currentWall.y2 }, - { x: prevWall.x1, y: prevWall.y1 }, - { x: prevWall.x2, y: prevWall.y2 }, - ], - p2 = [ - { x: currentWall.x1, y: currentWall.y1 }, - { x: currentWall.x2, y: currentWall.y2 }, - { x: nextWall.x1, y: nextWall.y1 }, - { x: nextWall.x2, y: nextWall.y2 }, - ] - - p1 = Array.from(new Set(p1.map((point) => JSON.stringify(point)))).map((point) => JSON.parse(point)) - p2 = Array.from(new Set(p2.map((point) => JSON.stringify(point)))).map((point) => JSON.parse(point)) - - let p1Lengths = [] - p1.forEach((point1, index) => { - let point2 - if (p1.length - 1 === index) { - point2 = p1[0] - } else { - point2 = p1[index + 1] - } - const dx = Math.abs(point2.x - point1.x) - const dy = Math.abs(point2.y - point1.y) - p1Lengths.push({ - length: Math.round(Math.sqrt(dx ** 2 + dy ** 2)), - coords: [ - { x: point1.x, y: point1.y }, - { x: point2.x, y: point2.y }, - ], - }) - }) - - let p2Lengths = [] - p2.forEach((point1, index) => { - let point2 - if (p2.length - 1 === index) { - point2 = p2[0] - } else { - point2 = p2[index + 1] - } - const dx = Math.abs(point2.x - point1.x) - const dy = Math.abs(point2.y - point1.y) - p2Lengths.push({ - length: Math.round(Math.sqrt(dx ** 2 + dy ** 2)), - coords: [ - { x: point1.x, y: point1.y }, - { x: point2.x, y: point2.y }, - ], - }) - }) - - let x1 = 0, - y1 = 0, - x2 = 0, - y2 = 0, - offsetX1 = 0, - offsetY1 = 0, - offsetX2 = 0, - offsetY2 = 0 - - let alfa = Math.round(Math.sqrt(Math.abs(currentWall.x1 - currentWall.x2) ** 2 + Math.abs(currentWall.y1 - currentWall.y2) ** 2)), - bravo = Math.round(Math.sqrt(Math.abs(prevWall.x1 - prevWall.x2) ** 2 + Math.abs(prevWall.y1 - prevWall.y2) ** 2)), - charlie = p1Lengths.filter((length) => length.length !== alfa && length.length !== bravo)[0], - alpha = Math.round(Math.acos((alfa ** 2 + charlie.length ** 2 - bravo ** 2) / (2 * alfa * charlie.length)) * (180 / Math.PI) * 100) / 100, - beta = Math.round(Math.acos((bravo ** 2 + charlie.length ** 2 - alfa ** 2) / (2 * bravo * charlie.length)) * (180 / Math.PI) * 100) / 100, - gamma = Math.round(Math.acos((alfa ** 2 + bravo ** 2 - charlie.length ** 2) / (2 * alfa * bravo)) * (180 / Math.PI) * 100) / 100, - isValley = checkValley(wall, currentWall, prevWall) - console.log('현재 라인 : ', alfa) - gamma = isValley ? Math.round(((360 - gamma) / 2) * 100) / 100 : Math.round((gamma / 2) * 100) / 100 - - console.log('이전 라인과의 각도', gamma) - - if (currentWall.x1 === currentWall.x2) { - offsetY1 = currentWall.attributes.width - offsetX1 = prevWall.attributes.width - x1 = Math.min(currentWall.x1, currentWall.x2) + offsetX1 - y1 = currentWall.y1 + offsetY1 - } else if (currentWall.y1 === currentWall.y2) { - offsetX1 = currentWall.attributes.width - offsetY1 = prevWall.attributes.width - x1 = currentWall.x1 + offsetX1 - y1 = Math.min(currentWall.y1, currentWall.y2) + offsetY1 - } else { - } - - bravo = Math.round(Math.sqrt(Math.abs(nextWall.x1 - nextWall.x2) ** 2 + Math.abs(nextWall.y1 - nextWall.y2) ** 2)) - charlie = p2Lengths.filter((length) => length.length !== alfa && length.length !== bravo)[0] - - // alpha = Math.round(Math.acos((alfa ** 2 + charlie ** 2 - bravo ** 2) / (2 * alfa * charlie)) * (180 / Math.PI)) - // betta = Math.round(Math.acos((bravo ** 2 + charlie ** 2 - alfa ** 2) / (2 * bravo * charlie)) * (180 / Math.PI)) - gamma = Math.round(Math.acos((alfa ** 2 + bravo ** 2 - charlie.length ** 2) / (2 * alfa * bravo)) * (180 / Math.PI) * 100) / 100 - isValley = checkValley(wall, currentWall, nextWall) - gamma = isValley ? Math.round(((360 - gamma) / 2) * 100) / 100 : Math.round((gamma / 2) * 100) / 100 - - console.log('다음 라인과의 각도', gamma) - - /*let offsetX = 0, - offsetY = 0 - let x1 = 0, - y1 = 0, - x2 = 0, - y2 = 0 - if (prevWall.direction !== nextWall.direction) { - if (currentWall.x1 === currentWall.x2) { - offsetX = prevWall.x1 < currentWall.x1 ? currentWall.attributes.width : -currentWall.attributes.width - x1 = currentWall.x1 - x2 = currentWall.x2 - y1 = Math.min(currentWall.y1, currentWall.y2) - Math.min(prevWall.attributes.width, nextWall.attributes.width) - y2 = Math.max(currentWall.y1, currentWall.y2) + Math.max(prevWall.attributes.width, nextWall.attributes.width) - } else { - offsetY = prevWall.y1 < currentWall.y1 ? currentWall.attributes.width : -currentWall.attributes.width - x1 = Math.min(currentWall.x1, currentWall.x2) - Math.min(prevWall.attributes.width, nextWall.attributes.width) - x2 = Math.max(currentWall.x1, currentWall.x2) + Math.max(prevWall.attributes.width, nextWall.attributes.width) - y1 = currentWall.y1 - y2 = currentWall.y2 - } - } else { - } - - x1 = x1 + offsetX - x2 = x2 + offsetX - y1 = y1 + offsetY - y2 = y2 + offsetY - - const roof = new QLine([x1, y1, x2, y2], { - fontSize: fontSize, - stroke: 'black', - strokeWidth: 1, - name: 'roofLine', - }) - canvas?.add(roof) - canvas?.renderAll()*/ - }) + return roof } /** @@ -1693,50 +1715,6 @@ export function useMode() { return isValley } - /** - * 구하려는 라인의 x1,y1좌표가 기준 - * @param line1 이전 라인 - * @param line2 현재 라인 - * @param line3 다음 라인 - * @param offset - * @returns {number} - */ - const getLineOffsetPoint = (line1, line2, line3, offset) => { - //밑변 - let a = Math.abs(line1.x - line2.x) - //빗변 - let c = Math.sqrt(Math.abs(line1.x - line2.x) ** 2 + Math.abs(line1.y - line2.y) ** 2) - - console.log(a, c) - //밑변과 빗변사이의 각도 - let alphaDegree = getDegreeBetweenTwoLines(line1, line2, line3) - alphaDegree = alphaDegree <= 90 ? alphaDegree : 180 - alphaDegree - alphaDegree = 90 - alphaDegree - console.log('alphaDegree : ', alphaDegree) - - // console.log('Math.tan(alphaDegree * (Math.PI / 180)) : ', Math.tan(alphaDegree * (Math.PI / 180))) - console.log(Math.round(offset * Math.tan(alphaDegree * (Math.PI / 180)))) - - const angle = getDegreeBetweenTwoLines(line1, line2, line3) - const side1 = line1.length - const side2 = line2.length - const side3 = Math.sqrt(side1 ** 2 + side2 ** 2 - 2 * side1 * side2 * Math.cos(angle * (Math.PI / 180))) - const beta = Math.round(Math.asin((side2 * Math.sin(angle * (Math.PI / 180))) / side3) * (180 / Math.PI) * 10) / 10 - const alpha = 180 - angle - beta - - console.log('angle : ', angle, 'alpha : ', alpha, 'beta : ', beta) - - const h = side2 * Math.sin(alpha * (Math.PI / 180)) - const h_new = h + offset - console.log('빗변까지 길이 : ', h, 'offset 적용된 빗변까지 길이 : ', h_new) - const newAdjacent = h_new / Math.sin(angle * (Math.PI / 180)) - console.log('offset 적용된 밑변 : ', newAdjacent) - - // offset = Math.sqrt(diffAdjacent ** 2 + diffAdjacent ** 2) / 2 - console.log('offset : ', offset) - return offset - } - /** * 구하려는 라인의 x1,y1좌표가 기준 * @param line1 이전 라인 diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index 1599905d..032ad5be 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -1206,10 +1206,6 @@ export const drawHippedRoof = (polygon, chon) => { } const drawRoofRidge = (polygon, chon) => { - const points = [] - polygon.wall.points.forEach((point) => points.push({ x: point.x, y: point.y })) - console.log('points : ', points) - const walls = polygon.wall.lines // 외벽의 라인 const roofs = polygon.lines // 지붕의 라인 let ridgeWall = []