From 9601c628243aea1d778bd8877337466b65d36ed5 Mon Sep 17 00:00:00 2001 From: yjnoh Date: Wed, 24 Jul 2024 18:08:54 +0900 Subject: [PATCH 01/27] =?UTF-8?q?=ED=8F=B4=EB=A6=AC=EA=B3=A4=20=ED=8C=A8?= =?UTF-8?q?=ED=84=B4=20=EB=B0=B0=EC=97=B4=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useMode.js | 238 +++++++++++++++++++++++++++++++------------ 1 file changed, 175 insertions(+), 63 deletions(-) diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index dab596c2..ef7c7cd0 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -1290,6 +1290,11 @@ export function useMode() { let vertCenterLine let secondVertCenterLine let templatePolygonObj = {} + let roofPatternPolygonArray = [] + + let bigRoofPolygon = [] + let middleRoofPolygon = [] + let smallRoofPolygon = [] if (prevHighIndex === 1) { if (horizontalDirection === 'left') { @@ -1415,7 +1420,7 @@ export function useMode() { let drawLastInLine2 = new QLine([secondVertCenterLine.x1, vertCenterLine.y2, vertCenterLine.x2, vertCenterLine.y2], centerLineOpt) canvas.add(drawLastInLine2) - const bigRoofPolygon = [ + bigRoofPolygon = [ { x: outLines[2].x1, y: outLines[2].y1 }, { x: outLines[2].x2, y: outLines[2].y2 }, { x: outLines[0].x1, y: outLines[0].y1 }, @@ -1424,20 +1429,19 @@ export function useMode() { { x: outLines[1].x2, y: outLines[1].y2 }, ] - const middleRoofPolygon = [ + middleRoofPolygon = [ { x: outLines[0].x2, y: outLines[0].y2 }, { x: outLines[0].x1, y: outLines[0].y1 }, { x: outLines[3].x2, y: outLines[3].y2 }, { x: outLines[3].x1, y: outLines[3].y1 }, ] - const smallRoofPolygon = [ + smallRoofPolygon = [ { x: outLines[1].x2, y: outLines[1].y2 }, { x: outLines[1].x1, y: outLines[1].y1 }, { x: outLines[4].x2, y: outLines[4].y2 }, { x: outLines[4].x1, y: outLines[4].y1 }, ] - setRoofPolygonPattern({ bigRoofPolygon, middleRoofPolygon, smallRoofPolygon, lines }) } else { //아래쪽 길게 오른쪽 방향 //배열 순서대로 뒤에꺼를 찾아서 계산한다 @@ -1565,7 +1569,7 @@ export function useMode() { let drawLastInLine2 = new QLine([secondVertCenterLine.x2, vertCenterLine.y1, vertCenterLine.x1, vertCenterLine.y1], centerLineOpt) canvas.add(drawLastInLine2) - const bigRoofPolygon = [ + bigRoofPolygon = [ { x: outLines[0].x1, y: outLines[0].y1 }, { x: outLines[0].x2, y: outLines[0].y2 }, { x: outLines[3].x1, y: outLines[3].y1 }, @@ -1574,20 +1578,19 @@ export function useMode() { { x: outLines[1].x2, y: outLines[0].y1 }, ] - const middleRoofPolygon = [ + middleRoofPolygon = [ { x: outLines[2].x1, y: outLines[2].y1 }, { x: outLines[2].x2, y: outLines[2].y2 }, { x: outLines[0].x2, y: outLines[0].y2 }, { x: outLines[0].x1, y: outLines[0].y1 }, ] - const smallRoofPolygon = [ + smallRoofPolygon = [ { x: outLines[4].x1, y: outLines[4].y1 }, { x: outLines[4].x2, y: outLines[4].y2 }, { x: outLines[1].x2, y: outLines[1].y2 }, { x: outLines[1].x1, y: outLines[1].y1 }, ] - setRoofPolygonPattern({ bigRoofPolygon, middleRoofPolygon, smallRoofPolygon, lines }) } } else { if (horizontalDirection === 'left') { @@ -1709,7 +1712,7 @@ export function useMode() { let drawLastInLine2 = new QLine([vertCenterLine.x1, vertCenterLine.y2, secondVertCenterLine.x2, vertCenterLine.y2], centerLineOpt) canvas.add(drawLastInLine2) - const bigRoofPolygon = [ + bigRoofPolygon = [ { x: outLines[2].x1, y: outLines[2].y1 }, { x: outLines[2].x2, y: outLines[2].y2 }, { x: outLines[1].x1, y: outLines[1].y1 }, @@ -1718,21 +1721,19 @@ export function useMode() { { x: outLines[0].x1, y: outLines[0].y1 }, ] - const middleRoofPolygon = [ + middleRoofPolygon = [ { x: outLines[0].x1, y: outLines[0].y1 }, { x: outLines[0].x2, y: outLines[0].y2 }, { x: outLines[4].x2, y: outLines[4].y2 }, { x: outLines[4].x1, y: outLines[4].y1 }, ] - const smallRoofPolygon = [ + smallRoofPolygon = [ { x: outLines[1].x2, y: outLines[1].y2 }, { x: outLines[1].x1, y: outLines[1].y1 }, { x: outLines[3].x2, y: outLines[3].y2 }, { x: outLines[3].x1, y: outLines[3].y1 }, ] - - setRoofPolygonPattern({ bigRoofPolygon, middleRoofPolygon, smallRoofPolygon, lines }) } else { //윗쪽 길게 오른쪽 방향 //배열 순서대로 뒤에꺼를 찾아서 계산한다 @@ -1854,7 +1855,7 @@ export function useMode() { let drawLastInLine2 = new QLine([secondLine.x2 - eaves, secondLine.y1, drawLastLine1.x2, secondLine.y1], centerLineOpt) canvas.add(drawLastInLine2) - const bigRoofPolygon = [ + bigRoofPolygon = [ { x: outLines[0].x1, y: outLines[0].y1 }, { x: outLines[0].x2, y: outLines[0].y2 }, { x: outLines[1].x1, y: outLines[0].y2 }, @@ -1863,24 +1864,28 @@ export function useMode() { { x: outLines[4].x2, y: outLines[4].y2 }, ] - const middleRoofPolygon = [ + middleRoofPolygon = [ { x: outLines[2].x1, y: outLines[2].y1 }, { x: outLines[2].x2, y: outLines[2].y2 }, { x: outLines[0].x2, y: outLines[0].y2 }, { x: outLines[0].x1, y: outLines[0].y1 }, ] - const smallRoofPolygon = [ + smallRoofPolygon = [ { x: outLines[3].x1, y: outLines[3].y1 }, { x: outLines[3].x2, y: outLines[3].y2 }, { x: outLines[1].x2, y: outLines[1].y2 }, { x: outLines[1].x1, y: outLines[1].y1 }, ] - - setRoofPolygonPattern({ bigRoofPolygon, middleRoofPolygon, smallRoofPolygon, lines }) } } + roofPatternPolygonArray.push(bigRoofPolygon) + roofPatternPolygonArray.push(middleRoofPolygon) + roofPatternPolygonArray.push(smallRoofPolygon) + + setRoofPolygonPattern({ roofPatternPolygonArray, lines }) + canvas.renderAll() } @@ -1893,6 +1898,8 @@ export function useMode() { let lines = [] //내각라인 let outLines = [] //아웃라인 let halfLength = 0 //선길이 + let offsetX + let offsetY const dashedCenterLineOpt = { stroke: 'black', @@ -1945,6 +1952,32 @@ export function useMode() { y: line.y1, })) + //좌표 재정렬 + function reSortQlineArray(array) { + let tmpArray = [] + array.forEach((arr, index) => { + let minX, minY, maxX, maxY + let tmp = arr + if (arr.x2 < arr.x1 || arr.y2 < arr.y1) { + minX = arr.x2 + minY = arr.y2 + maxX = arr.x1 + maxY = arr.y1 + tmp['x1'] = minX + tmp['y1'] = minY + tmp['x2'] = maxX + tmp['y2'] = maxY + tmp.line['x1'] = minX + tmp.line['y1'] = minY + tmp.line['x2'] = maxX + tmp.line['y2'] = maxY + } + tmpArray.push(tmp) + }) + + return tmpArray + } + // 외적을 계산하는 함수 function crossProduct(p1, p2, p3) { const dx1 = p2.x - p1.x @@ -1977,7 +2010,10 @@ export function useMode() { // 오목한 부분 인덱스 찾기 concaveIndices = findConcavePointIndices(points) //오목한 부분을 제외한 인덱스 const concavePoints = concaveIndices.map((index) => points[index]) - + const concaveLine = { + index: concavePointIndices[0], + line: lines[concavePointIndices[0]], + } for (var i = 0; i < points.length; i++) { var prev = points[(i - 1 + points.length) % points.length] var current = points[i] @@ -2012,11 +2048,8 @@ export function useMode() { y: averageNormal.y / lengthNormal, } - let offsetX - let offsetY - if (concavePointIndices[0] === i || concavePointIndices[1] === i) { - // 인덱스가 배열이랑 같으면 + //인덱스가 배열이랑 같으면 if ((concavePointIndices[0] === 4 && concavePointIndices[1] === 5) || (concavePointIndices[0] === 2 && concavePointIndices[1] === 3)) { offsetX = 1 offsetY = (offsetInputY / transformedMax) * originalMax * 2 @@ -2058,35 +2091,121 @@ export function useMode() { } canvas?.remove(outlinePolygon) //임시 폴리곤을 삭제 - canvas?.remove(outLines[concavePointIndices[0]]) //가운데 제외되는 선을 지운다 //라인들을 좌측에서 -> 우측으로 그리는거처럼 데이터 보정 - outLines.forEach((outline, index) => { - let minX, minY, maxX, maxY - if (outline.x2 < outline.x1 || outline.y2 < outline.y1) { - outLines[index].x1 = outline.x2 - outLines[index].y1 = outline.y2 - outLines[index].x2 = outline.x1 - outLines[index].y2 = outline.y1 - outLines[index].line.x1 = minX - outLines[index].line.y1 = minY - outLines[index].line.x2 = maxX - outLines[index].line.y2 = maxY + outLines = reSortQlineArray(outLines) + + let parallelLinesIdx = concavePointIndices[0] + 4 //들어간선에 무조건 평행하는 선 찾기 + let parallelLines = outLines[parallelLinesIdx] + if (parallelLinesIdx >= outLines.length) { + parallelLines = outLines[parallelLinesIdx - outLines.length] + parallelLinesIdx = parallelLinesIdx - outLines.length + } + + let vertCenterLine = [] + let halfHoriCenterLinePoint = [] //카라바선의 2분할의 1번 배열 + let horiCenterLine = [] + let shorVertCenterLine = [] + + if (concavePointIndices[0] % 2 === 0) { + // 오목한 부분이 세로선일때 ㄷ, 역ㄷ + } else { + // 오목한 부분이 세로선일때 ㄷ, 역ㄷ + outLines.forEach((outline, index) => { + if (!(index % 2 === 0)) { + //세로라인이 케라바 라인임 + + if (concavePointIndices[0] !== index) { + //오목이가 아니면 반으로 갈라서 계산 + + //카라바 선의 2분할 치수를 그림 + let halfLength = outline.length / 2 + let centerLine1 = new QLine([outline.x1, outline.y1, outline.x1 + halfLength, outline.y1], { + stroke: 'red', + strokeWidth: 2, + property: 'normal', + fontSize: 14, + }) + canvas.add(centerLine1) + + let centerLine2 = new QLine([centerLine1.x2, outline.y1, centerLine1.x2 + halfLength, outline.y1], { + stroke: 'red', + strokeWidth: 2, + property: 'normal', + fontSize: 14, + }) + canvas.add(centerLine2) + canvas.remove(outline) //기존 라인 삭제 + + halfHoriCenterLinePoint.push({ index: index, x1: centerLine1.x1, y1: centerLine1.y1, x2: centerLine1.x2, y2: centerLine1.y2 }) //각 카라바 라인의 1번이 마지막점을 잡아서 센터선으로 설정 + } + } + }) + + //각 센터 라인을 그림 + halfHoriCenterLinePoint.forEach((centerPoint) => { + let line = new QLine([centerPoint.x2, centerPoint.y1, centerPoint.x2, concaveLine.line.y1], centerLineOpt) + canvas.add(line) + + line['arrayIndex'] = centerPoint.index //커스텀으로 기존 index를 넣어줌 + vertCenterLine.push(line) + }) + + vertCenterLine = reSortQlineArray(vertCenterLine) + + //해당라인에서 만나는점을 계산 + vertCenterLine.forEach((vertLine) => { + if (parallelLinesIdx !== vertLine.arrayIndex) { + //평행선을 제외한 애들만 네모를 연결 + let nearLine + + if (vertLine.arrayIndex > concaveLine.index) { + //센터에 인덱스가 오목점 보다 크면 다음 작으면 앞에꺼 + nearLine = outLines[concaveLine.index + 1] + } else { + nearLine = outLines[concaveLine.index - 1] + } + + let nearLineY = nearLine.y1 + if (parallelLinesIdx < concaveLine.index) { + //오목점 위치가 평행선보다 크면 위쪽으로 오목 + nearLineY = nearLine.y2 + } + + let centerExtendLine = new QLine([vertLine.line.x1, nearLineY, nearLine.x1, nearLineY], centerLineOpt) + canvas.add(centerExtendLine) + + let betweenCenterLine = (vertLine.line.y1 + vertLine.line.y2) / 2 + let centerDashLine = new QLine([vertLine.line.x1, betweenCenterLine, nearLine.x1, betweenCenterLine], dashedCenterLineOpt) + + canvas.add(centerDashLine) + horiCenterLine.push(centerDashLine) + shorVertCenterLine.push(vertLine) //마지막에 가운데 선을 긋기 위해 담음 + } else { + let longDashLine = halfHoriCenterLinePoint.find((obj) => obj.index === parallelLinesIdx) + + let dashCenterExtendLineLength = (longDashLine.x2 - longDashLine.x1) * 2 + let betweenCenterLine = (vertLine.line.y1 + vertLine.line.y2) / 2 + + let centerDashLine = new QLine( + [longDashLine.x1, betweenCenterLine, longDashLine.x1 + dashCenterExtendLineLength, betweenCenterLine], + dashedCenterLineOpt, + ) + canvas.add(centerDashLine) + horiCenterLine.push(centerDashLine) + } + }) + + if (parallelLinesIdx < concaveLine.index) { + offsetY = offsetY * -1 // 위로올린다 } - }) - // for (let i = 0; i < outLines.length; i++) { - // if (!i % 2 === 0) { - // if (i === concavePointIndices[0] - 1 || i === concavePointIndices[1] + 1) { - // //배열 3번이나 5번일때 - // } else { - // } - // } - // } - - let parallelLines = concavePointIndices[0] + 4 //들어간선에 무조건 평행하는 선 찾기 - if (parallelLines > outLines.length) { - parallelLines = outLines[concavePointIndices[0] + 4 - outLines.length - 1] + const lastInLine = new QLine( + [shorVertCenterLine[0].x1, lines[concavePointIndices[0]].y1 + offsetY, shorVertCenterLine[1].x1, lines[concavePointIndices[0]].y2 + offsetY], + centerLineOpt, + ) + canvas.add(lastInLine) + canvas.remove(outLines[concavePointIndices[0]]) } canvas.renderAll() @@ -2512,6 +2631,8 @@ export function useMode() { } const makeRoofPatternPolygon = (roofStyle) => { + console.log('roofPolygonPattern', roofPolygonPattern) + if (Object.keys(roofPolygonPattern).length === 0 && roofPolygonPattern.constructor === Object) { alert('객체가 비어있습니다.') return @@ -2561,21 +2682,12 @@ export function useMode() { fontSize: 15, // fontSize는 필요에 따라 조정 } - const bigRoof = new QPolygon(roofPolygonPattern.bigRoofPolygon, commonOption) - const middleRoof = new QPolygon(roofPolygonPattern.middleRoofPolygon, commonOption) - const smallRoof = new QPolygon(roofPolygonPattern.smallRoofPolygon, commonOption) - - bigRoof.setViewLengthText(false) //치수 필요없음 - middleRoof.setViewLengthText(false) - smallRoof.setViewLengthText(false) - - bigRoof.sendToBack() //객체를 가장 뒤로 - middleRoof.sendToBack() - smallRoof.sendToBack() - - canvas.add(bigRoof) //캔버스 객체 추가 - canvas.add(middleRoof) - canvas.add(smallRoof) + roofPolygonPattern.roofPatternPolygonArray.forEach((patternPolygon, index) => { + const drawPolygon = new QPolygon(patternPolygon, commonOption) + drawPolygon.setViewLengthText(false) + drawPolygon.sendToBack() + canvas.add(drawPolygon) + }) canvas.renderAll() } From f243cd2d74ca07c2a65a6d7cb075562d4e19ecc4 Mon Sep 17 00:00:00 2001 From: yoosangwook Date: Thu, 25 Jul 2024 13:19:48 +0900 Subject: [PATCH 02/27] =?UTF-8?q?feature:=20=ED=85=9C=ED=94=8C=EB=A6=BFB?= =?UTF-8?q?=20=EC=BC=80=EC=9D=B4=EC=8A=A4=20=EC=A7=80=EB=B6=95=ED=8C=A8?= =?UTF-8?q?=ED=84=B4=20=EC=9E=85=ED=9E=88=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useMode.js | 107 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 106 insertions(+), 1 deletion(-) diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index ef7c7cd0..a540773e 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -2226,8 +2226,10 @@ export function useMode() { // handleOuterLineTemplateB(params) console.log(polygon.lines.length) if (polygon.lines.length === 4) { + console.log('4각형') handleTemplateBRect(params) - } else if (polygon.length === 6) { + } else if (polygon.lines.length === 6) { + console.log('6각형') handleTemplateB(params) } } @@ -2259,6 +2261,9 @@ export function useMode() { isActiveLengthText: false, } + const bigRoofPolygon = [] + const middleRoofPolygon = [] + polygon.lines.forEach((line, index) => { let outline if (index === 0) { @@ -2269,6 +2274,9 @@ export function useMode() { centerLinePoint.y2 = centeredPoint centerDashLinePoint.y1 = line.y1 - eaves centerDashLinePoint.y2 = line.y2 + eaves + bigRoofPolygon[0] = { x: line.x1 - edge, y: line.y1 - eaves } + middleRoofPolygon[0] = { x: line.x1 - edge, y: centeredPoint } + middleRoofPolygon[1] = { x: line.x2 - edge, y: line.y2 + eaves } } else if (index === 1) { outline = new QLine([line.x1 - edge, line.y1 + eaves, line.x2 + edge, line.y2 + eaves], qlineOpt) const centeredPoint = getCenterPoint(line.x1, line.x2) @@ -2277,6 +2285,8 @@ export function useMode() { centerDashLinePoint.x2 = centeredPoint } else if (index === 2) { outline = new QLine([line.x1 + edge, line.y1 + eaves, line.x2 + edge, line.y2 - eaves], qlineOpt) + bigRoofPolygon[3] = { x: line.x2 + edge, y: line.y2 - eaves } + middleRoofPolygon[2] = { x: line.x1 + edge, y: line.y1 + eaves } } else if (index === 3) { outline = new QLine([line.x1 + edge, line.y1 - eaves, line.x2 - edge, line.y2 - eaves], qlineOpt) } @@ -2295,6 +2305,18 @@ export function useMode() { ) canvas.add(centerDashLine2) + bigRoofPolygon[1] = { x: centerLine.x1, y: centerLine.y1 } + bigRoofPolygon[2] = { x: centerLine.x2, y: centerLine.y2 } + + middleRoofPolygon[3] = { x: centerLinePoint.x2, y: centerLinePoint.y2 } + + const roofPatternPolygonArray = [] + roofPatternPolygonArray.push(bigRoofPolygon) + roofPatternPolygonArray.push(middleRoofPolygon) + if (roofPatternPolygonArray.length > 0) { + setRoofPolygonPattern({ roofPatternPolygonArray, lines: polygon.lines }) + } + canvas.renderAll() } @@ -2354,6 +2376,13 @@ export function useMode() { fontSize: fontSize, } + /** + * 지붕 패턴을 위한 폴리곤 좌표 생성 + */ + const bigRoofPolygon = [] + const middleRoofPolygon = [] + const smallRoofPolygon = [] + rerangeOdd.forEach((line, index) => { const centeredPoint = getCenterPoint(line.y1, line.y2) let points1 = [] @@ -2367,26 +2396,50 @@ export function useMode() { centralLinePoint.y2 = centeredPoint centralDashLinePoint.y1 = line.y1 - eaves centralDashLinePoint.y2 = line.y2 + eaves + if (polygon.shape === 2) { + middleRoofPolygon[0] = { x: line.x1 - edge, y: line.y1 - eaves } + middleRoofPolygon[1] = { x: line.x1 - edge, y: centeredPoint } + } else { + bigRoofPolygon[1] = { x: line.x1 - edge, y: centeredPoint } + bigRoofPolygon[0] = { x: line.x1 - edge, y: line.y1 - eaves } + middleRoofPolygon[1] = { x: line.x2 - edge, y: line.y2 + eaves } + } } else if (index === 1) { if (polygon.shape === 2) { points1 = [line.x1 + edge, line.y1 - eaves, line.x1 + edge, centeredPoint] points2 = [line.x1 + edge, centeredPoint, line.x2 + edge, line.y2 + eaves] centralSubLinePoint.x2 = line.x1 + edge centralSubLinePoint.y2 = centeredPoint + bigRoofPolygon[2] = { x: line.x2 + edge, y: line.y2 + eaves } + bigRoofPolygon[3] = { x: line.x2 + edge, y: centeredPoint } + smallRoofPolygon[0] = { x: line.x1 + edge, y: centeredPoint } + smallRoofPolygon[1] = { x: line.x1 + edge, y: line.y1 - eaves } } else { points1 = [line.x1 + edge, getCenterPoint(rerangeOdd[2].y1, rerangeOdd[2].y2), line.x2 + edge, line.y2 + eaves] points2 = [line.x1, getCenterPoint(rerangeOdd[2].y1, rerangeOdd[2].y2), line.x1, line.y1 + eaves] centralLinePoint.x2 = line.x1 + edge centralSubLinePoint.y1 = getCenterPoint(rerangeOdd[2].y1, rerangeOdd[2].y2) centralSubLinePoint.y2 = getCenterPoint(rerangeOdd[2].y1, rerangeOdd[2].y2) + bigRoofPolygon[3] = { x: line.x1 + edge, y: centralSubLinePoint.y1 } + middleRoofPolygon[2] = { x: line.x2 + edge, y: line.y2 + eaves } + smallRoofPolygon[0] = { x: line.x1, y: centralSubLinePoint.y1 } + smallRoofPolygon[1] = { x: line.x1, y: line.y1 + eaves } } } else if (index === 2) { if (polygon.shape === 2) { points1 = [line.x1 + edge, line.y1 - eaves, line.x2 + edge, centralSubLinePoint.y2] points2 = [line.x2, line.y2 - eaves, line.x2, centralSubLinePoint.y2] + bigRoofPolygon[4] = { x: line.x2 + edge, y: centralSubLinePoint.y2 } + middleRoofPolygon[3] = { x: line.x1 + edge, y: line.y1 - eaves } + smallRoofPolygon[2] = { x: line.x2, y: line.y2 - eaves } + smallRoofPolygon[3] = { x: line.x2, y: centralSubLinePoint.y2 } } else { points1 = [line.x1 + edge, line.y1 - eaves, line.x2 + edge, centeredPoint] points2 = [line.x2 + edge, centeredPoint, line.x2 + edge, line.y2 + eaves] + bigRoofPolygon[4] = { x: line.x1 + edge, y: centralSubLinePoint.y2 } + bigRoofPolygon[5] = { x: line.x1 + edge, y: line.y1 - eaves } + smallRoofPolygon[2] = { x: line.x2 + edge, y: line.y2 + eaves } + smallRoofPolygon[3] = { x: line.x2 + edge, y: centeredPoint } } } } else { @@ -2395,12 +2448,26 @@ export function useMode() { centralSubLinePoint.x1 = line.x1 - edge centralSubLinePoint.y1 = getCenterPoint(line.y1, line.y2) centralSubLinePoint.y2 = getCenterPoint(line.y1, line.y2) + if (polygon.shape === 1) { + bigRoofPolygon[0] = { x: line.x1 - edge, y: line.y1 - eaves } + bigRoofPolygon[1] = { x: line.x1 - edge, y: getCenterPoint(line.y1, line.y2) } + smallRoofPolygon[0] = { x: line.x1 - edge, y: getCenterPoint(line.y1, line.y2) } + smallRoofPolygon[1] = { x: line.x2 - edge, y: line.y2 + eaves } + } else { + bigRoofPolygon[0] = { x: line.x1 - edge, y: centeredPoint } + bigRoofPolygon[1] = { x: line.x1 - edge, y: line.y2 + eaves } + smallRoofPolygon[0] = { x: line.x1 - edge, y: line.y1 - eaves } + smallRoofPolygon[1] = { x: centralSubLinePoint.x1, y: centralSubLinePoint.y1 } + } } else if (index === 1) { if (polygon.shape === 1) { points1 = [line.x1 - edge, centralSubLinePoint.y1, line.x2 - edge, line.y2 + eaves] points2 = [line.x1, centralSubLinePoint.y1, line.x1, line.y1 + eaves] centralLinePoint.x1 = line.x1 - edge centralSubLinePoint.x2 = line.x2 + bigRoofPolygon[2] = { x: line.x1 - edge, y: centralSubLinePoint.y2 } + middleRoofPolygon[1] = { x: line.x2 - edge, y: line.y2 + eaves } + smallRoofPolygon[2] = { x: line.x1, y: line.y1 + eaves } } else { points1 = [line.x1 + edge, line.y1 - eaves, line.x2 + edge, centeredPoint] points2 = [line.x2 + edge, centeredPoint, line.x2 + edge, line.y2 + eaves] @@ -2409,6 +2476,9 @@ export function useMode() { centralLinePoint.y2 = centeredPoint centralDashLinePoint.y1 = line.y1 - eaves centralDashLinePoint.y2 = line.y2 + eaves + bigRoofPolygon[2] = { x: line.x2 + edge, y: line.y2 + eaves } + bigRoofPolygon[3] = { x: line.x2 + edge, y: centralLinePoint.y2 } + middleRoofPolygon[3] = { x: line.x1 + edge, y: line.y1 - eaves } } } else { if (polygon.shape === 1) { @@ -2419,11 +2489,18 @@ export function useMode() { centralLinePoint.y2 = centeredPoint centralDashLinePoint.y1 = line.y1 - eaves centralDashLinePoint.y2 = line.y2 + eaves + bigRoofPolygon[5] = { x: line.x1 + edge, y: line.y1 - eaves } + middleRoofPolygon[2] = { x: line.x2 + edge, y: line.y2 + eaves } } else { points1 = [line.x1 - edge, line.y1 - eaves, line.x2 - edge, centralSubLinePoint.y1] points2 = [line.x2, line.y2 - eaves, line.x2, centralSubLinePoint.y2] centralLinePoint.x1 = line.x1 - edge centralSubLinePoint.x2 = line.x2 + bigRoofPolygon[4] = { x: line.x2 - edge, y: centralLinePoint.y1 } + bigRoofPolygon[5] = { x: line.x2 - edge, y: centralSubLinePoint.y2 } + middleRoofPolygon[0] = { x: line.x1 - edge, y: line.y1 - eaves } + smallRoofPolygon[2] = { x: line.x2, y: centralSubLinePoint.y2 } + smallRoofPolygon[3] = { x: line.x2, y: line.y2 - eaves } } } } @@ -2549,6 +2626,34 @@ export function useMode() { ) canvas.add(centralSubDashLine) + if (polygon.shape === 1) { + bigRoofPolygon[3] = { x: centralLine.x1, y: centralLine.y1 } + bigRoofPolygon[4] = { x: centralLine.x2, y: centralLine.y2 } + middleRoofPolygon[0] = { x: centralLine.x1, y: centralLine.y1 } + middleRoofPolygon[3] = { x: centralLine.x2, y: centralLine.y2 } + smallRoofPolygon[3] = { x: centralSubLinePoint.x2, y: centralSubLinePoint.y2 } + } else if (polygon.shape === 2) { + bigRoofPolygon[0] = { x: centralLinePoint.x1, y: centralLinePoint.y1 } + bigRoofPolygon[1] = { x: centralLinePoint.x1, y: centralDashLinePoint.y2 } + bigRoofPolygon[5] = { x: centralLinePoint.x2, y: centralLinePoint.y2 } + middleRoofPolygon[2] = { x: centralLine.x2, y: centralLine.y2 } + } else if (polygon.shape === 3) { + bigRoofPolygon[2] = { x: centralLine.x2, y: centralLine.y2 } + middleRoofPolygon[0] = { x: centralLine.x1, y: centralLine.y1 } + middleRoofPolygon[3] = { x: centralLine.x2, y: centralLine.y2 } + } else if (polygon.shape === 4) { + middleRoofPolygon[1] = { x: centralLine.x1, y: centralLine.y1 } + middleRoofPolygon[2] = { x: centralLine.x2, y: centralLine.y2 } + } + + const roofPatternPolygonArray = [] + // if (bigRoofPolygon.length > 0 && middleRoofPolygon.length > 0 && smallRoofPolygon.length > 0) { + roofPatternPolygonArray.push(bigRoofPolygon) + roofPatternPolygonArray.push(middleRoofPolygon) + roofPatternPolygonArray.push(smallRoofPolygon) + setRoofPolygonPattern({ roofPatternPolygonArray, lines: polygon.lines }) + // } + canvas.renderAll() } From adbf79ecd3884bfd1485e701ddaef924e2f823be Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Fri, 26 Jul 2024 12:59:27 +0900 Subject: [PATCH 03/27] =?UTF-8?q?=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EB=B0=8F=20=EC=A7=80=EB=B6=95=EC=84=A0=20=EA=B7=B8?= =?UTF-8?q?=EB=A6=AC=EA=B8=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Roof2.jsx | 2 +- src/components/fabric/QLine.js | 45 ++- src/components/fabric/QPolygon.js | 15 +- src/hooks/useMode.js | 105 +++++- src/util/canvas-util.js | 23 ++ src/util/qpolygon-utils.js | 581 +++++++++++++++--------------- 6 files changed, 456 insertions(+), 315 deletions(-) diff --git a/src/components/Roof2.jsx b/src/components/Roof2.jsx index 004f03db..370a1e2e 100644 --- a/src/components/Roof2.jsx +++ b/src/components/Roof2.jsx @@ -208,7 +208,7 @@ export default function Roof2() { ] if (canvas) { - const polygon = new QPolygon(eightPoint, { + const polygon = new QPolygon(type1, { fill: 'transparent', stroke: 'black', strokeWidth: 1, diff --git a/src/components/fabric/QLine.js b/src/components/fabric/QLine.js index 98948ac4..9c8c2133 100644 --- a/src/components/fabric/QLine.js +++ b/src/components/fabric/QLine.js @@ -36,28 +36,36 @@ export class QLine extends fabric.Group { } const line = new fabric.Line(points, { ...option, strokeWidth: 1 }) - super([line], {}) - this.initPoints = points - this.initOption = option - this.initLengthTxt = lengthTxt + // 길이가 1이하인 선은 생성하지 않음 + if (Math.abs(x1 - x2) <= 1 && Math.abs(y1 - y2) <= 1) { + super([], {}) + this.initPoints = points + this.initOption = option + this.initLengthTxt = lengthTxt + } else { + super([line], {}) + this.initPoints = points + this.initOption = option + this.initLengthTxt = lengthTxt - this.x1 = x1 - this.y1 = y1 - this.x2 = x2 - this.y2 = y2 - this.line = line - this.fontSize = option.fontSize - this.direction = option.direction ?? getDirectionByPoint({ x: x1, y: y1 }, { x: x2, y: y2 }) - this.parent = option.parent - this.idx = option.idx + this.x1 = x1 + this.y1 = y1 + this.x2 = x2 + this.y2 = y2 + this.line = line + this.fontSize = option.fontSize + this.direction = option.direction ?? getDirectionByPoint({ x: x1, y: y1 }, { x: x2, y: y2 }) + this.parent = option.parent + this.idx = option.idx - if (lengthTxt > 0) { - this.lengthTxt = Number(lengthTxt) + if (lengthTxt > 0) { + this.lengthTxt = Number(lengthTxt) + } + + option.isActiveLengthText ?? this.init() + this.addControl() } - - option.isActiveLengthText ?? this.init() - this.addControl() } init() { @@ -156,6 +164,7 @@ export class QLine extends fabric.Group { initPoints: this.initPoints, initOption: this.initOption, initLengthTxt: this.initLengthTxt, + points: this.points, }) } diff --git a/src/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js index f903289e..b10c8465 100644 --- a/src/components/fabric/QPolygon.js +++ b/src/components/fabric/QPolygon.js @@ -12,7 +12,7 @@ import { sortedPoints, } from '@/util/canvas-util' import { QLine } from '@/components/fabric/QLine' -import { drawHelpLineInHexagon2 } from '@/util/qpolygon-utils' +import { drawHelpLineInHexagon } from '@/util/qpolygon-utils' export default class QPolygon extends fabric.Group { type = 'QPolygon' @@ -73,6 +73,7 @@ export default class QPolygon extends fabric.Group { stroke: this.stroke, strokeWidth: this.strokeWidth, fontSize: this.fontSize, + idx: i, direction: getDirectionByPoint(point, nextPoint), }) @@ -321,19 +322,20 @@ export default class QPolygon extends fabric.Group { // 보조선 그리기 drawHelpLine(chon = 4) { - if (!this.isValid()) { + drawHelpLineInHexagon(this, chon) + /*if (!this.isValid()) { return - } + }*/ - if (this.lines.length === 4) { + /*if (this.lines.length === 4) { this.drawHelpLineInRect(chon) } else if (this.lines.length === 6 || this.lines.length === 8) { // TODO : 6각형 drawHelpLineInHexagon2(this, chon) - } /* else if (this.lines.length === 8) { + } /!* else if (this.lines.length === 8) { // TODO : 8각형 this.drawHelpLineInOctagon(chon) - }*/ + }*!/*/ } /** @@ -925,6 +927,7 @@ export default class QPolygon extends fabric.Group { initPoints: this.initPoints, initOption: this.initOption, objects: this.getObjects().map((obj) => obj.toObject(propertiesToInclude)), + pattern: this.pattern, }) } diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index dab596c2..9d3743df 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -4,8 +4,9 @@ import QPolygon from '@/components/fabric/QPolygon' import { findTopTwoIndexesByDistance, getCenterPoint, getDirection, getStartIndex, rearrangeArray } from '@/util/canvas-util' import { useRecoilState } from 'recoil' -import { fontSizeState, roofPolygonPatternArrayState, roofState, sortedPolygonArray, wallState } from '@/store/canvasAtom' +import { canvasSizeState, fontSizeState, roofPolygonPatternArrayState, roofState, sortedPolygonArray, wallState } from '@/store/canvasAtom' import { QLine } from '@/components/fabric/QLine' +import { fabric } from 'fabric' export const Mode = { DRAW_LINE: 'drawLine', // 기준선 긋기모드 @@ -32,8 +33,12 @@ export function useMode() { const [roof, setRoof] = useRecoilState(roofState) const [wall, setWall] = useRecoilState(wallState) + const [endPoint, setEndPoint] = useState(null) + const [roofPolygonPattern, setRoofPolygonPattern] = useRecoilState(roofPolygonPatternArrayState) + const [canvasSize] = useRecoilState(canvasSizeState) + useEffect(() => { // 이벤트 리스너 추가 if (!canvas) { @@ -47,6 +52,93 @@ export function useMode() { } }, [canvas]) // 빈 배열을 전달하여 컴포넌트가 마운트될 때만 실행되도록 함 + useEffect(() => { + if (canvas?.getObjects().find((obj) => obj.name === 'connectLine')) { + canvas?.remove(canvas?.getObjects().find((obj) => obj.name === 'connectLine')) + } + canvas?.off('mouse:move', (e) => addLineEndPointToMousePoint(e, endPoint)) + canvas?.off('mouse:move') + canvas?.on('mouse:move', drawMouseLines) + canvas?.off('mouse:out', removeMouseLines) + canvas?.on('mouse:out', removeMouseLines) + if (!endPoint) { + return + } + + canvas?.on('mouse:move', (e) => addLineEndPointToMousePoint(e, endPoint)) + }, [endPoint]) + + const drawMouseLines = (e) => { + // 현재 마우스 포인터의 위치를 가져옵니다. + const pointer = canvas?.getPointer(e.e) + + // 기존에 그려진 가이드라인을 제거합니다. + removeMouseLines() + + if (canvas?.getActiveObject()) { + return + } + + // 가로선을 그립니다. + const horizontalLine = new fabric.Line([0, pointer.y, canvasSize.horizontal, pointer.y], { + stroke: 'black', + strokeWidth: 1, + selectable: false, + name: 'mouseLine', + }) + + // 세로선을 그립니다. + const verticalLine = new fabric.Line([pointer.x, 0, pointer.x, canvasSize.vertical], { + stroke: 'black', + strokeWidth: 1, + selectable: false, + name: 'mouseLine', + }) + + // 선들을 캔버스에 추가합니다. + canvas?.add(horizontalLine, verticalLine) + + // 캔버스를 다시 그립니다. + canvas?.renderAll() + } + + /** + * 마우스 포인터의 가이드라인을 제거합니다. + */ + const removeMouseLines = () => { + if (canvas?._objects.length > 0) { + const mouseLines = canvas?._objects.filter((obj) => obj.name === 'mouseLine') + mouseLines.forEach((item) => canvas?.remove(item)) + } + canvas?.renderAll() + } + + const addLineEndPointToMousePoint = (e, endPoint) => { + if (canvas?.getObjects().find((obj) => obj.name === 'connectLine')) { + canvas?.remove(canvas?.getObjects().find((obj) => obj.name === 'connectLine')) + } + + if (!endPoint) { + return + } + + const pointer = canvas?.getPointer(e.e) + + // 마우스 포인터 위치랑 endPoint를 연결하는 line 생성 + const line = new QLine([endPoint.left, endPoint.top, pointer.x, pointer.y], { + stroke: 'black', + strokeWidth: 2, + selectable: false, + viewLengthText: true, + fontSize: fontSize, + }) + + line.set({ name: 'connectLine' }) + + canvas?.add(line) + canvas?.renderAll() + } + const addEvent = (mode) => { switch (mode) { case 'default': @@ -98,6 +190,7 @@ export function useMode() { originY: 'center', selectable: false, }) + historyPoints.current.push(circle) points.current.push(circle) canvas?.add(circle) @@ -142,6 +235,8 @@ export function useMode() { selectable: false, }) + setEndPoint(endPointCircle) + canvas?.add(endPointCircle) historyPoints.current.push(endPointCircle) @@ -221,10 +316,12 @@ export function useMode() { } const changeMode = (canvas, mode) => { + setEndPoint(null) setMode(mode) // mode변경 시 이전 이벤트 제거 setCanvas(canvas) canvas?.off('mouse:down') + addEvent(mode) } @@ -242,6 +339,8 @@ export function useMode() { selectable: false, }) + setEndPoint(circle) + historyPoints.current.push(circle) points.current.push(circle) canvas?.add(circle) @@ -308,7 +407,7 @@ export function useMode() { originY: 'center', selectable: false, }) - + setEndPoint(endPointCircle) canvas?.add(endPointCircle) historyPoints.current.push(endPointCircle) @@ -556,7 +655,7 @@ export function useMode() { if (!otherLines) { // polygon.fillCell() canvas.renderAll() - polygon.setViewLengthText(false) + // polygon.setViewLengthText(false) setMode(Mode.DEFAULT) } diff --git a/src/util/canvas-util.js b/src/util/canvas-util.js index 072f7bab..e7bdb373 100644 --- a/src/util/canvas-util.js +++ b/src/util/canvas-util.js @@ -649,3 +649,26 @@ export function removeDuplicatePoints(points) { return uniquePoints } + +/** + * x,y가 다르면서 가장 가까운 점 + * @param targetPoint + * @param points + * @returns {null} + */ +export function findClosestPointWithDifferentXY(targetPoint, points) { + let closestPoint = null + let smallestDistance = Infinity + + points.forEach((point) => { + if (point.x !== targetPoint.x && point.y !== targetPoint.y) { + const distance = Math.sqrt(Math.pow(point.x - targetPoint.x, 2) + Math.pow(point.y - targetPoint.y, 2)) + if (distance < smallestDistance) { + smallestDistance = distance + closestPoint = point + } + } + }) + + return closestPoint +} diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index e683e4be..bd5676bd 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -1,7 +1,19 @@ import { fabric } from 'fabric' import QPolygon from '@/components/fabric/QPolygon' import { QLine } from '@/components/fabric/QLine' -import { calculateIntersection, calculateIntersection2, distanceBetweenPoints, findIntersection1, removeDuplicatePoints } from '@/util/canvas-util' +import { + calculateDistance, + calculateIntersection, + calculateIntersection2, + distanceBetweenPoints, + findClosestLineToPoint, + findClosestPoint, + findClosestPointWithDifferentXY, + findIntersection1, + getRoofHypotenuse, + removeDuplicatePoints, +} from '@/util/canvas-util' +import { help } from 'mathjs' export const defineQPloygon = () => { fabric.QPolygon = fabric.util.createClass(fabric.Group, {}) @@ -14,8 +26,287 @@ export const defineQPloygon = () => { } } -export const drawHelpLineInHexagon2 = (polygon, chon) => { - const oneSideLines = [...polygon.lines].map((line) => { +export const drawHelpLineInHexagon = (polygon, chon) => { + const centerLines = drawCenterLines(polygon) + let helpLines = [] + + const ridgeStartPoints = [] + const ridgeEndPoints = [] + + const centerInterSectionPoints = [] + + let maxLength = 0 + + polygon.lines = polygon.lines.sort((a, b) => a.length - b.length) + + polygon.wall.lines = getOneSideLines(polygon.wall) + + // 짧은 라인 순서대로 삼각 지붕을 그린다. + polygon.lines.forEach((line, index) => { + if (line.length > maxLength) { + maxLength = line.length + } + const wallLine = polygon.wall.lines.filter((wallLine) => wallLine.idx === line.idx)[0] + + const checkPoint1 = polygon.points.filter((point) => point.x === line.x1 && point.y === line.y1)[0] + const checkPoint2 = polygon.points.filter((point) => point.x === line.x2 && point.y === line.y2)[0] + + if (checkPoint1.alreadyIntersected || checkPoint2.alreadyIntersected) { + return + } + + const angle1 = Math.atan2(wallLine.y1 - line.y1, wallLine.x1 - line.x1) + const angle2 = Math.atan2(wallLine.y2 - line.y2, wallLine.x2 - line.x2) + + const helpLineLength = Math.min(line.length) + + const firstX2 = Math.floor(line.x1 + helpLineLength * Math.cos(angle1)) + const firstY2 = Math.floor(line.y1 + helpLineLength * Math.sin(angle1)) + + const secondX2 = Math.floor(line.x2 + helpLineLength * Math.cos(angle2)) + const secondY2 = Math.floor(line.y2 + helpLineLength * Math.sin(angle2)) + + const firstHelpLine = new QLine([line.x1, line.y1, firstX2, firstY2], { + fontSize: polygon.fontSize, + }) + + const secondHelpLine = new QLine([line.x2, line.y2, secondX2, secondY2], { + fontSize: polygon.fontSize, + }) + + const interSectionPoint = calculateIntersection2(firstHelpLine, secondHelpLine) + + if (interSectionPoint) { + if (polygon.inPolygon(interSectionPoint) && polygon.wall.inPolygon(interSectionPoint)) { + checkPoint1.alreadyIntersected = true + checkPoint2.alreadyIntersected = true + + const helpLine1 = new QLine([line.x1, line.y1, interSectionPoint.x, interSectionPoint.y], { + fontSize: polygon.fontSize, + stroke: 'skyblue', + }) + + const helpLine2 = new QLine([line.x2, line.y2, interSectionPoint.x, interSectionPoint.y], { + fontSize: polygon.fontSize, + stroke: 'skyblue', + }) + + helpLines.push(helpLine1) + helpLines.push(helpLine2) + + polygon.canvas.add(helpLine1) + polygon.canvas.add(helpLine2) + + ridgeStartPoints.push(interSectionPoint) + } + } + + polygon.canvas.renderAll() + }) + // points를 순회하면서 이미 그려진 점이 아닌 경우 centerLine과 만나는 점을 찾는다. + polygon.points.forEach((point, index) => { + if (point.alreadyIntersected) { + return + } + + const wallPoint = polygon.wall.points[index] + + const angle = Math.atan2(wallPoint.y - point.y, wallPoint.x - point.x) + + const newX2 = Math.floor(point.x + (maxLength / 2 + 50) * Math.cos(angle)) + const newY2 = Math.floor(point.y + (maxLength / 2 + 50) * Math.sin(angle)) + + const helpLine = new QLine([point.x, point.y, newX2, newY2], { + fontSize: polygon.fontSize, + stroke: 'green', + }) + + const relatedPoints = [] + + centerLines.forEach((centerLine) => { + const interSectionPoint = calculateIntersection2(helpLine, centerLine) + + if (interSectionPoint) { + if (polygon.inPolygon(interSectionPoint) && polygon.wall.inPolygon(interSectionPoint)) { + relatedPoints.push(interSectionPoint) + + centerInterSectionPoints.push(interSectionPoint) + } + } + }) + + helpLine.set({ relatedPoints: relatedPoints }) + helpLines.push(helpLine) + }) + + helpLines = helpLines.filter((line) => line.relatedPoints?.length > 0) + + ridgeStartPoints.forEach((point) => { + // x 혹은 y가 같으면서 가장 가까운 점을 찾는다. + let arrivalPoint + let hipLine + let distance = Infinity + let startPoint + helpLines.forEach((line) => { + line.relatedPoints.forEach((relatedPoint) => { + if (Math.abs(point.x - relatedPoint.x) <= 2 || Math.abs(point.y - relatedPoint.y) <= 2) { + if (distanceBetweenPoints(point, relatedPoint) < distance) { + startPoint = point + distance = distanceBetweenPoints(point, relatedPoint) + hipLine = line + arrivalPoint = relatedPoint + } + } + }) + }) + + if (arrivalPoint) { + hipLine.relatedPoints.forEach((relatedPoint) => { + if (relatedPoint.x !== arrivalPoint.x && relatedPoint.y !== arrivalPoint.y) { + centerInterSectionPoints.splice(centerInterSectionPoints.indexOf(relatedPoint), 1) + } + }) + + helpLines.splice(helpLines.indexOf(hipLine), 1) + + const helpLine = new QLine([hipLine.x1, hipLine.y1, arrivalPoint.x, arrivalPoint.y], { + fontSize: polygon.fontSize, + stroke: 'red', + }) + const ridge = new QLine([startPoint.x, startPoint.y, arrivalPoint.x, arrivalPoint.y], { + stroke: 'green', + fontSize: polygon.fontSize, + }) + + polygon.canvas.add(helpLine) + polygon.canvas.add(ridge) + + polygon.canvas.renderAll() + ridgeEndPoints.push(arrivalPoint) + } + }) + + /** + * 안쓰는 점 제거 + */ + centerInterSectionPoints.forEach((point) => { + ridgeEndPoints.forEach((endPoint) => { + if (!(Math.abs(point.x - endPoint.x) < 2 && Math.abs(point.y - endPoint.y) < 2)) { + centerInterSectionPoints.splice(centerInterSectionPoints.indexOf(point), 1) + } + }) + }) + + // ridgeEndPoints와 가까운 centerInterSectionPoints를 찾아서 연결한다. + const remainingPoints = centerInterSectionPoints + const ridgeEndRemainingPoints = ridgeEndPoints + + helpLines.forEach((line) => { + remainingPoints.forEach((point) => { + if (line.relatedPoints.includes(point)) { + const hip = new QLine([line.x1, line.y1, point.x, point.y], { + stroke: 'red', + fontSize: polygon.fontSize, + }) + + polygon.canvas.add(hip) + polygon.canvas.renderAll() + } + }) + }) + + while (ridgeEndRemainingPoints.length > 0) { + const point = ridgeEndRemainingPoints.shift() + const closestPoint = findClosestPoint(point, remainingPoints) + if (!closestPoint) continue + const line = new QLine([point.x, point.y, closestPoint.x, closestPoint.y], { + stroke: 'green', + fontSize: polygon.fontSize, + }) + + polygon.canvas.add(line) + polygon.canvas.renderAll() + } + + // centerInterSectionPoints에 남아있는 점들을 가까운 점끼리 연결한다. + while (remainingPoints.length > 0) { + const point = remainingPoints.shift() + const closestPoint = findClosestPoint(point, remainingPoints) + if (!closestPoint) continue + const line = new QLine([point.x, point.y, closestPoint.x, closestPoint.y], { + stroke: 'purple', + fontSize: polygon.fontSize, + }) + + polygon.canvas.add(line) + polygon.canvas.renderAll() + } +} + +export const drawCenterLines = (polygon) => { + const centerLines = [] + + const oneSideLines = getOneSideLines(polygon) + + const horizontalLines = oneSideLines.filter((line) => line.direction === 'right') + const verticalLines = oneSideLines.filter((line) => line.direction === 'bottom') + // horizontalLines 를 y1 좌표 기준으로 정렬한다. + horizontalLines.sort((a, b) => a.y1 - b.y1) + // verticalLines 를 x1 좌표 기준으로 정렬한다. + verticalLines.sort((a, b) => a.x1 - b.x1) + + let maxHorizontalLineLength = 0 + let maxVerticalLineLength = 0 + // 모든 가로선의 중심선을 긋는다. + horizontalLines.forEach((line, index) => { + const nextLine = horizontalLines[(index + 1) % horizontalLines.length] + + line.line.set({ strokeWidth: 5 }) + nextLine.line.set({ strokeWidth: 5 }) + + polygon.canvas.renderAll() + + const startCenterX = Math.min(line.x1, nextLine.x1) + const startCenterY = (line.y1 + nextLine.y1) / 2 + + const endCenterX = line.x2 > nextLine.x2 ? line.x2 : nextLine.x2 + const endCenterY = startCenterY + + const centerLine = new QLine([startCenterX, startCenterY, endCenterX, endCenterY], { + fontSize: polygon.fontSize, + stroke: 'red', + strokeWidth: 1, + direction: 'horizontal', + }) + + centerLines.push(centerLine) + }) + + // 모든 세로선의 중심선을 긋는다. + verticalLines.forEach((line, index) => { + const nextLine = verticalLines[(index + 1) % verticalLines.length] + + const startCenterX = (line.x1 + nextLine.x1) / 2 + const startCenterY = Math.min(line.y1, nextLine.y1) + + const endCenterX = startCenterX + let endCenterY = line.y2 > nextLine.y2 ? line.y2 : nextLine.y2 + + const centerLine = new QLine([startCenterX, startCenterY, endCenterX, endCenterY], { + fontSize: polygon.fontSize, + stroke: 'blue', + strokeWidth: 1, + direction: 'vertical', + }) + + centerLines.push(centerLine) + }) + + return centerLines +} + +const getOneSideLines = (polygon) => { + return [...polygon.lines].map((line) => { let newX1, newY1, newX2, newY2 if (line.direction === 'top') { newX1 = line.x2 @@ -42,288 +333,4 @@ export const drawHelpLineInHexagon2 = (polygon, chon) => { } return line }) - - const centerLines = [] - const helpLines = [] - const ridgeStartPoints = [] - - const horizontalLines = oneSideLines.filter((line) => line.direction === 'right') - const verticalLines = oneSideLines.filter((line) => line.direction === 'bottom') - // horizontalLines 를 y1 좌표 기준으로 정렬한다. - horizontalLines.sort((a, b) => a.y1 - b.y1) - // verticalLines 를 x1 좌표 기준으로 정렬한다. - verticalLines.sort((a, b) => a.x1 - b.x1) - - const maxHorizontalLineLength = horizontalLines.reduce((prev, current) => (prev.length > current.length ? prev.length : current.length)) - const maxVerticalLineLength = verticalLines.reduce((prev, current) => (prev.length > current.length ? prev.length : current.length)) - - // 모든 가로선의 중심선을 긋는다. - horizontalLines.forEach((line, index) => { - const nextLine = horizontalLines[(index + 1) % horizontalLines.length] - - line.line.set({ strokeWidth: 5 }) - nextLine.line.set({ strokeWidth: 5 }) - - polygon.canvas.renderAll() - - const startCenterX = Math.min(line.x1, nextLine.x1) - const startCenterY = (line.y1 + nextLine.y1) / 2 - - const endCenterX = line.length >= nextLine.length ? startCenterX + line.length : startCenterX + nextLine.length - const endCenterY = startCenterY - - const centerLine = new QLine([startCenterX, startCenterY, endCenterX, endCenterY], { - fontSize: polygon.fontSize, - stroke: 'red', - strokeWidth: 1, - direction: 'horizontal', - }) - - /*polygon.canvas.add(centerLine) - polygon.canvas.renderAll()*/ - - centerLines.push(centerLine) - }) - - // 모든 세로선의 중심선을 긋는다. - verticalLines.forEach((line, index) => { - const nextLine = verticalLines[(index + 1) % verticalLines.length] - - const startCenterX = (line.x1 + nextLine.x1) / 2 - const startCenterY = Math.min(line.y1, nextLine.y1) - - const endCenterX = startCenterX - let endCenterY = line.length >= nextLine.length ? startCenterY + line.length : startCenterY + nextLine.length - - const centerLine = new QLine([startCenterX, startCenterY, endCenterX, endCenterY], { - fontSize: polygon.fontSize, - stroke: 'blue', - strokeWidth: 1, - direction: 'vertical', - }) - - /*polygon.canvas.add(centerLine) - polygon.canvas.renderAll()*/ - centerLines.push(centerLine) - }) - - polygon.points.forEach((point, index) => { - const wallPoint = polygon.wall.points[index] - // 두 점의 좌표 - const x1 = point.x - const y1 = point.y - const x2 = wallPoint.x - const y2 = wallPoint.y - - let newX2, newY2 - - // x1, y1을 기준으로 x2, y2와의 거리를 유지한 새로운 직선 생성 - const angle = Math.atan2(y2 - y1, x2 - x1) - - let previousIndex = index === 0 ? polygon.lines.length - 1 : index - 1 - const maxLength = Math.max(polygon.lines[index].length, polygon.lines[previousIndex].length) - - newX2 = Math.floor(x1 + (maxLength / 2 + polygon.points.length * 10) * Math.cos(angle)) - newY2 = Math.floor(y1 + (maxLength / 2 + polygon.points.length * 10) * Math.sin(angle)) - - const line = new QLine([x1, y1, newX2, newY2], { - fontSize: polygon.fontSize, - stroke: 'green', - idx: index, - }) - line.set({ degree: fabric.util.radiansToDegrees(angle) }) - polygon.canvas.add(line) - helpLines.push(line) - polygon.canvas.renderAll() - }) - - helpLines.forEach((line, index) => { - for (let i = index + 1; i < helpLines.length; i++) { - const nextLine = helpLines[i] - - if (line.isAlreadyInterSection || nextLine.isAlreadyInterSection) { - continue - } - - let intersectionPoint = calculateIntersection(line, nextLine) - - if (!intersectionPoint) { - continue - } - - const circle = new fabric.Circle({ - radius: 3, - fill: 'red', - left: intersectionPoint.x - 3, - top: intersectionPoint.y - 3, - }) - - polygon.canvas.add(circle) - - line.set({ isAlreadyInterSection: true }) - nextLine.set({ isAlreadyInterSection: true }) - - const helpLine1 = new QLine([nextLine.x1, nextLine.y1, intersectionPoint.x, intersectionPoint.y], { - fontSize: polygon.fontSize, - stroke: 'skyblue', - }) - - const helpLine2 = new QLine([line.x1, line.y1, intersectionPoint.x, intersectionPoint.y], { - fontSize: polygon.fontSize, - stroke: 'skyblue', - }) - - ridgeStartPoints.push(intersectionPoint) - polygon.canvas.add(helpLine1) - polygon.canvas.add(helpLine2) - polygon.canvas.remove(nextLine) - polygon.canvas.remove(line) - polygon.canvas.renderAll() - } - }) - - // 안만나는 선들 - const notInterSectionLines = helpLines.filter((line) => !line.isAlreadyInterSection) - const ridgeEndPoints = [] - let interSectionPoints = [] - - notInterSectionLines.forEach((line, index) => { - let subCenterLines - if (Math.abs(line.degree) < 90) { - subCenterLines = centerLines.filter((centerLine) => centerLine.direction === 'vertical') - } else { - subCenterLines = centerLines.filter((centerLine) => centerLine.direction === 'horizontal') - } - - centerLines.forEach((centerLine) => { - const interSectionPoint = calculateIntersection2(line, centerLine) - - if (!interSectionPoint) { - return - } - - ridgeStartPoints.forEach((point) => { - line.interSectionPoints.push(interSectionPoint) - interSectionPoints.push(interSectionPoint) - - const newLine = new QLine([line.x1, line.y1, interSectionPoint.x, interSectionPoint.y], { - stroke: 'black', - fontSize: polygon.fontSize, - }) - - const circle = new fabric.Circle({ - radius: 3, - fill: 'blue', - left: interSectionPoint.x - 3, - top: interSectionPoint.y - 3, - }) - polygon.canvas.add(circle) - polygon.canvas.add(newLine) - - polygon.canvas.remove(line) - - line.set({ isAlreadyInterSection: true }) - }) - }) - }) - - interSectionPoints = removeDuplicatePoints(interSectionPoints) - - const startRidgePoint = ridgeStartPoints[0] - - const endRidgePoint = ridgeStartPoints[ridgeStartPoints.length - 1] - - let step = 0 - - while (true) { - if (step % 2 === 0) { - const nextPoint = interSectionPoints.find((point) => point.x === startRidgePoint.x || point.y === startRidgePoint.y) - - if (nextPoint) { - const ridge = new QLine([startRidgePoint.x, startRidgePoint.y, nextPoint.x, nextPoint.y], { - stroke: 'green', - fontSize: polygon.fontSize, - }) - polygon.canvas.add(ridge) - polygon.canvas.renderAll() - } - - console.log('nextPoint', nextPoint) - console.log('startRidgePoint', startRidgePoint) - } else { - } - - step++ - - break - } - - /*ridgeStartPoints.forEach((point, index) => { - for (let i = index + 1; i < ridgeStartPoints.length; i++) { - const currentPoint = ridgeStartPoints[index] - const nextPoint = ridgeStartPoints[i] - - if (currentPoint.x === nextPoint.x || currentPoint.y === nextPoint.y) { - const ridge = new QLine([currentPoint.x, currentPoint.y, nextPoint.x, nextPoint.y], { - stroke: 'black', - fontSize: polygon.fontSize, - }) - polygon.canvas.add(ridge) - polygon.canvas.renderAll() - break - } - } - })*/ - - /*ridgeStartPoints.forEach((point, index) => { - let arrivalPoint - let distance = Infinity - let startPoint - interSectionPoints.forEach((interSectionPoint) => { - if (Math.abs(point.x - interSectionPoint.x) < 1 || Math.abs(point.y - interSectionPoint.y) < 1) { - if (distanceBetweenPoints(point, interSectionPoint) < distance) { - startPoint = point - distance = distanceBetweenPoints(point, interSectionPoint) - arrivalPoint = interSectionPoint - } - } - }) - - if (arrivalPoint) { - const line = notInterSectionLines.filter((line) => line.interSectionPoints.includes(arrivalPoint))[0] - - const ridge = new QLine([startPoint.x, startPoint.y, arrivalPoint.x, arrivalPoint.y], { - stroke: 'black', - fontSize: polygon.fontSize, - }) - - const helpLine = new QLine([line.x1, line.y1, arrivalPoint.x, arrivalPoint.y], { - stroke: 'red', - fontSize: polygon.fontSize, - }) - - ridgeEndPoints.push(arrivalPoint) - polygon.canvas.add(ridge) - polygon.canvas.add(helpLine) - // polygon.canvas.remove(line) - polygon.canvas.renderAll() - debugger - } - })*/ - - /*for (let i = 0; i < ridgeEndPoints.length; i = i + 2) { - const currentRidgeEndPoint = ridgeEndPoints[i] - const nextRidgeEndPoint = ridgeEndPoints[(i + 1) % ridgeEndPoints.length] - const ridgeConnectLine = new QLine([currentRidgeEndPoint.x, currentRidgeEndPoint.y, nextRidgeEndPoint.x, nextRidgeEndPoint.y], { - fontSize: polygon.fontSize, - stroke: 'green', - }) - - polygon.canvas.add(ridgeConnectLine) - polygon.canvas.renderAll() - }*/ -} - -export const drawHelpLineInHexagon = (polygon, chon) => { - // 가장 긴라인을 기준으로 centerLine을 그린다. } From 6ecfab24729434d8bd9cb42c8b0b05620c6421f4 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Fri, 26 Jul 2024 13:42:13 +0900 Subject: [PATCH 04/27] =?UTF-8?q?import=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Roof2.jsx | 8 ++++---- src/components/fabric/QLine.js | 2 +- src/util/qpolygon-utils.js | 36 +++++++++++++++++++++------------- 3 files changed, 27 insertions(+), 19 deletions(-) diff --git a/src/components/Roof2.jsx b/src/components/Roof2.jsx index 370a1e2e..f8546298 100644 --- a/src/components/Roof2.jsx +++ b/src/components/Roof2.jsx @@ -6,10 +6,10 @@ import QRect from '@/components/fabric/QRect' import QPolygon from '@/components/fabric/QPolygon' import RangeSlider from './ui/RangeSlider' -import { useRecoilState, useRecoilValue } from 'recoil' -import { canvasAtom, canvasListState, canvasSizeState, fontSizeState, sortedPolygonArray } from '@/store/canvasAtom' +import { useRecoilState } from 'recoil' +import { canvasSizeState, fontSizeState, sortedPolygonArray } from '@/store/canvasAtom' import { QLine } from '@/components/fabric/QLine' -import { getTests, getCanvasState, insertCanvasState } from '@/lib/canvas' +import { getCanvasState, insertCanvasState } from '@/lib/canvas' import { calculateIntersection2 } from '@/util/canvas-util' export default function Roof2() { @@ -208,7 +208,7 @@ export default function Roof2() { ] if (canvas) { - const polygon = new QPolygon(type1, { + const polygon = new QPolygon(eightPoint, { fill: 'transparent', stroke: 'black', strokeWidth: 1, diff --git a/src/components/fabric/QLine.js b/src/components/fabric/QLine.js index 9c8c2133..3924558c 100644 --- a/src/components/fabric/QLine.js +++ b/src/components/fabric/QLine.js @@ -1,5 +1,5 @@ import { fabric } from 'fabric' -import { getDirection, getDirectionByPoint } from '@/util/canvas-util' +import { getDirectionByPoint } from '@/util/canvas-util' export class QLine extends fabric.Group { line diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index bd5676bd..8ac0e078 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -1,19 +1,7 @@ import { fabric } from 'fabric' import QPolygon from '@/components/fabric/QPolygon' import { QLine } from '@/components/fabric/QLine' -import { - calculateDistance, - calculateIntersection, - calculateIntersection2, - distanceBetweenPoints, - findClosestLineToPoint, - findClosestPoint, - findClosestPointWithDifferentXY, - findIntersection1, - getRoofHypotenuse, - removeDuplicatePoints, -} from '@/util/canvas-util' -import { help } from 'mathjs' +import { calculateIntersection2, distanceBetweenPoints, findClosestPoint } from '@/util/canvas-util' export const defineQPloygon = () => { fabric.QPolygon = fabric.util.createClass(fabric.Group, {}) @@ -58,7 +46,9 @@ export const drawHelpLineInHexagon = (polygon, chon) => { const angle1 = Math.atan2(wallLine.y1 - line.y1, wallLine.x1 - line.x1) const angle2 = Math.atan2(wallLine.y2 - line.y2, wallLine.x2 - line.x2) - const helpLineLength = Math.min(line.length) + // line을 이등변 삼각형의 밑변으로 보고 높이를 구한다. + + const helpLineLength = Math.sqrt(Math.pow(line.length, 2) - Math.pow(line.length / 2, 2)) - 50 const firstX2 = Math.floor(line.x1 + helpLineLength * Math.cos(angle1)) const firstY2 = Math.floor(line.y1 + helpLineLength * Math.sin(angle1)) @@ -142,6 +132,7 @@ export const drawHelpLineInHexagon = (polygon, chon) => { helpLines = helpLines.filter((line) => line.relatedPoints?.length > 0) ridgeStartPoints.forEach((point) => { + point.alreadyIntersected = false // x 혹은 y가 같으면서 가장 가까운 점을 찾는다. let arrivalPoint let hipLine @@ -183,6 +174,8 @@ export const drawHelpLineInHexagon = (polygon, chon) => { polygon.canvas.renderAll() ridgeEndPoints.push(arrivalPoint) + + point.alreadyIntersected = true } }) @@ -241,6 +234,21 @@ export const drawHelpLineInHexagon = (polygon, chon) => { polygon.canvas.add(line) polygon.canvas.renderAll() } + + const notIntersectedRidgeStartPoints = ridgeStartPoints.filter((point) => !point.alreadyIntersected) + // 만나지 않은 마루 시작점 + while (notIntersectedRidgeStartPoints.length > 0) { + const point = notIntersectedRidgeStartPoints.shift() + const closestPoint = findClosestPoint(point, notIntersectedRidgeStartPoints) + if (!closestPoint) continue + const line = new QLine([point.x, point.y, closestPoint.x, closestPoint.y], { + stroke: 'purple', + fontSize: polygon.fontSize, + }) + + polygon.canvas.add(line) + polygon.canvas.renderAll() + } } export const drawCenterLines = (polygon) => { From 58820d2bf20aa83a62c0e36f039036d70e09c6a2 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Fri, 26 Jul 2024 13:53:17 +0900 Subject: [PATCH 05/27] =?UTF-8?q?=ED=8C=94=EA=B0=81=ED=98=95=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EC=8B=9C=EC=9E=91?= =?UTF-8?q?=EC=A0=90=20=ED=81=B0=20=EB=B9=A8=EA=B0=84=20=EC=9B=90=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Roof2.jsx | 24 +++++++++++++++++++++++- src/hooks/useCanvas.js | 19 ++++--------------- src/hooks/useMode.js | 8 ++++---- src/util/qpolygon-utils.js | 2 +- 4 files changed, 32 insertions(+), 21 deletions(-) diff --git a/src/components/Roof2.jsx b/src/components/Roof2.jsx index f8546298..27aef946 100644 --- a/src/components/Roof2.jsx +++ b/src/components/Roof2.jsx @@ -207,8 +207,30 @@ export default function Roof2() { { x: 897, y: 215 }, ] + const eightPoint3 = [ + { x: 190, y: 147 }, + { x: 190, y: 747 }, + { x: 490, y: 747 }, + { x: 490, y: 497 }, + { x: 640, y: 497 }, + { x: 640, y: 747 }, + { x: 1090, y: 747 }, + { x: 1090, y: 147 }, + ] + + const eightPoint4 = [ + { x: 228, y: 92 }, + { x: 228, y: 592 }, + { x: 478, y: 592 }, + { x: 478, y: 342 }, + { x: 728, y: 342 }, + { x: 728, y: 592 }, + { x: 1078, y: 592 }, + { x: 1078, y: 92 }, + ] + if (canvas) { - const polygon = new QPolygon(eightPoint, { + const polygon = new QPolygon(eightPoint4, { fill: 'transparent', stroke: 'black', strokeWidth: 1, diff --git a/src/hooks/useCanvas.js b/src/hooks/useCanvas.js index 44ed17ce..fb4bd571 100644 --- a/src/hooks/useCanvas.js +++ b/src/hooks/useCanvas.js @@ -7,6 +7,8 @@ import { canvasSizeState, fontSizeState } from '@/store/canvasAtom' import QPolygon from '@/components/fabric/QPolygon' import { QLine } from '@/components/fabric/QLine' import QRect from '@/components/fabric/QRect' +import { defineQPloygon } from '@/util/qpolygon-utils' +import { defineQLine } from '@/util/qline-utils' export function useCanvas(id) { const [canvas, setCanvas] = useState() @@ -118,21 +120,8 @@ export function useCanvas(id) { fabric.QLine = fabric.util.createClass(fabric.Group, {}) fabric.QPolygon = fabric.util.createClass(fabric.Group, {}) - // fromObject 메서드를 QLine 클래스에 직접 추가 - fabric.QLine.fromObject = function (object, callback) { - const { initOption, initPoints, initLengthTxt } = object - fabric.util.enlivenObjects(object.objects, function (enlivenedObjects) { - return callback(new QLine(initPoints, initOption, initLengthTxt)) - }) - } - - // fromObject 메서드를 QLine 클래스에 직접 추가 - fabric.QPolygon.fromObject = function (object, callback) { - const { initOption, initPoints, initLengthTxt } = object - fabric.util.enlivenObjects(object.objects, function (enlivenedObjects) { - return callback(new QPolygon(initPoints, initOption, initLengthTxt)) - }) - } + defineQPloygon() + defineQLine() } /** diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index 18a7a574..3b36974f 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -329,11 +329,11 @@ export function useMode() { canvas?.on('mouse:down', function (options) { const pointer = canvas?.getPointer(options.e) const circle = new fabric.Circle({ - radius: 1, + radius: 5, fill: 'transparent', // 원 안을 비웁니다. - stroke: 'black', // 원 테두리 색상을 검은색으로 설정합니다. - left: pointer.x, - top: pointer.y, + stroke: 'red', // 원 테두리 색상을 검은색으로 설정합니다. + left: pointer.x - 5, + top: pointer.y - 5, originX: 'center', originY: 'center', selectable: false, diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index 8ac0e078..982fa111 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -48,7 +48,7 @@ export const drawHelpLineInHexagon = (polygon, chon) => { // line을 이등변 삼각형의 밑변으로 보고 높이를 구한다. - const helpLineLength = Math.sqrt(Math.pow(line.length, 2) - Math.pow(line.length / 2, 2)) - 50 + const helpLineLength = Math.sqrt(Math.pow(line.length, 2) - Math.pow(line.length / 2, 2)) - 10 const firstX2 = Math.floor(line.x1 + helpLineLength * Math.cos(angle1)) const firstY2 = Math.floor(line.y1 + helpLineLength * Math.sin(angle1)) From 11fec70ac2d64273c83242d654abe5dd6c05e386 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Fri, 26 Jul 2024 15:40:55 +0900 Subject: [PATCH 06/27] =?UTF-8?q?guideLine=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useMode.js | 114 ++++++++++++++++++++++++++++--------------- 1 file changed, 75 insertions(+), 39 deletions(-) diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index 3b36974f..249984c7 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -25,6 +25,7 @@ export function useMode() { const points = useRef([]) const historyPoints = useRef([]) const historyLines = useRef([]) + const startPoint = useRef() const [canvas, setCanvas] = useState(null) const [zoom, setZoom] = useState(100) const [fontSize] = useRecoilState(fontSizeState) @@ -35,6 +36,8 @@ export function useMode() { const [endPoint, setEndPoint] = useState(null) + const pointCount = useRef(0) + const [roofPolygonPattern, setRoofPolygonPattern] = useRecoilState(roofPolygonPatternArrayState) const [canvasSize] = useRecoilState(canvasSizeState) @@ -102,6 +105,62 @@ export function useMode() { canvas?.renderAll() } + useEffect(() => { + if (pointCount.current <= 2) { + removeGuideLines() + return + } + drawGuideLines() + }, [pointCount.current]) + + const removeGuideLines = () => { + const guideLines = canvas?._objects.filter((obj) => obj.name === 'guideLine') + guideLines?.forEach((item) => canvas?.remove(item)) + } + + const drawGuideLines = () => { + // 이름이 guideLine인 가이드라인을 제거합니다. + removeGuideLines() + + const arrivalX = startPoint.current?.left + const arrivalY = startPoint.current?.top + + const lastX = endPoint?.left + const lastY = endPoint?.top + + if (lastX === arrivalX || lastY === arrivalY) { + // 둘중 하나라도 같으면 guideLine은 한개만 생성 + const guideLine = new QLine([lastX, lastY, arrivalX, arrivalY], { + fontSize: fontSize, + stroke: 'black', + strokeWidth: 1, + strokeDashArray: [1, 1, 1], + }) + guideLine.name = 'guideLine' + canvas?.add(guideLine) + } else { + const guideLine1 = new QLine([lastX, lastY, lastX, arrivalY], { + fontSize: fontSize, + stroke: 'black', + strokeWidth: 1, + strokeDashArray: [1, 1, 1], + }) + + const guideLine2 = new QLine([guideLine1.x2, guideLine1.y2, arrivalX, arrivalY], { + fontSize: fontSize, + stroke: 'black', + strokeWidth: 1, + strokeDashArray: [1, 1, 1], + }) + + guideLine1.name = 'guideLine' + guideLine2.name = 'guideLine' + + canvas?.add(guideLine1) + canvas?.add(guideLine2) + } + } + /** * 마우스 포인터의 가이드라인을 제거합니다. */ @@ -125,12 +184,10 @@ export function useMode() { const pointer = canvas?.getPointer(e.e) // 마우스 포인터 위치랑 endPoint를 연결하는 line 생성 - const line = new QLine([endPoint.left, endPoint.top, pointer.x, pointer.y], { + const line = new fabric.Line([endPoint.left, endPoint.top, pointer.x, pointer.y], { stroke: 'black', - strokeWidth: 2, + strokeWidth: 1, selectable: false, - viewLengthText: true, - fontSize: fontSize, }) line.set({ name: 'connectLine' }) @@ -235,8 +292,6 @@ export function useMode() { selectable: false, }) - setEndPoint(endPointCircle) - canvas?.add(endPointCircle) historyPoints.current.push(endPointCircle) @@ -244,6 +299,10 @@ export function useMode() { points.current.forEach((point) => { canvas?.remove(point) }) + + setEndPoint(endPointCircle) + pointCount.current = pointCount.current + 1 + points.current = [endPointCircle] canvas.renderAll() @@ -317,6 +376,8 @@ export function useMode() { const changeMode = (canvas, mode) => { setEndPoint(null) + pointCount.current = 0 + setMode(mode) // mode변경 시 이전 이벤트 제거 setCanvas(canvas) @@ -338,6 +399,10 @@ export function useMode() { originY: 'center', selectable: false, }) + if (!startPoint.current) { + startPoint.current = circle + pointCount.current = pointCount.current + 1 + } setEndPoint(circle) @@ -347,6 +412,7 @@ export function useMode() { if (points.current.length === 2) { const length = Number(prompt('길이를 입력하세요:')) + // length 값이 숫자가 아닌 경우 if (isNaN(length) || length === 0) { //마지막 추가 된 points 제거합니다. @@ -382,40 +448,10 @@ export function useMode() { } } - const line = new QLine( - [points.current[0].left, points.current[0].top, points.current[0].left + scaledVector.x, points.current[0].top + scaledVector.y], - { - stroke: 'black', - strokeWidth: 2, - selectable: false, - viewLengthText: true, - direction: getDirection(points.current[0], points.current[1]), - fontSize: fontSize, - }, - ) + const verticalLength = scaledVector.y + const horizontalLength = scaledVector.x - pushHistoryLine(line) - - // 라인의 끝에 점을 추가합니다. - const endPointCircle = new fabric.Circle({ - radius: 1, - fill: 'transparent', // 원 안을 비웁니다. - stroke: 'black', // 원 테두리 색상을 검은색으로 설정합니다. - left: points.current[0].left + scaledVector.x, - top: points.current[0].top + scaledVector.y, - originX: 'center', - originY: 'center', - selectable: false, - }) - setEndPoint(endPointCircle) - canvas?.add(endPointCircle) - - historyPoints.current.push(endPointCircle) - - points.current.forEach((point) => { - canvas?.remove(point) - }) - points.current = [endPointCircle] + drawCircleAndLine(verticalLength, horizontalLength) } } From aaeccdb6dd27bef817599b77991e4ee4c39eb83d Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Fri, 26 Jul 2024 17:11:01 +0900 Subject: [PATCH 07/27] =?UTF-8?q?clear=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useMode.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index 249984c7..8261fa31 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -703,6 +703,8 @@ export function useMode() { */ const handleClear = () => { canvas?.clear() + startPoint.current = null + setEndPoint(null) points.current = [] historyPoints.current = [] historyLines.current = [] From 94f1b9b28a008aa2c37680c2033de62babc04aa9 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Fri, 26 Jul 2024 18:29:21 +0900 Subject: [PATCH 08/27] =?UTF-8?q?=EC=A7=80=EB=B6=95=20=EC=84=A0=EA=B7=B8?= =?UTF-8?q?=EB=A6=AC=EA=B8=B0=20=EC=9E=91=EC=97=85=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Roof2.jsx | 2 +- src/util/qpolygon-utils.js | 80 ++++++++++++++++++++++++++++---------- 2 files changed, 61 insertions(+), 21 deletions(-) diff --git a/src/components/Roof2.jsx b/src/components/Roof2.jsx index 27aef946..ecd83b02 100644 --- a/src/components/Roof2.jsx +++ b/src/components/Roof2.jsx @@ -230,7 +230,7 @@ export default function Roof2() { ] if (canvas) { - const polygon = new QPolygon(eightPoint4, { + const polygon = new QPolygon(type1, { fill: 'transparent', stroke: 'black', strokeWidth: 1, diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index 982fa111..b50cd4b9 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -48,7 +48,7 @@ export const drawHelpLineInHexagon = (polygon, chon) => { // line을 이등변 삼각형의 밑변으로 보고 높이를 구한다. - const helpLineLength = Math.sqrt(Math.pow(line.length, 2) - Math.pow(line.length / 2, 2)) - 10 + const helpLineLength = Math.sqrt(2 * Math.pow(line.length / 2, 2)) const firstX2 = Math.floor(line.x1 + helpLineLength * Math.cos(angle1)) const firstY2 = Math.floor(line.y1 + helpLineLength * Math.sin(angle1)) @@ -182,18 +182,61 @@ export const drawHelpLineInHexagon = (polygon, chon) => { /** * 안쓰는 점 제거 */ - centerInterSectionPoints.forEach((point) => { - ridgeEndPoints.forEach((endPoint) => { - if (!(Math.abs(point.x - endPoint.x) < 2 && Math.abs(point.y - endPoint.y) < 2)) { - centerInterSectionPoints.splice(centerInterSectionPoints.indexOf(point), 1) + + const ridgeEndRemainingPoints = [...ridgeEndPoints] + + const uniqueInterSectionPoints = Array.from(new Set(centerInterSectionPoints.map((point) => `${point.x},${point.y}`))).map((key) => { + const [x, y] = key.split(',').map(Number) + return { x, y } + }) + + while (ridgeEndRemainingPoints.length > 0) { + const point = ridgeEndRemainingPoints.shift() + let isExist = false + uniqueInterSectionPoints.forEach((uniquePoint) => { + const degree = calculateAngle(point, uniquePoint) + + if (Math.abs(45 - Math.abs(degree)) <= 5 || Math.abs(135 - Math.abs(degree)) <= 5) { + const line = new QLine([point.x, point.y, uniquePoint.x, uniquePoint.y], { + stroke: 'purple', + fontSize: polygon.fontSize, + }) + + ridgeEndPoints.push(uniquePoint) + + ridgeEndPoints.splice(ridgeEndPoints.indexOf(point), 1) + + isExist = true + + polygon.canvas.add(line) + polygon.canvas.renderAll() + } + + if (isExist) { + return } }) - }) + } + + const ridgeEndRemainingPoints2 = [...ridgeEndPoints] + + while (ridgeEndRemainingPoints2.length > 0) { + // 남아있는 점끼리 연결한다. + const point = ridgeEndRemainingPoints2.shift() + const closestPoint = findClosestPoint(point, ridgeEndRemainingPoints2) + if (!closestPoint) continue + const line = new QLine([point.x, point.y, closestPoint.x, closestPoint.y], { + stroke: 'purple', + fontSize: polygon.fontSize, + }) + + polygon.canvas.add(line) + polygon.canvas.renderAll() + } // ridgeEndPoints와 가까운 centerInterSectionPoints를 찾아서 연결한다. const remainingPoints = centerInterSectionPoints - const ridgeEndRemainingPoints = ridgeEndPoints - + /* helpLines.forEach((line) => { remainingPoints.forEach((point) => { if (line.relatedPoints.includes(point)) { @@ -208,18 +251,7 @@ export const drawHelpLineInHexagon = (polygon, chon) => { }) }) - while (ridgeEndRemainingPoints.length > 0) { - const point = ridgeEndRemainingPoints.shift() - const closestPoint = findClosestPoint(point, remainingPoints) - if (!closestPoint) continue - const line = new QLine([point.x, point.y, closestPoint.x, closestPoint.y], { - stroke: 'green', - fontSize: polygon.fontSize, - }) - polygon.canvas.add(line) - polygon.canvas.renderAll() - } // centerInterSectionPoints에 남아있는 점들을 가까운 점끼리 연결한다. while (remainingPoints.length > 0) { @@ -248,9 +280,11 @@ export const drawHelpLineInHexagon = (polygon, chon) => { polygon.canvas.add(line) polygon.canvas.renderAll() - } + }*/ } +export const drawHelpLineInHexagon2 = (polygon, chon) => {} + export const drawCenterLines = (polygon) => { const centerLines = [] @@ -342,3 +376,9 @@ const getOneSideLines = (polygon) => { return line }) } +const calculateAngle = (point1, point2) => { + const deltaX = point2.x - point1.x + const deltaY = point2.y - point1.y + const angleInRadians = Math.atan2(deltaY, deltaX) + return angleInRadians * (180 / Math.PI) +} From d88fb8f38934c2c05ddb463fec7c47e3c9ceacba Mon Sep 17 00:00:00 2001 From: yjnoh Date: Fri, 26 Jul 2024 18:29:33 +0900 Subject: [PATCH 09/27] =?UTF-8?q?8=EA=B0=81=20=EA=B0=80=EB=A1=9C=20?= =?UTF-8?q?=EC=9E=91=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useMode.js | 443 +++++++++++++++++++++++++++++++++---------- 1 file changed, 348 insertions(+), 95 deletions(-) diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index 249984c7..e691ef71 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -2015,16 +2015,15 @@ export function useMode() { } } - roofPatternPolygonArray.push(bigRoofPolygon) - roofPatternPolygonArray.push(middleRoofPolygon) - roofPatternPolygonArray.push(smallRoofPolygon) - - setRoofPolygonPattern({ roofPatternPolygonArray, lines }) + roofPatternPolygonArray.push(bigRoofPolygon) //지붕폴리곤 + roofPatternPolygonArray.push(middleRoofPolygon) //중간라인 폴리곤 + roofPatternPolygonArray.push(smallRoofPolygon) //작은지붕폴리곤 + setRoofPolygonPattern({ roofPatternPolygonArray, lines }) //모든 행을 저장 canvas.renderAll() } - const handleOuterLineTemplateA8Points = (polygon, offsetInputX = 50, offsetInputY = 20) => { + const handleOuterLineTemplateA8Points = (polygon, offsetInputX = 20, offsetInputY = 50) => { let offsetPoints = [] const originalMax = 71 @@ -2038,15 +2037,15 @@ export function useMode() { const dashedCenterLineOpt = { stroke: 'black', - strokeWidth: 4, + strokeWidth: 1, property: 'centerLine', - strokeDashArray: [5, 5], + strokeDashArray: [8, 4], fontSize: 14, } const centerLineOpt = { stroke: 'blue', - strokeWidth: 5, + strokeWidth: 2, property: 'bigHoriCenter', fontSize: 14, } @@ -2056,10 +2055,8 @@ export function useMode() { const start = polygon.points[i] const end = polygon.points[(i + 1) % polygon.points.length] // 다음 점, 마지막 점의 경우 첫 점으로 - const color = i % 2 === 0 ? '#A0D468' : 'skyblue' - const line = new QLine([start.x, start.y, end.x, end.y], { - stroke: color, + stroke: '#A0D468', strokeWidth: 2, property: 'normal', fontSize: 14, @@ -2074,12 +2071,6 @@ export function useMode() { const sortedIndex = getStartIndex(polygon.lines) let tmpArraySorted = rearrangeArray(polygon.lines, sortedIndex) - - if (tmpArraySorted[0].direction === 'right') { - //시계방향 - tmpArraySorted = tmpArraySorted.reverse() //그럼 배열을 거꾸로 만들어서 무조건 반시계방향으로 배열 보정 - } - setSortedArray(tmpArraySorted) //recoil에 넣음 const points = tmpArraySorted.map((line) => ({ @@ -2090,9 +2081,10 @@ export function useMode() { //좌표 재정렬 function reSortQlineArray(array) { let tmpArray = [] + let minX, minY, maxX, maxY + let tmp array.forEach((arr, index) => { - let minX, minY, maxX, maxY - let tmp = arr + tmp = arr if (arr.x2 < arr.x1 || arr.y2 < arr.y1) { minX = arr.x2 minY = arr.y2 @@ -2149,56 +2141,46 @@ export function useMode() { index: concavePointIndices[0], line: lines[concavePointIndices[0]], } - for (var i = 0; i < points.length; i++) { - var prev = points[(i - 1 + points.length) % points.length] - var current = points[i] - var next = points[(i + 1) % points.length] + + for (let i = 0; i < points.length; i++) { + let prev = points[(i - 1 + points.length) % points.length] + let current = points[i] + let next = points[(i + 1) % points.length] // 두 벡터 계산 (prev -> current, current -> next) - var vector1 = { x: current.x - prev.x, y: current.y - prev.y } - var vector2 = { x: next.x - current.x, y: next.y - current.y } + let vector1 = { x: current.x - prev.x, y: current.y - prev.y } + let vector2 = { x: next.x - current.x, y: next.y - current.y } // 벡터의 길이 계산 - var length1 = Math.sqrt(vector1.x * vector1.x + vector1.y * vector1.y) - var length2 = Math.sqrt(vector2.x * vector2.x + vector2.y * vector2.y) + let length1 = Math.sqrt(vector1.x * vector1.x + vector1.y * vector1.y) + let length2 = Math.sqrt(vector2.x * vector2.x + vector2.y * vector2.y) // 벡터를 단위 벡터로 정규화 - var unitVector1 = { x: vector1.x / length1, y: vector1.y / length1 } - var unitVector2 = { x: vector2.x / length2, y: vector2.y / length2 } + let unitVector1 = { x: vector1.x / length1, y: vector1.y / length1 } + let unitVector2 = { x: vector2.x / length2, y: vector2.y / length2 } // 법선 벡터 계산 (왼쪽 방향) - var normal1 = { x: -unitVector1.y, y: unitVector1.x } - var normal2 = { x: -unitVector2.y, y: unitVector2.x } + let normal1 = { x: -unitVector1.y, y: unitVector1.x } + let normal2 = { x: -unitVector2.y, y: unitVector2.x } // 법선 벡터 평균 계산 - var averageNormal = { + let averageNormal = { x: (normal1.x + normal2.x) / 2, y: (normal1.y + normal2.y) / 2, } // 평균 법선 벡터를 단위 벡터로 정규화 - var lengthNormal = Math.sqrt(averageNormal.x * averageNormal.x + averageNormal.y * averageNormal.y) - var unitNormal = { + let lengthNormal = Math.sqrt(averageNormal.x * averageNormal.x + averageNormal.y * averageNormal.y) + let unitNormal = { x: averageNormal.x / lengthNormal, y: averageNormal.y / lengthNormal, } - if (concavePointIndices[0] === i || concavePointIndices[1] === i) { - //인덱스가 배열이랑 같으면 - if ((concavePointIndices[0] === 4 && concavePointIndices[1] === 5) || (concavePointIndices[0] === 2 && concavePointIndices[1] === 3)) { - offsetX = 1 - offsetY = (offsetInputY / transformedMax) * originalMax * 2 - } else { - offsetX = (offsetInputX / transformedMax) * originalMax * 2 - offsetY = 1 - } - } else { - offsetX = (offsetInputX / transformedMax) * originalMax * 2 - offsetY = (offsetInputY / transformedMax) * originalMax * 2 - } + offsetX = (offsetInputX / transformedMax) * originalMax * 2 + offsetY = (offsetInputY / transformedMax) * originalMax * 2 // 오프셋 적용 - var offsetPoint = { + let offsetPoint = { x1: current.x + unitNormal.x * offsetX, y1: current.y + unitNormal.y * offsetY, } @@ -2207,6 +2189,7 @@ export function useMode() { } const outlinePolygon = makePolygon(offsetPoints) + outlinePolygon.setViewLengthText(false) // 아웃라인 폴리곤의 각 변을 선으로 생성 for (let i = 0; i < outlinePolygon.points.length; i++) { @@ -2218,6 +2201,7 @@ export function useMode() { strokeWidth: 2, property: 'normal', fontSize: 14, + idx: i, }) // 선을 배열에 추가 @@ -2227,13 +2211,8 @@ export function useMode() { canvas?.remove(outlinePolygon) //임시 폴리곤을 삭제 - //라인들을 좌측에서 -> 우측으로 그리는거처럼 데이터 보정 - outLines = reSortQlineArray(outLines) - let parallelLinesIdx = concavePointIndices[0] + 4 //들어간선에 무조건 평행하는 선 찾기 - let parallelLines = outLines[parallelLinesIdx] if (parallelLinesIdx >= outLines.length) { - parallelLines = outLines[parallelLinesIdx - outLines.length] parallelLinesIdx = parallelLinesIdx - outLines.length } @@ -2242,10 +2221,207 @@ export function useMode() { let horiCenterLine = [] let shorVertCenterLine = [] + let edgeIndexArray = [] + if (concavePointIndices[0] % 2 === 0) { - // 오목한 부분이 세로선일때 ㄷ, 역ㄷ + //케라바 색을 바꾼다 + lines.forEach((line, index) => { + if (index % 2 === 0) { + line.line.set('stroke', 'skyblue') + if (concavePointIndices[0] !== index) { + edgeIndexArray.push(index) + } + } + }) + + console.log('edgeIndexArray', edgeIndexArray) + + outLines = reSortQlineArray(outLines) + edgeIndexArray.forEach((idx, index) => { + //가로라인이 케라바 라인임 + if (concavePointIndices[0] !== idx) { + //오목이가 아니면 반으로 갈라서 계산 + + //카라바 선의 2분할 치수를 그림 + let halfLength = outLines[idx].length / 2 + let centerLine1 = new QLine([outLines[idx].x1, outLines[idx].y1, outLines[idx].x1, outLines[idx].y1 + halfLength], centerLineOpt) + canvas.add(centerLine1) + + let centerLine2 = new QLine([outLines[idx].x1, centerLine1.y2, outLines[idx].x2, centerLine1.y2 + halfLength], centerLineOpt) + canvas.add(centerLine2) + canvas.remove(outLines[idx]) //기존 라인 삭제 + + halfHoriCenterLinePoint.push({ index: idx, x1: centerLine1.x1, y1: centerLine1.y1, x2: centerLine1.x2, y2: centerLine1.y2 }) //각 카라바 라인의 1번이 마지막점을 잡아서 센터선으로 설정 + } + }) + + console.log('halfHoriCenterLinePoint', halfHoriCenterLinePoint) + + // //각 센터 라인을 그림 + halfHoriCenterLinePoint.forEach((centerPoint) => { + let tmpX2 = parallelLinesIdx !== centerPoint.index ? concaveLine.line.x2 : outLines[concavePointIndices[0]].x1 //평행선에서 내려오는 선은 아웃라인에 닿아야한다 + + let line = new QLine([centerPoint.x2, centerPoint.y2, tmpX2, centerPoint.y2], centerLineOpt) + canvas.add(line) + + line['arrayIndex'] = centerPoint.index //커스텀으로 기존 index를 넣어줌 + vertCenterLine.push(line) + }) + + vertCenterLine = reSortQlineArray(vertCenterLine) + lines = reSortQlineArray(lines) + + console.log('parallelLinesIdx', parallelLinesIdx) + + //해당라인에서 만나는점을 계산 + vertCenterLine.forEach((vertLine) => { + if (parallelLinesIdx !== vertLine.arrayIndex) { + //평행선을 제외한 애들만 네모를 연결 + let nearLine + let nearOutline + if (vertLine.arrayIndex > concaveLine.index) { + //센터에 인덱스가 오목점 보다 크면 다음 작으면 앞에꺼 + nearLine = lines[concaveLine.index + 1] + nearOutline = outLines[concaveLine.index + 1] + } else { + nearLine = lines[concaveLine.index - 1] + nearOutline = outLines[concaveLine.index - 1] + } + + let nearLineY = nearLine.y1 + if (parallelLinesIdx < concaveLine.index) { + //오목점 위치가 평행선보다 크면 위쪽으로 오목 + nearLineY = nearLine.y2 + } + + //기존에 있는 라인에서 연장해서 새로 그림 + let centerExtendHoriLine = new QLine([vertLine.x1, nearOutline.line.y1, vertLine.x2, nearOutline.line.y2], centerLineOpt) + canvas.add(centerExtendHoriLine) + canvas.remove(nearOutline) + outLines.splice(nearOutline.idx, 1, centerExtendHoriLine) //아웃라인에 데이터를 다시 넣는다 + + let centerExtendLine = new QLine([vertLine.line.x2, vertLine.line.y2, centerExtendHoriLine.x2, centerExtendHoriLine.y2], centerLineOpt) + canvas.add(centerExtendLine) //새로그리고 + + let betweenCenterLine = (vertLine.line.x1 + vertLine.line.x2) / 2 + let centerDashLine = new QLine([vertLine.line.y1, betweenCenterLine, nearOutline.y1, betweenCenterLine], dashedCenterLineOpt) + + canvas.add(centerDashLine) + horiCenterLine.push(centerDashLine) + shorVertCenterLine.push(vertLine) //마지막에 가운데 선을 긋기 위해 담음 + } else { + let longDashLine = halfHoriCenterLinePoint.find((obj) => obj.index === parallelLinesIdx) + + let dashCenterExtendLineLength = longDashLine.x2 - longDashLine.x1 + let betweenCenterLine = (vertLine.line.y1 + vertLine.line.y2) / 2 + let totalLength = ((longDashLine.x2 - longDashLine.x1) * 2) / dashCenterExtendLineLength + + //반 쪼개서 그린다 + for (let i = 0; i < totalLength; i++) { + let startX = i === 0 ? longDashLine.x1 : longDashLine.x1 + dashCenterExtendLineLength + let centerDashLine = new QLine([startX, betweenCenterLine, startX + dashCenterExtendLineLength, betweenCenterLine], dashedCenterLineOpt) + canvas.add(centerDashLine) + horiCenterLine.push(centerDashLine) + } + } + }) + + // //마지막에 오목한 외곽선을 연장한다 + // const tmpLastOutLine = outLines[concavePointIndices[0]] + // const lastOutLine = new QLine([shorVertCenterLine[0].x1, tmpLastOutLine.y1, shorVertCenterLine[1].x1, tmpLastOutLine.y2], centerLineOpt) + // canvas.add(lastOutLine) + // canvas.remove(tmpLastOutLine) + + // let tmpVertCenterLine = outLines.filter((x, index) => index % 2 === 0) //세로만 찾음 + // tmpVertCenterLine = tmpVertCenterLine.concat(vertCenterLine) + // tmpVertCenterLine.sort((a, b) => a.x1 - b.x1) + // tmpVertCenterLine.push(lastOutLine) + + // let roofPatternPolygonArray = [] + // let tmpArray = [] + // let tmpBigArray = [] + + // const lastCenterLine = tmpVertCenterLine[tmpVertCenterLine.length - 1] //마지막 센터라인을 정의 + + // for (let i = 0; i < tmpVertCenterLine.length - 1; i++) { + // //-1인건 마지막은 오목한 선이라 돌 필요 없음 + // //라인 하나에 두점씩 나온다 + // let firstPointObj = {} + // let secondPointObj = {} + + // let x1 = tmpVertCenterLine[i].x1 + // let y1 = tmpVertCenterLine[i].y1 + // let x2 = tmpVertCenterLine[i].x2 + // let y2 = tmpVertCenterLine[i].y2 + + // if (i === 2 || i === 4) { + // tmpArray = [] + // const prevLine = tmpVertCenterLine[i - 1] //뒤에서 앞라인을 찾는다 + // const nextLine = tmpVertCenterLine[i + 1] + + // //내 앞뒤 라인 + // const tmpX1 = i === 2 ? prevLine.x1 : nextLine.x1 + // const tmpY1 = i === 2 ? prevLine.y1 : nextLine.y1 + // const tmpX2 = i === 2 ? prevLine.x2 : nextLine.x2 + // const tmpY2 = i === 2 ? prevLine.y2 : nextLine.y2 + + // firstPointObj = { x: tmpX1, y: tmpY1 } + // secondPointObj = { x: tmpX2, y: tmpY2 } + // tmpArray.push(firstPointObj) + // tmpArray.push(secondPointObj) + + // //현재 내 선 + // firstPointObj = { x: x1, y: y1 } + // secondPointObj = { x: x2, y: y2 } + // tmpArray.push(firstPointObj) + // tmpArray.push(secondPointObj) + // roofPatternPolygonArray.push(tmpArray) + // } else { + // if (i === 1 || i === 5) { + // // 큰 폴리곤은 가운데 선으로 되야됨 + // if (outLines.length / 2 < concavePointIndices[0]) { + // //오목이가 배열 전체보다 크면 위쪽 방향 + // x2 = i === 1 ? lastCenterLine.x2 : lastCenterLine.x1 + // y2 = i === 1 ? lastCenterLine.y2 : lastCenterLine.y1 + // } else { + // x1 = i === 1 ? lastCenterLine.x1 : lastCenterLine.x2 + // y1 = i === 1 ? lastCenterLine.y1 : lastCenterLine.y2 + // } + // } + + // if (i === 5) { + // //5번일때는 앞에 3번에 선이 필요하다 + // let prevX1 = tmpVertCenterLine[i - 2].x1 + // let prevY1 = tmpVertCenterLine[i - 2].y1 + // let prevX2 = tmpVertCenterLine[i - 2].x2 + // let prevY2 = tmpVertCenterLine[i - 2].y2 + // firstPointObj = { x: prevX1, y: prevY1 } + // secondPointObj = { x: prevX2, y: prevY2 } + // tmpBigArray.push(firstPointObj) + // tmpBigArray.push(secondPointObj) + // } + + // firstPointObj = { x: x1, y: y1 } + // secondPointObj = { x: x2, y: y2 } + // tmpBigArray.push(firstPointObj) + // tmpBigArray.push(secondPointObj) + + // if (i === 3 || i === 6) { + // roofPatternPolygonArray.push(tmpBigArray) + // tmpBigArray = [] + // } + // } + // } } else { - // 오목한 부분이 세로선일때 ㄷ, 역ㄷ + // 오목한 부분이 세로선일때 아래ㄷ, 위ㄷ + //라인들을 좌측에서 -> 우측으로 그리는거처럼 데이터 보정 + + lines.forEach((line, index) => { + if (!(index % 2 === 0)) { + line.line.set('stroke', 'skyblue') + } + }) + outLines = reSortQlineArray(outLines) outLines.forEach((outline, index) => { if (!(index % 2 === 0)) { //세로라인이 케라바 라인임 @@ -2255,20 +2431,10 @@ export function useMode() { //카라바 선의 2분할 치수를 그림 let halfLength = outline.length / 2 - let centerLine1 = new QLine([outline.x1, outline.y1, outline.x1 + halfLength, outline.y1], { - stroke: 'red', - strokeWidth: 2, - property: 'normal', - fontSize: 14, - }) + let centerLine1 = new QLine([outline.x1, outline.y1, outline.x1 + halfLength, outline.y1], centerLineOpt) canvas.add(centerLine1) - let centerLine2 = new QLine([centerLine1.x2, outline.y1, centerLine1.x2 + halfLength, outline.y1], { - stroke: 'red', - strokeWidth: 2, - property: 'normal', - fontSize: 14, - }) + let centerLine2 = new QLine([centerLine1.x2, outline.y1, centerLine1.x2 + halfLength, outline.y1], centerLineOpt) canvas.add(centerLine2) canvas.remove(outline) //기존 라인 삭제 @@ -2279,7 +2445,9 @@ export function useMode() { //각 센터 라인을 그림 halfHoriCenterLinePoint.forEach((centerPoint) => { - let line = new QLine([centerPoint.x2, centerPoint.y1, centerPoint.x2, concaveLine.line.y1], centerLineOpt) + let tmpY2 = parallelLinesIdx !== centerPoint.index ? concaveLine.line.y1 : outLines[concavePointIndices[0]].y2 //평행선에서 내려오는 선은 아웃라인에 닿아야한다 + + let line = new QLine([centerPoint.x2, centerPoint.y1, centerPoint.x2, tmpY2], centerLineOpt) canvas.add(line) line['arrayIndex'] = centerPoint.index //커스텀으로 기존 index를 넣어줌 @@ -2287,18 +2455,21 @@ export function useMode() { }) vertCenterLine = reSortQlineArray(vertCenterLine) + lines = reSortQlineArray(lines) //해당라인에서 만나는점을 계산 vertCenterLine.forEach((vertLine) => { if (parallelLinesIdx !== vertLine.arrayIndex) { //평행선을 제외한 애들만 네모를 연결 let nearLine - + let nearOutline if (vertLine.arrayIndex > concaveLine.index) { //센터에 인덱스가 오목점 보다 크면 다음 작으면 앞에꺼 - nearLine = outLines[concaveLine.index + 1] + nearLine = lines[concaveLine.index + 1] + nearOutline = outLines[concaveLine.index + 1] } else { - nearLine = outLines[concaveLine.index - 1] + nearLine = lines[concaveLine.index - 1] + nearOutline = outLines[concaveLine.index - 1] } let nearLineY = nearLine.y1 @@ -2307,11 +2478,17 @@ export function useMode() { nearLineY = nearLine.y2 } - let centerExtendLine = new QLine([vertLine.line.x1, nearLineY, nearLine.x1, nearLineY], centerLineOpt) - canvas.add(centerExtendLine) + let centerExtendLine = new QLine([vertLine.line.x1, nearLineY, nearOutline.x1, nearLineY], centerLineOpt) + canvas.add(centerExtendLine) //새로그리고 + + //기존에 있는 라인에서 연장해서 새로 그림 + let centerExtendHoriLine = new QLine([nearOutline.line.x1, vertLine.y1, nearOutline.line.x2, vertLine.line.y2], centerLineOpt) + canvas.add(centerExtendHoriLine) + canvas.remove(nearOutline) + outLines.splice(nearOutline.idx, 1, centerExtendHoriLine) //아웃라인에 데이터를 다시 넣는다 let betweenCenterLine = (vertLine.line.y1 + vertLine.line.y2) / 2 - let centerDashLine = new QLine([vertLine.line.x1, betweenCenterLine, nearLine.x1, betweenCenterLine], dashedCenterLineOpt) + let centerDashLine = new QLine([vertLine.line.x1, betweenCenterLine, nearOutline.x1, betweenCenterLine], dashedCenterLineOpt) canvas.add(centerDashLine) horiCenterLine.push(centerDashLine) @@ -2319,30 +2496,108 @@ export function useMode() { } else { let longDashLine = halfHoriCenterLinePoint.find((obj) => obj.index === parallelLinesIdx) - let dashCenterExtendLineLength = (longDashLine.x2 - longDashLine.x1) * 2 + let dashCenterExtendLineLength = longDashLine.x2 - longDashLine.x1 let betweenCenterLine = (vertLine.line.y1 + vertLine.line.y2) / 2 + let totalLength = ((longDashLine.x2 - longDashLine.x1) * 2) / dashCenterExtendLineLength - let centerDashLine = new QLine( - [longDashLine.x1, betweenCenterLine, longDashLine.x1 + dashCenterExtendLineLength, betweenCenterLine], - dashedCenterLineOpt, - ) - canvas.add(centerDashLine) - horiCenterLine.push(centerDashLine) + //반 쪼개서 그린다 + for (let i = 0; i < totalLength; i++) { + let startX = i === 0 ? longDashLine.x1 : longDashLine.x1 + dashCenterExtendLineLength + let centerDashLine = new QLine([startX, betweenCenterLine, startX + dashCenterExtendLineLength, betweenCenterLine], dashedCenterLineOpt) + canvas.add(centerDashLine) + horiCenterLine.push(centerDashLine) + } } }) - if (parallelLinesIdx < concaveLine.index) { - offsetY = offsetY * -1 // 위로올린다 + //마지막에 오목한 외곽선을 연장한다 + const tmpLastOutLine = outLines[concavePointIndices[0]] + const lastOutLine = new QLine([shorVertCenterLine[0].x1, tmpLastOutLine.y1, shorVertCenterLine[1].x1, tmpLastOutLine.y2], centerLineOpt) + canvas.add(lastOutLine) + canvas.remove(tmpLastOutLine) + + let tmpVertCenterLine = outLines.filter((x, index) => index % 2 === 0) //세로만 찾음 + tmpVertCenterLine = tmpVertCenterLine.concat(vertCenterLine) + tmpVertCenterLine.sort((a, b) => a.x1 - b.x1) + tmpVertCenterLine.push(lastOutLine) + + let roofPatternPolygonArray = [] + let tmpArray = [] + let tmpBigArray = [] + + const lastCenterLine = tmpVertCenterLine[tmpVertCenterLine.length - 1] //마지막 센터라인을 정의 + + for (let i = 0; i < tmpVertCenterLine.length - 1; i++) { + //-1인건 마지막은 오목한 선이라 돌 필요 없음 + //라인 하나에 두점씩 나온다 + let firstPointObj = {} + let secondPointObj = {} + + let x1 = tmpVertCenterLine[i].x1 + let y1 = tmpVertCenterLine[i].y1 + let x2 = tmpVertCenterLine[i].x2 + let y2 = tmpVertCenterLine[i].y2 + + if (i === 2 || i === 4) { + tmpArray = [] + const prevLine = tmpVertCenterLine[i - 1] //뒤에서 앞라인을 찾는다 + const nextLine = tmpVertCenterLine[i + 1] + + //내 앞뒤 라인 + const tmpX1 = i === 2 ? prevLine.x1 : nextLine.x1 + const tmpY1 = i === 2 ? prevLine.y1 : nextLine.y1 + const tmpX2 = i === 2 ? prevLine.x2 : nextLine.x2 + const tmpY2 = i === 2 ? prevLine.y2 : nextLine.y2 + + firstPointObj = { x: tmpX1, y: tmpY1 } + secondPointObj = { x: tmpX2, y: tmpY2 } + tmpArray.push(firstPointObj) + tmpArray.push(secondPointObj) + + //현재 내 선 + firstPointObj = { x: x1, y: y1 } + secondPointObj = { x: x2, y: y2 } + tmpArray.push(firstPointObj) + tmpArray.push(secondPointObj) + roofPatternPolygonArray.push(tmpArray) + } else { + if (i === 1 || i === 5) { + // 큰 폴리곤은 가운데 선으로 되야됨 + if (outLines.length / 2 < concavePointIndices[0]) { + //오목이가 배열 전체보다 크면 위쪽 방향 + x2 = i === 1 ? lastCenterLine.x2 : lastCenterLine.x1 + y2 = i === 1 ? lastCenterLine.y2 : lastCenterLine.y1 + } else { + x1 = i === 1 ? lastCenterLine.x1 : lastCenterLine.x2 + y1 = i === 1 ? lastCenterLine.y1 : lastCenterLine.y2 + } + } + + if (i === 5) { + //5번일때는 앞에 3번에 선이 필요하다 + let prevX1 = tmpVertCenterLine[i - 2].x1 + let prevY1 = tmpVertCenterLine[i - 2].y1 + let prevX2 = tmpVertCenterLine[i - 2].x2 + let prevY2 = tmpVertCenterLine[i - 2].y2 + firstPointObj = { x: prevX1, y: prevY1 } + secondPointObj = { x: prevX2, y: prevY2 } + tmpBigArray.push(firstPointObj) + tmpBigArray.push(secondPointObj) + } + + firstPointObj = { x: x1, y: y1 } + secondPointObj = { x: x2, y: y2 } + tmpBigArray.push(firstPointObj) + tmpBigArray.push(secondPointObj) + + if (i === 3 || i === 6) { + roofPatternPolygonArray.push(tmpBigArray) + tmpBigArray = [] + } + } } - - const lastInLine = new QLine( - [shorVertCenterLine[0].x1, lines[concavePointIndices[0]].y1 + offsetY, shorVertCenterLine[1].x1, lines[concavePointIndices[0]].y2 + offsetY], - centerLineOpt, - ) - canvas.add(lastInLine) - canvas.remove(outLines[concavePointIndices[0]]) + setRoofPolygonPattern({ roofPatternPolygonArray, lines }) } - canvas.renderAll() } @@ -2871,8 +3126,6 @@ export function useMode() { } const makeRoofPatternPolygon = (roofStyle) => { - console.log('roofPolygonPattern', roofPolygonPattern) - if (Object.keys(roofPolygonPattern).length === 0 && roofPolygonPattern.constructor === Object) { alert('객체가 비어있습니다.') return @@ -2918,7 +3171,7 @@ export function useMode() { const commonOption = { fill: pattern, - selectable: false, + selectable: true, fontSize: 15, // fontSize는 필요에 따라 조정 } From bdd928ba265c8ea064ed18f6b94d289316c329e7 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Sat, 27 Jul 2024 16:34:47 +0900 Subject: [PATCH 10/27] =?UTF-8?q?QLine=20=EB=B3=B5=EA=B5=AC=20=EC=9E=91?= =?UTF-8?q?=EC=97=85=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Roof2.jsx | 1 + src/components/fabric/QLine.js | 7 +++ src/components/fabric/QLine3.js | 91 +++++++++++++++++++++++++++++++++ src/hooks/useCanvas.js | 19 +++++-- src/util/qline-utils.js | 11 ++-- src/util/qpolygon-utils.js | 3 +- 6 files changed, 119 insertions(+), 13 deletions(-) create mode 100644 src/components/fabric/QLine3.js diff --git a/src/components/Roof2.jsx b/src/components/Roof2.jsx index 370a1e2e..e483ad7e 100644 --- a/src/components/Roof2.jsx +++ b/src/components/Roof2.jsx @@ -11,6 +11,7 @@ import { canvasAtom, canvasListState, canvasSizeState, fontSizeState, sortedPoly import { QLine } from '@/components/fabric/QLine' import { getTests, getCanvasState, insertCanvasState } from '@/lib/canvas' import { calculateIntersection2 } from '@/util/canvas-util' +import { CustomLine } from '@/components/fabric/QLine' export default function Roof2() { const { canvas, handleRedo, handleUndo, setCanvasBackgroundWithDots, saveImage, addCanvas } = useCanvas('canvas') diff --git a/src/components/fabric/QLine.js b/src/components/fabric/QLine.js index 9c8c2133..a6916774 100644 --- a/src/components/fabric/QLine.js +++ b/src/components/fabric/QLine.js @@ -91,6 +91,13 @@ export class QLine extends fabric.Group { }) } + static fromObject(object, callback) { + const { points, initOption, initLengthTxt } = object + const instance = new QLine(points, initOption, initLengthTxt) + callback && callback(instance) + return instance + } + addLengthText(isFirst) { if (this.text) { this.removeWithUpdate(this.text) diff --git a/src/components/fabric/QLine3.js b/src/components/fabric/QLine3.js new file mode 100644 index 00000000..9002d9a7 --- /dev/null +++ b/src/components/fabric/QLine3.js @@ -0,0 +1,91 @@ +import { fabric } from 'fabric' + +export const QLine2 = fabric.util.createClass(fabric.Line, { + type: 'QLine2', + text: null, + canvas: null, + initialize: function (points, options, canvas) { + if (canvas) { + this.canvas = canvas + } + this.callSuper('initialize', points, options) + }, + + toObject: function (propertiesToInclude) { + return fabric.util.object.extend(this.callSuper('toObject', propertiesToInclude), { + type: this.type, + x1: this.x1, + y1: this.y1, + x2: this.x2, + y2: this.y2, + }) + }, + init: function () { + this.addLengthText(true) + + this.on('moving', () => { + this.addLengthText(false) + }) + + this.on('modified', (e) => { + this.addLengthText(false) + }) + + this.on('removed', () => { + if (this.text) { + this.canvas.remove(this.text) + } + + this.text = null + }) + }, + + addLengthText(isFirst) { + if (this.text) { + this.canvas.remove(this.text) + this.text = null + } + + if (isFirst && this.lengthTxt > 0) { + const text = new fabric.Textbox(this.lengthTxt.toFixed(0).toString(), { + left: (this.x1 + this.x2) / 2, + top: (this.y1 + this.y2) / 2, + fontSize: this.fontSize, + }) + this.length = this.lengthTxt + this.text = text + this.canvas.add(text) + return + } + const scaleX = this.scaleX + const scaleY = this.scaleY + const x1 = this.left + const y1 = this.top + const x2 = this.left + this.width * scaleX + const y2 = this.top + this.height * scaleY + const dx = x2 - x1 + const dy = y2 - y1 + this.length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(0)) + + const text = new fabric.Textbox(this.length.toFixed(0).toString(), { + left: (x1 + x2) / 2, + top: (y1 + y2) / 2, + fontSize: this.fontSize, + selectable: false, + }) + text.set({ name: 'lengthText' }) + this.text = text + this.canvas.add(text) + }, + setFontSize(fontSize) { + this.fontSize = fontSize + this.text.set({ fontSize }) + }, + _render: function (ctx) { + this.callSuper('_render', ctx) + this.init() + }, + _set: function (key, value) { + this.callSuper('_set', key, value) + }, +}) diff --git a/src/hooks/useCanvas.js b/src/hooks/useCanvas.js index 44ed17ce..2ffdf9f9 100644 --- a/src/hooks/useCanvas.js +++ b/src/hooks/useCanvas.js @@ -110,13 +110,24 @@ export function useCanvas(id) { fabric.Object.prototype.cornerStyle = 'rect' fabric.Object.prototype.cornerStrokeColor = '#2BEBC8' fabric.Object.prototype.cornerSize = 6 - + fabric.QLine = QLine QPolygon.prototype.canvas = canvas QLine.prototype.canvas = canvas QRect.prototype.canvas = canvas - fabric.QLine = fabric.util.createClass(fabric.Group, {}) + QLine.fromObject = function (object, callback) { + const { x1, x2, y1, y2 } = object + const instance = new QLine([x1, y1, x2, y2], object, canvas) + canvas?.add(instance) + } + + // fabric.QLine.fromObject = fabric.Line.fromObject + + // CustomLine.prototype.canvas = canvas + + // fabric.QLine = fabric.util.createClass(fabric.Group, {}) fabric.QPolygon = fabric.util.createClass(fabric.Group, {}) + // Register the custom class with fabric // fromObject 메서드를 QLine 클래스에 직접 추가 fabric.QLine.fromObject = function (object, callback) { @@ -577,9 +588,7 @@ export function useCanvas(id) { }, function (o, object) { // 각 객체가 로드될 때마다 호출됩니다. - console.log('Object loaded: ', o, object) - - canvas?.add(object) + // canvas?.add(object) canvas?.renderAll() }, ) diff --git a/src/util/qline-utils.js b/src/util/qline-utils.js index 4cbcb118..77821886 100644 --- a/src/util/qline-utils.js +++ b/src/util/qline-utils.js @@ -2,11 +2,8 @@ import { fabric } from 'fabric' import { QLine } from '@/components/fabric/QLine' export const defineQLine = () => { - fabric.QLine = fabric.util.createClass(fabric.Group, {}) - fabric.QLine.fromObject = function (object, callback) { - const { initOption, initPoints, initLengthTxt } = object - fabric.util.enlivenObjects(object.objects, function (enlivenedObjects) { - return callback(new QLine(initPoints, initOption, initLengthTxt)) - }) - } + /*fabric.QLine = QLine + fabric.QLine.fromObject = (object, callback) => { + return new fabric.QLine([object.x1, object.y1, object.x2, object.y2], object) + }*/ } diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index bd5676bd..d09b8f21 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -20,8 +20,9 @@ export const defineQPloygon = () => { // fromObject 메서드를 QLine 클래스에 직접 추가 fabric.QPolygon.fromObject = function (object, callback) { const { initOption, initPoints, initLengthTxt } = object + fabric.util.enlivenObjects(object.objects, function (enlivenedObjects) { - return callback(new QPolygon(initPoints, initOption, initLengthTxt)) + return callback(new QPolygon(initPoints, object, initLengthTxt)) }) } } From 96b1e5822ffd4e85a16187a9a5f8e2f590cac749 Mon Sep 17 00:00:00 2001 From: yjnoh Date: Mon, 29 Jul 2024 11:32:30 +0900 Subject: [PATCH 11/27] =?UTF-8?q?8=EA=B0=81=20=E3=84=B7=EC=9E=90=20?= =?UTF-8?q?=ED=8C=A8=ED=84=B4=20=EC=9E=91=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useMode.js | 189 +++++++++++++++++++++++-------------------- 1 file changed, 101 insertions(+), 88 deletions(-) diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index 033b0a7f..1ee98028 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -2226,6 +2226,7 @@ export function useMode() { let edgeIndexArray = [] if (concavePointIndices[0] % 2 === 0) { + //concave가 짝수면 좌우로 그려진 ㄷ자 //케라바 색을 바꾼다 lines.forEach((line, index) => { if (index % 2 === 0) { @@ -2236,8 +2237,6 @@ export function useMode() { } }) - console.log('edgeIndexArray', edgeIndexArray) - outLines = reSortQlineArray(outLines) edgeIndexArray.forEach((idx, index) => { //가로라인이 케라바 라인임 @@ -2257,8 +2256,6 @@ export function useMode() { } }) - console.log('halfHoriCenterLinePoint', halfHoriCenterLinePoint) - // //각 센터 라인을 그림 halfHoriCenterLinePoint.forEach((centerPoint) => { let tmpX2 = parallelLinesIdx !== centerPoint.index ? concaveLine.line.x2 : outLines[concavePointIndices[0]].x1 //평행선에서 내려오는 선은 아웃라인에 닿아야한다 @@ -2273,8 +2270,6 @@ export function useMode() { vertCenterLine = reSortQlineArray(vertCenterLine) lines = reSortQlineArray(lines) - console.log('parallelLinesIdx', parallelLinesIdx) - //해당라인에서 만나는점을 계산 vertCenterLine.forEach((vertLine) => { if (parallelLinesIdx !== vertLine.arrayIndex) { @@ -2302,118 +2297,136 @@ export function useMode() { canvas.remove(nearOutline) outLines.splice(nearOutline.idx, 1, centerExtendHoriLine) //아웃라인에 데이터를 다시 넣는다 - let centerExtendLine = new QLine([vertLine.line.x2, vertLine.line.y2, centerExtendHoriLine.x2, centerExtendHoriLine.y2], centerLineOpt) + //가로형에선 기본으로 ㄷ자 형태로 한다 + let centerExtendLine = new QLine([vertLine.line.x1, vertLine.line.y1, centerExtendHoriLine.x1, centerExtendHoriLine.y1], centerLineOpt) + + //오목이가 배열에 반보다 작으면 역 ㄷ자 여서 변경 + if (concavePointIndices[0] < outLines.length / 2) { + centerExtendLine = new QLine([vertLine.line.x2, vertLine.line.y2, centerExtendHoriLine.x2, centerExtendHoriLine.y2], centerLineOpt) + } + canvas.add(centerExtendLine) //새로그리고 let betweenCenterLine = (vertLine.line.x1 + vertLine.line.x2) / 2 - let centerDashLine = new QLine([vertLine.line.y1, betweenCenterLine, nearOutline.y1, betweenCenterLine], dashedCenterLineOpt) + let centerDashLine = new QLine([betweenCenterLine, centerExtendLine.y1, betweenCenterLine, centerExtendLine.y2], dashedCenterLineOpt) canvas.add(centerDashLine) horiCenterLine.push(centerDashLine) shorVertCenterLine.push(vertLine) //마지막에 가운데 선을 긋기 위해 담음 } else { - let longDashLine = halfHoriCenterLinePoint.find((obj) => obj.index === parallelLinesIdx) + let longDashLine = halfHoriCenterLinePoint.find((obj) => obj.index === parallelLinesIdx) //평행선 - let dashCenterExtendLineLength = longDashLine.x2 - longDashLine.x1 - let betweenCenterLine = (vertLine.line.y1 + vertLine.line.y2) / 2 - let totalLength = ((longDashLine.x2 - longDashLine.x1) * 2) / dashCenterExtendLineLength + let dashCenterExtendLineLength = longDashLine.y2 - longDashLine.y1 //y반개 길이 + let betweenCenterLine = (vertLine.line.x1 + vertLine.line.x2) / 2 //y의 길이 + let totalLength = ((longDashLine.y2 - longDashLine.y1) * 2) / dashCenterExtendLineLength //2개로 나눔 //반 쪼개서 그린다 for (let i = 0; i < totalLength; i++) { - let startX = i === 0 ? longDashLine.x1 : longDashLine.x1 + dashCenterExtendLineLength - let centerDashLine = new QLine([startX, betweenCenterLine, startX + dashCenterExtendLineLength, betweenCenterLine], dashedCenterLineOpt) + //2번에 나눠서 + let startY = i === 0 ? longDashLine.y1 : longDashLine.y1 + dashCenterExtendLineLength //시작 하는 y의 좌표 + //x값은 고정이임 //TODO: 지붕 각도 계산법에 의해 재계산해야함 + let centerDashLine = new QLine([betweenCenterLine, startY, betweenCenterLine, startY + dashCenterExtendLineLength], dashedCenterLineOpt) canvas.add(centerDashLine) horiCenterLine.push(centerDashLine) } } }) - // //마지막에 오목한 외곽선을 연장한다 - // const tmpLastOutLine = outLines[concavePointIndices[0]] - // const lastOutLine = new QLine([shorVertCenterLine[0].x1, tmpLastOutLine.y1, shorVertCenterLine[1].x1, tmpLastOutLine.y2], centerLineOpt) - // canvas.add(lastOutLine) - // canvas.remove(tmpLastOutLine) + //마지막에 오목한 외곽선을 연장한다 + const tmpLastOutLine = outLines[concavePointIndices[0]] + const lastOutLine = new QLine([tmpLastOutLine.x1, shorVertCenterLine[0].y1, tmpLastOutLine.x1, shorVertCenterLine[1].y1], centerLineOpt) + canvas.add(lastOutLine) + canvas.remove(tmpLastOutLine) - // let tmpVertCenterLine = outLines.filter((x, index) => index % 2 === 0) //세로만 찾음 - // tmpVertCenterLine = tmpVertCenterLine.concat(vertCenterLine) - // tmpVertCenterLine.sort((a, b) => a.x1 - b.x1) - // tmpVertCenterLine.push(lastOutLine) + //폴리곤 패턴을 그리기 위해 작성 + let tmpVertCenterLine = outLines.filter((x, index) => index % 2 !== 0) //세로만 찾음 + tmpVertCenterLine = tmpVertCenterLine.concat(vertCenterLine) + tmpVertCenterLine.sort((a, b) => a.y1 - b.y1) + tmpVertCenterLine.push(lastOutLine) - // let roofPatternPolygonArray = [] - // let tmpArray = [] - // let tmpBigArray = [] + let roofPatternPolygonArray = [] + let tmpArray = [] + let tmpBigArray = [] - // const lastCenterLine = tmpVertCenterLine[tmpVertCenterLine.length - 1] //마지막 센터라인을 정의 + console.log('tmpVertCenterLine', tmpVertCenterLine) - // for (let i = 0; i < tmpVertCenterLine.length - 1; i++) { - // //-1인건 마지막은 오목한 선이라 돌 필요 없음 - // //라인 하나에 두점씩 나온다 - // let firstPointObj = {} - // let secondPointObj = {} + const lastCenterLine = tmpVertCenterLine[tmpVertCenterLine.length - 1] //마지막 센터라인을 정의 - // let x1 = tmpVertCenterLine[i].x1 - // let y1 = tmpVertCenterLine[i].y1 - // let x2 = tmpVertCenterLine[i].x2 - // let y2 = tmpVertCenterLine[i].y2 + for (let i = 0; i < tmpVertCenterLine.length - 1; i++) { + //-1인건 마지막은 오목한 선이라 돌 필요 없음 + //라인 하나에 두점씩 나온다 + let firstPointObj = {} + let secondPointObj = {} - // if (i === 2 || i === 4) { - // tmpArray = [] - // const prevLine = tmpVertCenterLine[i - 1] //뒤에서 앞라인을 찾는다 - // const nextLine = tmpVertCenterLine[i + 1] + let x1 = tmpVertCenterLine[i].x1 + let y1 = tmpVertCenterLine[i].y1 + let x2 = tmpVertCenterLine[i].x2 + let y2 = tmpVertCenterLine[i].y2 - // //내 앞뒤 라인 - // const tmpX1 = i === 2 ? prevLine.x1 : nextLine.x1 - // const tmpY1 = i === 2 ? prevLine.y1 : nextLine.y1 - // const tmpX2 = i === 2 ? prevLine.x2 : nextLine.x2 - // const tmpY2 = i === 2 ? prevLine.y2 : nextLine.y2 + if (i === 2 || i === 4) { + //작은 네모들 + tmpArray = [] + const prevLine = tmpVertCenterLine[i - 1] //뒤에서 앞라인을 찾는다 + const nextLine = tmpVertCenterLine[i + 1] - // firstPointObj = { x: tmpX1, y: tmpY1 } - // secondPointObj = { x: tmpX2, y: tmpY2 } - // tmpArray.push(firstPointObj) - // tmpArray.push(secondPointObj) + //내 앞뒤 라인 + const tmpX1 = i === 2 ? prevLine.x1 : nextLine.x1 + const tmpY1 = i === 2 ? prevLine.y1 : nextLine.y1 + const tmpX2 = i === 2 ? prevLine.x2 : nextLine.x2 + const tmpY2 = i === 2 ? prevLine.y2 : nextLine.y2 - // //현재 내 선 - // firstPointObj = { x: x1, y: y1 } - // secondPointObj = { x: x2, y: y2 } - // tmpArray.push(firstPointObj) - // tmpArray.push(secondPointObj) - // roofPatternPolygonArray.push(tmpArray) - // } else { - // if (i === 1 || i === 5) { - // // 큰 폴리곤은 가운데 선으로 되야됨 - // if (outLines.length / 2 < concavePointIndices[0]) { - // //오목이가 배열 전체보다 크면 위쪽 방향 - // x2 = i === 1 ? lastCenterLine.x2 : lastCenterLine.x1 - // y2 = i === 1 ? lastCenterLine.y2 : lastCenterLine.y1 - // } else { - // x1 = i === 1 ? lastCenterLine.x1 : lastCenterLine.x2 - // y1 = i === 1 ? lastCenterLine.y1 : lastCenterLine.y2 - // } - // } + firstPointObj = { x: tmpX1, y: tmpY1 } + secondPointObj = { x: tmpX2, y: tmpY2 } + tmpArray.push(firstPointObj) + tmpArray.push(secondPointObj) - // if (i === 5) { - // //5번일때는 앞에 3번에 선이 필요하다 - // let prevX1 = tmpVertCenterLine[i - 2].x1 - // let prevY1 = tmpVertCenterLine[i - 2].y1 - // let prevX2 = tmpVertCenterLine[i - 2].x2 - // let prevY2 = tmpVertCenterLine[i - 2].y2 - // firstPointObj = { x: prevX1, y: prevY1 } - // secondPointObj = { x: prevX2, y: prevY2 } - // tmpBigArray.push(firstPointObj) - // tmpBigArray.push(secondPointObj) - // } + //현재 내 선 + firstPointObj = { x: x1, y: y1 } + secondPointObj = { x: x2, y: y2 } + tmpArray.push(firstPointObj) + tmpArray.push(secondPointObj) + roofPatternPolygonArray.push(tmpArray) + } else { + //큰 육각 + if (i === 1 || i === 5) { + // 큰 폴리곤은 가운데 선으로 되야됨 + if (outLines.length / 2 > concavePointIndices[0]) { + x2 = i === 1 ? lastCenterLine.x1 : lastCenterLine.x2 + y2 = i === 1 ? lastCenterLine.y1 : lastCenterLine.y2 + } else { + //오목이가 배열 전체보다 크면 오른쪽 오목이 + x1 = i === 1 ? lastCenterLine.x1 : lastCenterLine.x2 + y1 = i === 1 ? lastCenterLine.y2 : lastCenterLine.y1 + } + } - // firstPointObj = { x: x1, y: y1 } - // secondPointObj = { x: x2, y: y2 } - // tmpBigArray.push(firstPointObj) - // tmpBigArray.push(secondPointObj) + if (i === 5) { + //5번일때는 앞에 3번에 선이 필요하다 + let prevX1 = tmpVertCenterLine[i - 2].x1 + let prevY1 = tmpVertCenterLine[i - 2].y1 + let prevX2 = tmpVertCenterLine[i - 2].x2 + let prevY2 = tmpVertCenterLine[i - 2].y2 + firstPointObj = { x: prevX1, y: prevY1 } + secondPointObj = { x: prevX2, y: prevY2 } + tmpBigArray.push(firstPointObj) + tmpBigArray.push(secondPointObj) + } - // if (i === 3 || i === 6) { - // roofPatternPolygonArray.push(tmpBigArray) - // tmpBigArray = [] - // } - // } - // } + firstPointObj = { x: x1, y: y1 } + secondPointObj = { x: x2, y: y2 } + tmpBigArray.push(firstPointObj) + tmpBigArray.push(secondPointObj) + + if (i === 3 || i === 6) { + roofPatternPolygonArray.push(tmpBigArray) + tmpBigArray = [] + } + } + } + + console.log('roofPatternPolygonArray', roofPatternPolygonArray) + + setRoofPolygonPattern({ roofPatternPolygonArray, lines }) } else { // 오목한 부분이 세로선일때 아래ㄷ, 위ㄷ //라인들을 좌측에서 -> 우측으로 그리는거처럼 데이터 보정 From 5b1dd76aa6a0bc6274173c92f04d74774b0e4368 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Mon, 29 Jul 2024 15:33:43 +0900 Subject: [PATCH 12/27] =?UTF-8?q?QLine,=20QPolygon=20canvas=20=EC=97=AD?= =?UTF-8?q?=EC=A7=81=EB=A0=AC=ED=99=94=20=EB=AC=B8=EC=A0=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/components/Roof2.jsx | 60 +- src/components/fabric/QLine.js | 243 +++----- src/components/fabric/QLine2.js | 184 ++++++ src/components/fabric/QLine3.js | 91 --- src/components/fabric/QPolygon.js | 902 ++++----------------------- src/components/fabric/QPolygon3.js | 941 +++++++++++++++++++++++++++++ src/hooks/useCanvas.js | 69 +-- src/hooks/useMode.js | 2 +- src/util/qpolygon-utils.js | 11 +- 9 files changed, 1395 insertions(+), 1108 deletions(-) create mode 100644 src/components/fabric/QLine2.js delete mode 100644 src/components/fabric/QLine3.js create mode 100644 src/components/fabric/QPolygon3.js diff --git a/src/components/Roof2.jsx b/src/components/Roof2.jsx index e483ad7e..9d36b6b0 100644 --- a/src/components/Roof2.jsx +++ b/src/components/Roof2.jsx @@ -3,7 +3,6 @@ import { useEffect, useState } from 'react' import { Mode, useMode } from '@/hooks/useMode' import { Button } from '@nextui-org/react' import QRect from '@/components/fabric/QRect' -import QPolygon from '@/components/fabric/QPolygon' import RangeSlider from './ui/RangeSlider' import { useRecoilState, useRecoilValue } from 'recoil' @@ -12,6 +11,7 @@ import { QLine } from '@/components/fabric/QLine' import { getTests, getCanvasState, insertCanvasState } from '@/lib/canvas' import { calculateIntersection2 } from '@/util/canvas-util' import { CustomLine } from '@/components/fabric/QLine' +import { QPolygon } from '@/components/fabric/QPolygon' export default function Roof2() { const { canvas, handleRedo, handleUndo, setCanvasBackgroundWithDots, saveImage, addCanvas } = useCanvas('canvas') @@ -100,12 +100,10 @@ export default function Roof2() { selectable: true, fontSize: fontSize, }, - canvas, ) - canvas?.add(polygon) - polygon.fillCell({ width: 50, height: 30, padding: 10 }) + // polygon.fillCell({ width: 50, height: 30, padding: 10 }) } } @@ -187,14 +185,14 @@ export default function Roof2() { ] const eightPoint = [ - { x: 240, y: 130 }, - { x: 240, y: 630 }, - { x: 640, y: 630 }, - { x: 640, y: 480 }, - { x: 440, y: 480 }, - { x: 440, y: 280 }, - { x: 740, y: 280 }, - { x: 740, y: 130 }, + { x: 240.1111, y: 130.1111 }, + { x: 240.1111, y: 630.1111 }, + { x: 640.1111, y: 630.1111 }, + { x: 640.1111, y: 480.1111 }, + { x: 440.1111, y: 480.1111 }, + { x: 440.1111, y: 280.1111 }, + { x: 740.1111, y: 280.1111 }, + { x: 740.1111, y: 130.1111 }, ] const eightPoint2 = [ @@ -209,7 +207,7 @@ export default function Roof2() { ] if (canvas) { - const polygon = new QPolygon(type1, { + const polygon = new QPolygon(eightPoint, { fill: 'transparent', stroke: 'black', strokeWidth: 1, @@ -220,8 +218,6 @@ export default function Roof2() { canvas?.add(polygon) - console.log(polygon) - handleOuterlinesTest2(polygon) // const lines = togglePolygonLine(polygon) @@ -242,27 +238,19 @@ export default function Roof2() { const makeQLine = () => { if (canvas) { - const line = new QLine( - [50, 250, 900, 250], - { - stroke: 'black', - strokeWidth: 5, - fontSize: fontSize, - selectable: true, - }, - 50, - ) + const line = new QLine([50, 250, 900, 250], { + stroke: 'black', + strokeWidth: 5, + fontSize: fontSize, + selectable: true, + }) - const line2 = new QLine( - [450, 450, 821, 78], - { - stroke: 'black', - strokeWidth: 5, - fontSize: fontSize, - selectable: true, - }, - 50, - ) + const line2 = new QLine([450, 450, 821, 78], { + stroke: 'black', + strokeWidth: 5, + fontSize: fontSize, + selectable: true, + }) canvas?.add(line) canvas?.add(line2) @@ -270,8 +258,6 @@ export default function Roof2() { const interSectionPoint = calculateIntersection2(line, line2) if (interSectionPoint) { - console.log(interSectionPoint) - const circle = new fabric.Circle({ radius: 5, fill: 'red', diff --git a/src/components/fabric/QLine.js b/src/components/fabric/QLine.js index a6916774..2d1e87df 100644 --- a/src/components/fabric/QLine.js +++ b/src/components/fabric/QLine.js @@ -1,127 +1,87 @@ import { fabric } from 'fabric' -import { getDirection, getDirectionByPoint } from '@/util/canvas-util' +import { v4 as uuidv4 } from 'uuid' +import { getDirection } from '@/util/canvas-util' -export class QLine extends fabric.Group { - line - text - fontSize - length = 0 - x1 - y1 - x2 - y2 - direction - idx - type = 'QLine' - parent - isAlreadyInterSection = false - - interSectionPoints = [] - lengthTxt = 0 - - initPoints - initOption - initLengthTxt - - constructor(points, option = { isActiveLengthText: true }, lengthTxt) { +export const QLine = fabric.util.createClass(fabric.Line, { + type: 'QLine', + text: null, + id: null, + line: null, + length: 0, + direction: null, + idx: 0, + initialize: function (points, options, canvas) { + this.callSuper('initialize', points, options) + if (options.id) { + this.id = options.id + } else { + this.id = uuidv4() + } + this.line = this // 소수점 전부 제거 points.forEach((point) => { point = Math.round(point) }) - const [x1, y1, x2, y2] = points - - if (!option.fontSize) { - throw new Error('Font size is required.') - } - - const line = new fabric.Line(points, { ...option, strokeWidth: 1 }) - - // 길이가 1이하인 선은 생성하지 않음 - if (Math.abs(x1 - x2) <= 1 && Math.abs(y1 - y2) <= 1) { - super([], {}) - this.initPoints = points - this.initOption = option - this.initLengthTxt = lengthTxt - } else { - super([line], {}) - this.initPoints = points - this.initOption = option - this.initLengthTxt = lengthTxt - - this.x1 = x1 - this.y1 = y1 - this.x2 = x2 - this.y2 = y2 - this.line = line - this.fontSize = option.fontSize - this.direction = option.direction ?? getDirectionByPoint({ x: x1, y: y1 }, { x: x2, y: y2 }) - this.parent = option.parent - this.idx = option.idx - - if (lengthTxt > 0) { - this.lengthTxt = Number(lengthTxt) - } - - option.isActiveLengthText ?? this.init() - this.addControl() - } - } - - init() { - this.addLengthText(true) - } - - addControl() { - this.on('moving', () => { - this.addLengthText(false) - }) - - this.on('modified', (e) => { - this.addLengthText(false) - }) - - this.on('selected', () => { - console.log(this) - Object.keys(this.controls).forEach((controlKey) => { - if (controlKey !== 'ml' && controlKey !== 'mr') { - this.setControlVisible(controlKey, false) - } - }) - }) - } - - static fromObject(object, callback) { - const { points, initOption, initLengthTxt } = object - const instance = new QLine(points, initOption, initLengthTxt) - callback && callback(instance) - return instance - } - - addLengthText(isFirst) { - if (this.text) { - this.removeWithUpdate(this.text) - this.text = null - } - - if (isFirst && this.lengthTxt > 0) { - const text = new fabric.Textbox(this.lengthTxt.toFixed(0).toString(), { - left: (this.x1 + this.x2) / 2, - top: (this.y1 + this.y2) / 2, - fontSize: this.fontSize, - }) - this.length = this.lengthTxt - this.text = text - this.addWithUpdate(text) - return - } - const scaleX = this.scaleX const scaleY = this.scaleY const x1 = this.left const y1 = this.top const x2 = this.left + this.width * scaleX const y2 = this.top + this.height * scaleY + this.idx = options.idx ?? 0 + const dx = x2 - x1 + const dy = y2 - y1 + this.length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(0)) + + this.direction = options.direction ?? getDirection({ x: this.x1, y: this.y1 }, { x: this.x2, y: this.y2 }) + + if (canvas) { + this.canvas = canvas + } + }, + + toObject: function (propertiesToInclude) { + return fabric.util.object.extend(this.callSuper('toObject', propertiesToInclude), { + type: this.type, + text: this.text, + }) + }, + init: function () { + this.addLengthText() + + this.on('moving', () => { + this.addLengthText() + }) + + this.on('modified', (e) => { + this.addLengthText() + }) + + this.on('removed', () => { + const thisText = this.canvas.getObjects().find((obj) => obj.name === 'lengthText' && obj.parentId === this.id) + if (thisText) { + this.canvas.remove(thisText) + } + this.text = null + }) + }, + + addLengthText() { + const thisText = this.canvas.getObjects().find((obj) => obj.name === 'lengthText' && obj.parentId === this.id) + + const scaleX = this.scaleX + const scaleY = this.scaleY + const x1 = this.left + const y1 = this.top + const x2 = this.left + this.width * scaleX + const y2 = this.top + this.height * scaleY + + if (thisText) { + thisText.set({ text: this.length.toFixed(0).toString(), left: (x1 + x2) / 2, top: (y1 + y2) / 2 }) + return + } + const dx = x2 - x1 const dy = y2 - y1 this.length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(0)) @@ -130,52 +90,27 @@ export class QLine extends fabric.Group { left: (x1 + x2) / 2, top: (y1 + y2) / 2, fontSize: this.fontSize, + selectable: false, + parentId: this.id, + name: 'lengthText', }) - this.text = text - this.addWithUpdate(text) - } + this.text = text + this.canvas.add(text) + }, setFontSize(fontSize) { this.fontSize = fontSize this.text.set({ fontSize }) - this.addWithUpdate() - } - - fromObject(object, callback) { - console.log('fromObject', object, callback) - } - - async = true - - toObject(propertiesToInclude) { - return fabric.util.object.extend(this.callSuper('toObject'), { - length: this.length, - line: this.line, - text: this.text, - fontSize: this.fontSize, - x1: this.x1, - y1: this.y1, - x2: this.x2, - y2: this.y2, - direction: this.direction, - idx: this.idx, - type: this.type, - parent: this.parent, - isAlreadyInterSection: this.isAlreadyInterSection, - interSectionPoints: this.interSectionPoints, - lengthTxt: this.lengthTxt, - setFontSize: this.setFontSize, - addLengthText: this.addLengthText, - init: this.init, - addControl: this.addControl, - initPoints: this.initPoints, - initOption: this.initOption, - initLengthTxt: this.initLengthTxt, - points: this.points, - }) - } - - _set(key, value) { + }, + _render: function (ctx) { + this.callSuper('_render', ctx) + this.init() + }, + _set: function (key, value) { this.callSuper('_set', key, value) - } -} + }, + + setCanvas(canvas) { + this.canvas = canvas + }, +}) diff --git a/src/components/fabric/QLine2.js b/src/components/fabric/QLine2.js new file mode 100644 index 00000000..9379ba26 --- /dev/null +++ b/src/components/fabric/QLine2.js @@ -0,0 +1,184 @@ +/** + * 문제 없는경우 제거 + */ +import { fabric } from 'fabric' +import { getDirection, getDirectionByPoint } from '@/util/canvas-util' + +export class QLine2 extends fabric.Group { + line + text + fontSize + length = 0 + x1 + y1 + x2 + y2 + direction + idx + type = 'QLine' + parent + isAlreadyInterSection = false + + interSectionPoints = [] + lengthTxt = 0 + + initPoints + initOption + initLengthTxt + + constructor(points, option = { isActiveLengthText: true }, lengthTxt) { + // 소수점 전부 제거 + points.forEach((point) => { + point = Math.round(point) + }) + + const [x1, y1, x2, y2] = points + + if (!option.fontSize) { + throw new Error('Font size is required.') + } + + const line = new fabric.Line(points, { ...option, strokeWidth: 1 }) + + // 길이가 1이하인 선은 생성하지 않음 + if (Math.abs(x1 - x2) <= 1 && Math.abs(y1 - y2) <= 1) { + super([], {}) + this.initPoints = points + this.initOption = option + this.initLengthTxt = lengthTxt + } else { + super([line], {}) + this.initPoints = points + this.initOption = option + this.initLengthTxt = lengthTxt + + this.x1 = x1 + this.y1 = y1 + this.x2 = x2 + this.y2 = y2 + this.line = line + this.fontSize = option.fontSize + this.direction = option.direction ?? getDirectionByPoint({ x: x1, y: y1 }, { x: x2, y: y2 }) + this.parent = option.parent + this.idx = option.idx + + if (lengthTxt > 0) { + this.lengthTxt = Number(lengthTxt) + } + + option.isActiveLengthText ?? this.init() + this.addControl() + } + } + + init() { + this.addLengthText(true) + } + + addControl() { + this.on('moving', () => { + this.addLengthText(false) + }) + + this.on('modified', (e) => { + this.addLengthText(false) + }) + + this.on('selected', () => { + console.log(this) + Object.keys(this.controls).forEach((controlKey) => { + if (controlKey !== 'ml' && controlKey !== 'mr') { + this.setControlVisible(controlKey, false) + } + }) + }) + } + + static fromObject(object, callback) { + const { points, initOption, initLengthTxt } = object + const instance = new QLine(points, initOption, initLengthTxt) + callback && callback(instance) + return instance + } + + addLengthText(isFirst) { + if (this.text) { + this.removeWithUpdate(this.text) + this.text = null + } + + if (isFirst && this.lengthTxt > 0) { + const text = new fabric.Textbox(this.lengthTxt.toFixed(0).toString(), { + left: (this.x1 + this.x2) / 2, + top: (this.y1 + this.y2) / 2, + fontSize: this.fontSize, + }) + this.length = this.lengthTxt + this.text = text + this.addWithUpdate(text) + return + } + + const scaleX = this.scaleX + const scaleY = this.scaleY + const x1 = this.left + const y1 = this.top + const x2 = this.left + this.width * scaleX + const y2 = this.top + this.height * scaleY + const dx = x2 - x1 + const dy = y2 - y1 + this.length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(0)) + + const text = new fabric.Textbox(this.length.toFixed(0).toString(), { + left: (x1 + x2) / 2, + top: (y1 + y2) / 2, + fontSize: this.fontSize, + }) + this.text = text + this.addWithUpdate(text) + } + + setFontSize(fontSize) { + this.fontSize = fontSize + this.text.set({ fontSize }) + this.addWithUpdate() + } + + fromObject(object, callback) { + console.log('fromObject', object, callback) + } + + async = true + + toObject(propertiesToInclude) { + return fabric.util.object.extend(this.callSuper('toObject'), { + length: this.length, + line: this.line, + text: this.text, + fontSize: this.fontSize, + x1: this.x1, + y1: this.y1, + x2: this.x2, + y2: this.y2, + direction: this.direction, + idx: this.idx, + type: this.type, + parent: this.parent, + isAlreadyInterSection: this.isAlreadyInterSection, + interSectionPoints: this.interSectionPoints, + lengthTxt: this.lengthTxt, + setFontSize: this.setFontSize, + addLengthText: this.addLengthText, + init: this.init, + addControl: this.addControl, + initPoints: this.initPoints, + initOption: this.initOption, + initLengthTxt: this.initLengthTxt, + points: this.points, + }) + } + + _set(key, value) { + this.callSuper('_set', key, value) + } +} diff --git a/src/components/fabric/QLine3.js b/src/components/fabric/QLine3.js deleted file mode 100644 index 9002d9a7..00000000 --- a/src/components/fabric/QLine3.js +++ /dev/null @@ -1,91 +0,0 @@ -import { fabric } from 'fabric' - -export const QLine2 = fabric.util.createClass(fabric.Line, { - type: 'QLine2', - text: null, - canvas: null, - initialize: function (points, options, canvas) { - if (canvas) { - this.canvas = canvas - } - this.callSuper('initialize', points, options) - }, - - toObject: function (propertiesToInclude) { - return fabric.util.object.extend(this.callSuper('toObject', propertiesToInclude), { - type: this.type, - x1: this.x1, - y1: this.y1, - x2: this.x2, - y2: this.y2, - }) - }, - init: function () { - this.addLengthText(true) - - this.on('moving', () => { - this.addLengthText(false) - }) - - this.on('modified', (e) => { - this.addLengthText(false) - }) - - this.on('removed', () => { - if (this.text) { - this.canvas.remove(this.text) - } - - this.text = null - }) - }, - - addLengthText(isFirst) { - if (this.text) { - this.canvas.remove(this.text) - this.text = null - } - - if (isFirst && this.lengthTxt > 0) { - const text = new fabric.Textbox(this.lengthTxt.toFixed(0).toString(), { - left: (this.x1 + this.x2) / 2, - top: (this.y1 + this.y2) / 2, - fontSize: this.fontSize, - }) - this.length = this.lengthTxt - this.text = text - this.canvas.add(text) - return - } - const scaleX = this.scaleX - const scaleY = this.scaleY - const x1 = this.left - const y1 = this.top - const x2 = this.left + this.width * scaleX - const y2 = this.top + this.height * scaleY - const dx = x2 - x1 - const dy = y2 - y1 - this.length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(0)) - - const text = new fabric.Textbox(this.length.toFixed(0).toString(), { - left: (x1 + x2) / 2, - top: (y1 + y2) / 2, - fontSize: this.fontSize, - selectable: false, - }) - text.set({ name: 'lengthText' }) - this.text = text - this.canvas.add(text) - }, - setFontSize(fontSize) { - this.fontSize = fontSize - this.text.set({ fontSize }) - }, - _render: function (ctx) { - this.callSuper('_render', ctx) - this.init() - }, - _set: function (key, value) { - this.callSuper('_set', key, value) - }, -}) diff --git a/src/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js index b10c8465..699c7182 100644 --- a/src/components/fabric/QPolygon.js +++ b/src/components/fabric/QPolygon.js @@ -1,135 +1,136 @@ import { fabric } from 'fabric' -import { - calculateIntersection, - calculateIntersection2, - distanceBetweenPoints, - findClosestLineToPoint, - findTopTwoIndexesByDistance, - getDegreeByChon, - getDirectionByPoint, - getRoofHeight, - getRoofHypotenuse, - sortedPoints, -} from '@/util/canvas-util' +import { v4 as uuidv4 } from 'uuid' import { QLine } from '@/components/fabric/QLine' +import { distanceBetweenPoints, findTopTwoIndexesByDistance, getDirectionByPoint, sortedPoints } from '@/util/canvas-util' import { drawHelpLineInHexagon } from '@/util/qpolygon-utils' -export default class QPolygon extends fabric.Group { - type = 'QPolygon' - polygon - points - texts = [] - lines = [] - canvas - fontSize - qCells = [] - name - shape = 0 // 점 6개일때의 shape 모양 - helpPoints = [] - helpLines = [] - - wall - - initPoints - initOption - - constructor(points, options, canvas) { - /*if (points.length !== 4 && points.length !== 6) { - throw new Error('Points must be 4 or 6.') - }*/ - if (!options.fontSize) { - throw new Error('Font size is required.') - } - +export const QPolygon = fabric.util.createClass(fabric.Polygon, { + type: 'QPolygon', + lines: [], + texts: [], + id: null, + length: 0, + initialize: function (points, options, canvas) { // 소수점 전부 제거 points.forEach((point) => { point.x = Math.round(point.x) point.y = Math.round(point.y) }) + points = sortedPoints(points) + this.callSuper('initialize', points, options) + if (options.id) { + this.id = options.id + } else { + this.id = uuidv4() + } - const sortPoints = sortedPoints(points) - const polygon = new fabric.Polygon(sortPoints, options) - - super([polygon], { selectable: false }) - - this.fontSize = options.fontSize - this.points = sortPoints - this.polygon = polygon - this.name = options.name - - this.initPoints = points - this.initOption = options + if (canvas) { + this.canvas = canvas + } this.init() - this.addEvent() this.initLines() this.setShape() - } + }, + + setShape() { + let shape = 0 + if (this.lines.length !== 6) { + return + } + //외각선 기준 + const topIndex = findTopTwoIndexesByDistance(this.lines).sort((a, b) => a - b) //배열중에 큰 2값을 가져옴 TODO: 나중에는 인자로 받아서 다각으로 수정 해야됨 + + //일단 배열 6개 짜리 기준의 선 번호 + if (topIndex[0] === 4) { + if (topIndex[1] === 5) { + //1번 + shape = 1 + } + } else if (topIndex[0] === 1) { + //4번 + if (topIndex[1] === 2) { + shape = 4 + } + } else if (topIndex[0] === 0) { + if (topIndex[1] === 1) { + //2번 + shape = 2 + } else if (topIndex[1] === 5) { + //3번 + shape = 3 + } + } + + this.shape = shape + }, + + toObject: function (propertiesToInclude) { + return fabric.util.object.extend(this.callSuper('toObject', propertiesToInclude), { + type: this.type, + text: this.text, + }) + }, + init: function () { + this.addLengthText() + + this.on('moving', () => { + this.addLengthText() + }) + + this.on('modified', (e) => { + this.addLengthText() + }) + + this.on('selected', () => { + Object.keys(this.controls).forEach((controlKey) => { + if (controlKey !== 'ml' && controlKey !== 'mr') { + this.setControlVisible(controlKey, false) + } + }) + }) + + this.on('removed', () => { + const thisText = this.canvas.getObjects().filter((obj) => obj.name === 'lengthText' && obj.parentId === this.id) + thisText.forEach((text) => { + this.canvas.remove(text) + }) + this.texts = null + }) + }, initLines() { + this.lines = [] + this.points.forEach((point, i) => { const nextPoint = this.points[(i + 1) % this.points.length] const line = new QLine([point.x, point.y, nextPoint.x, nextPoint.y], { stroke: this.stroke, strokeWidth: this.strokeWidth, fontSize: this.fontSize, - idx: i, direction: getDirectionByPoint(point, nextPoint), + idx: i, }) - this.lines.push(line) }) - } + }, - init() { - this.addLengthText() - } - - addEvent() { - this.on('scaling', (e) => { - this.updateLengthText() - }) - - this.on('selected', function () { - // 모든 컨트롤 떼기 - - Object.keys(this.controls).forEach((controlKey) => { - if (controlKey !== 'mtr') { - this.setControlVisible(controlKey, false) - } - }) - }) - } - - setWall(wall) { - this.wall = wall - } - - setFontSize(fontSize) { - this.fontSize = fontSize - this.texts.forEach((text) => { - text.set({ fontSize }) - }) - - this.getObjects().forEach((obj) => { - if (obj.type[0] === 'Q') { - obj.setFontSize(fontSize) - } - }) - - this.canvas.add() - } + // 보조선 그리기 + drawHelpLine(chon = 4) { + drawHelpLineInHexagon(this, chon) + }, addLengthText() { - if (this.texts.length > 0) { - this.texts.forEach((text) => { - this.canvas.remove(text) - }) - this.texts = [] - } - const points = this.points + const points = this.getCurrentPoints() points.forEach((start, i) => { + const thisText = this.canvas.getObjects().find((obj) => obj.name === 'lengthText' && obj.parentId === this.id && obj.idx === i) + + if (thisText) { + thisText.set({ left: (start.x + points[(i + 1) % points.length].x) / 2, top: (start.y + points[(i + 1) % points.length].y) / 2 }) + return + } + const end = points[(i + 1) % points.length] const dx = end.x - start.x const dy = end.y - start.y @@ -143,31 +144,29 @@ export default class QPolygon extends fabric.Group { top: midPoint.y, fontSize: this.fontSize, selectable: false, + parentId: this.id, + idx: i, + name: 'lengthText', }) this.texts.push(text) this.canvas.add(text) + this.canvas.renderAll() }) - - this.canvas.renderAll() - } - - updateLengthText() { - const points = this.getCurrentPoints() - - points.forEach((start, i) => { - const end = points[(i + 1) % points.length] - const dx = end.x - start.x - const dy = end.y - start.y - const length = Math.sqrt(dx * dx + dy * dy) - - // Update the text object with the new length - this.texts[i].set({ text: length.toFixed(0) }) - }) - - this.canvas.renderAll() - } - + }, + setFontSize(fontSize) { + this.fontSize = fontSize + this.text.set({ fontSize }) + }, + _render: function (ctx) { + this.callSuper('_render', ctx) + }, + _set: function (key, value) { + this.callSuper('_set', key, value) + }, + setCanvas(canvas) { + this.canvas = canvas + }, fillCell(cell = { width: 50, height: 100, padding: 10 }) { const points = this.getCurrentPoints() let bounds @@ -207,23 +206,7 @@ export default class QPolygon extends fabric.Group { } this.canvas.renderAll() - } - - /** - * this.lines의 direction이 top 인 line의 모든 합이 bottom 인 line의 모든 합과 같은지 확인 - * this.lines의 direction이 left 인 line의 모든 합이 right 인 line의 모든 합과 같은지 확인 - * return {boolean} - */ - isValid() { - const leftLinesLengthSum = this.lines.filter((line) => line.direction === 'left').reduce((sum, line) => sum + line.length, 0) - const rightLinesLengthSum = this.lines.filter((line) => line.direction === 'right').reduce((sum, line) => sum + line.length, 0) - - const topLinesLengthSum = this.lines.filter((line) => line.direction === 'top').reduce((sum, line) => sum + line.length, 0) - const bottomLinesLengthSum = this.lines.filter((line) => line.direction === 'bottom').reduce((sum, line) => sum + line.length, 0) - - return leftLinesLengthSum === rightLinesLengthSum && topLinesLengthSum === bottomLinesLengthSum - } - + }, inPolygon(point) { const vertices = this.getCurrentPoints() let intersects = 0 @@ -253,7 +236,7 @@ export default class QPolygon extends fabric.Group { } return intersects % 2 === 1 - } + }, distanceFromEdge(point) { const vertices = this.getCurrentPoints() @@ -284,16 +267,7 @@ export default class QPolygon extends fabric.Group { } return minDistance - } - - setViewLengthText(boolean) { - this.texts.forEach((text) => { - text.visible = boolean - }) - - this.canvas.renderAll() - } - + }, getCurrentPoints() { const scaleX = this.scaleX const scaleY = this.scaleY @@ -313,625 +287,13 @@ export default class QPolygon extends fabric.Group { y: point.y * scaleY + movingY, } }) - } - - fillBackground(pattern) { - this.polygon.set({ fill: pattern }) - this.canvas.requestRenderAll() - } - - // 보조선 그리기 - drawHelpLine(chon = 4) { - drawHelpLineInHexagon(this, chon) - /*if (!this.isValid()) { - return - }*/ - - /*if (this.lines.length === 4) { - this.drawHelpLineInRect(chon) - } else if (this.lines.length === 6 || this.lines.length === 8) { - // TODO : 6각형 - drawHelpLineInHexagon2(this, chon) - } /!* else if (this.lines.length === 8) { - // TODO : 8각형 - this.drawHelpLineInOctagon(chon) - }*!/*/ - } - - /** - * 현재 점 6개만 가능 - */ - setShape() { - let shape = 0 - if (this.lines.length !== 6) { - return - } - //외각선 기준 - const topIndex = findTopTwoIndexesByDistance(this.lines).sort((a, b) => a - b) //배열중에 큰 2값을 가져옴 TODO: 나중에는 인자로 받아서 다각으로 수정 해야됨 - - //일단 배열 6개 짜리 기준의 선 번호 - if (topIndex[0] === 4) { - if (topIndex[1] === 5) { - //1번 - shape = 1 - } - } else if (topIndex[0] === 1) { - //4번 - if (topIndex[1] === 2) { - shape = 4 - } - } else if (topIndex[0] === 0) { - if (topIndex[1] === 1) { - //2번 - shape = 2 - } else if (topIndex[1] === 5) { - //3번 - shape = 3 - } - } - - this.shape = shape - } - - /** - * 현재 점 6개만 가능 - * @returns {number} - */ - getShape() { - return this.shape - } - - drawHelpLineInRect(chon) { - let type = 1 - let smallestLength = Infinity - let maxLength = 0 - - this.lines.forEach((line) => { - if (line.length < smallestLength) { - smallestLength = line.length - } - if (line.length > maxLength) { - maxLength = line.length - } + }, + setWall: function (wall) { + this.wall = wall + }, + setViewLengthText(isView) { + this.texts.forEach((text) => { + text.set({ visible: isView }) }) - - // QPolygon 객체의 모든 선들을 가져옵니다. - const lines = [...this.lines] - - // 이 선들을 길이에 따라 정렬합니다. - lines.sort((a, b) => a.length - b.length) - - // 정렬된 배열에서 가장 작은 두 선을 선택합니다. - let smallestLines - - if (smallestLength === maxLength) { - // 정사각형인 경우 0, 2번째 라인이 가장 짧은 라인 - - smallestLines = [lines[0], lines[2]] - } else { - smallestLines = lines.slice(0, 2) - } - - let needPlusLine - let needMinusLine - - const direction = smallestLines[0].direction - - if (direction === 'top' || direction === 'bottom') { - needPlusLine = smallestLines[0].x1 < smallestLines[1].x1 ? smallestLines[0] : smallestLines[1] - needMinusLine = needPlusLine === smallestLines[0] ? smallestLines[1] : smallestLines[0] - - type = 1 // 가로가 긴 사각형 - } - - if (direction === 'left' || direction === 'right') { - needPlusLine = smallestLines[0].y1 < smallestLines[1].y1 ? smallestLines[0] : smallestLines[1] - needMinusLine = needPlusLine === smallestLines[0] ? smallestLines[1] : smallestLines[0] - - type = 2 // 세로가 긴 사각형 - } - - let point1 - let point2 - - if (type === 1) { - point1 = { - x: needPlusLine.x1 + smallestLength / 2, - y: needPlusLine.y1 > needPlusLine.y2 ? needPlusLine.y1 - smallestLength / 2 : needPlusLine.y2 - smallestLength / 2, - } - - point2 = { - x: needMinusLine.x1 - smallestLength / 2, - y: needMinusLine.y1 > needMinusLine.y2 ? needMinusLine.y1 - smallestLength / 2 : needMinusLine.y2 - smallestLength / 2, - } - } else if (type === 2) { - point1 = { - x: needPlusLine.x1 > needPlusLine.x2 ? needPlusLine.x1 - smallestLength / 2 : needPlusLine.x2 - smallestLength / 2, - y: needPlusLine.y1 + smallestLength / 2, - } - - point2 = { - x: needMinusLine.x1 > needMinusLine.x2 ? needMinusLine.x1 - smallestLength / 2 : needMinusLine.x2 - smallestLength / 2, - y: needMinusLine.y1 - smallestLength / 2, - } - } - - // 빗변1 - const realLine1 = new QLine( - [needPlusLine.x1, needPlusLine.y1, point1.x, point1.y], - { fontSize: this.fontSize, stroke: 'black', strokeWidth: 1 }, - getRoofHypotenuse(smallestLength / 2), - ) - - // 빗변2 - const realLine2 = new QLine( - [needPlusLine.x2, needPlusLine.y2, point1.x, point1.y], - { fontSize: this.fontSize, stroke: 'black', strokeWidth: 1 }, - getRoofHypotenuse(smallestLength / 2), - ) - - // 빗변3 - const realLine3 = new QLine( - [needMinusLine.x1, needMinusLine.y1, point2.x, point2.y], - { fontSize: this.fontSize, stroke: 'black', strokeWidth: 1 }, - getRoofHypotenuse(smallestLength / 2), - ) - - // 빗변4 - const realLine4 = new QLine( - [needMinusLine.x2, needMinusLine.y2, point2.x, point2.y], - { fontSize: this.fontSize, stroke: 'black', strokeWidth: 1 }, - getRoofHypotenuse(smallestLength / 2), - ) - - let centerPoint1 - let centerPoint2 - - if (type === 1) { - centerPoint1 = { x: point1.x - smallestLength / 2, y: point1.y } - centerPoint2 = { x: point2.x + smallestLength / 2, y: point2.y } - } else if (type === 2) { - centerPoint1 = { x: point1.x, y: point1.y - smallestLength / 2 } - centerPoint2 = { x: point2.x, y: point2.y + smallestLength / 2 } - } - - // 옆으로 누워있는 지붕의 높이 - const realLine5 = new QLine( - [point1.x, point1.y, centerPoint1.x, centerPoint1.y], - { - fontSize: this.fontSize, - stroke: 'black', - strokeWidth: 1, - strokeDashArray: [5, 5], - }, - getRoofHeight(smallestLength / 2, getDegreeByChon(chon)), - ) - - // 옆으로 누워있는 지붕의 높이 - const realLine6 = new QLine( - [point2.x, point2.y, centerPoint2.x, centerPoint2.y], - { - fontSize: this.fontSize, - stroke: 'black', - strokeWidth: 1, - strokeDashArray: [5, 5], - }, - getRoofHeight(smallestLength / 2, getDegreeByChon(chon)), - ) - - // 용마루 - const ridge = new QLine([point1.x, point1.y, point2.x, point2.y], { - fontSize: this.fontSize, - stroke: 'black', - strokeWidth: 1, - }) - - this.canvas.add(realLine1) - this.canvas.add(realLine2) - this.canvas.add(realLine3) - this.canvas.add(realLine4) - this.canvas.add(realLine5) - this.canvas.add(realLine6) - if (smallestLength !== maxLength) { - // 정사각형이 아닌경우에만 용마루를 추가한다. - this.canvas.add(ridge) - } - } - drawHelpLineInHexagon2(chon) { - const oneSideLines = [...this.lines].map((line) => { - let newX1, newY1, newX2, newY2 - if (line.direction === 'top') { - newX1 = line.x2 - newY1 = line.y2 - newX2 = line.x1 - newY2 = line.y1 - - line.x1 = newX1 - line.y1 = newY1 - line.x2 = newX2 - line.y2 = newY2 - line.direction = 'bottom' - } else if (line.direction === 'left') { - newX1 = line.x2 - newY1 = line.y2 - newX2 = line.x1 - newY2 = line.y1 - - line.x1 = newX1 - line.y1 = newY1 - line.x2 = newX2 - line.y2 = newY2 - line.direction = 'right' - } - return line - }) - - const centerLines = [] - const helpLines = [] - const ridgeStartPoints = [] - - const horizontalLines = oneSideLines.filter((line) => line.direction === 'right') - const verticalLines = oneSideLines.filter((line) => line.direction === 'bottom') - - const horizontalMaxLength = horizontalLines.reduce((max, obj) => Math.max(max, obj.length), 0) - const verticalMaxLength = verticalLines.reduce((max, obj) => Math.max(max, obj.length), 0) - // 모든 가로선의 중심선을 긋는다. - horizontalLines.forEach((line, index) => { - const nextLine = horizontalLines[(index + 1) % horizontalLines.length] - - const startCenterX = Math.max(line.x1, nextLine.x1) - const startCenterY = (line.y1 + nextLine.y1) / 2 - - let endCenterX = line.length >= nextLine.length ? startCenterX + line.length : startCenterX + nextLine.length - const endCenterY = startCenterY - - if (endCenterX > Math.max(line.x2, nextLine.x2)) { - endCenterX = Math.max(line.x2, nextLine.x2) - } - - const centerLine = new QLine([startCenterX, startCenterY, endCenterX, endCenterY], { - fontSize: this.fontSize, - stroke: 'red', - strokeWidth: 1, - direction: 'horizontal', - }) - - // this.addWithUpdate(centerLine) - - centerLines.push(centerLine) - }) - - // 모든 세로선의 중심선을 긋는다. - verticalLines.forEach((line, index) => { - const nextLine = verticalLines[(index + 1) % verticalLines.length] - - const startCenterX = (line.x1 + nextLine.x1) / 2 - const startCenterY = Math.min(line.y1, nextLine.y1) - - const endCenterX = startCenterX - let endCenterY = line.length >= nextLine.length ? startCenterY + line.length : startCenterY + nextLine.length - - if (endCenterY > Math.max(line.y2, nextLine.y2)) { - endCenterY = Math.max(line.y2, nextLine.y2) - } - - const centerLine = new QLine([startCenterX, startCenterY, endCenterX, endCenterY], { - fontSize: this.fontSize, - stroke: 'blue', - strokeWidth: 1, - direction: 'vertical', - }) - // this.addWithUpdate(centerLine) - - centerLines.push(centerLine) - }) - - const maxLength = horizontalMaxLength < verticalMaxLength ? horizontalMaxLength : verticalMaxLength - - this.points.forEach((point, index) => { - const wallPoint = this.wall.points[index] - // 두 점의 좌표 - const x1 = point.x - const y1 = point.y - const x2 = wallPoint.x - const y2 = wallPoint.y - - let newX2, newY2 - - // x1, y1을 기준으로 x2, y2와의 거리를 유지한 새로운 직선 생성 - const angle = Math.atan2(y2 - y1, x2 - x1) - - newX2 = Math.floor(x1 + (maxLength / 2 + 50) * Math.cos(angle)) - newY2 = Math.floor(y1 + (maxLength / 2 + 50) * Math.sin(angle)) - - const line = new QLine([x1, y1, newX2, newY2], { - fontSize: this.fontSize, - stroke: 'green', - idx: index, - }) - this.addWithUpdate(line) - helpLines.push(line) - this.canvas.renderAll() - }) - - helpLines.forEach((line, index) => { - if (line.isAlreadyInterSection) { - return - } - const nextLine = helpLines[(index + 1 + helpLines.length) % helpLines.length] - this.canvas.renderAll() - - let intersectionPoint = calculateIntersection(line, nextLine) - if (!intersectionPoint) { - return - } - - line.set({ isAlreadyInterSection: true }) - nextLine.set({ isAlreadyInterSection: true }) - - const helpLine1 = new QLine([nextLine.x1, nextLine.y1, intersectionPoint.x, intersectionPoint.y], { - fontSize: this.fontSize, - stroke: 'skyblue', - }) - - const helpLine2 = new QLine([line.x1, line.y1, intersectionPoint.x, intersectionPoint.y], { - fontSize: this.fontSize, - stroke: 'skyblue', - }) - - ridgeStartPoints.push(intersectionPoint) - this.addWithUpdate(helpLine1) - this.addWithUpdate(helpLine2) - this.removeWithUpdate(nextLine) - this.removeWithUpdate(line) - this.canvas.renderAll() - }) - - // 안만나는 선들 - const notInterSectionLines = helpLines.filter((line) => !line.isAlreadyInterSection) - const ridgeEndPoints = [] - const interSectionPoints = [] - - notInterSectionLines.forEach((line, index) => { - line.line.set({ strokeWidth: (index + 1) * 5 }) - - centerLines.forEach((centerLine) => { - const interSectionPoint = calculateIntersection2(line, centerLine) - - if (!this.inPolygon(interSectionPoint) || !interSectionPoint) { - return - } - line.interSectionPoints.push(interSectionPoint) - interSectionPoints.push(interSectionPoint) - }) - }) - - ridgeStartPoints.forEach((point, index) => { - let arrivalPoint - let distance = Infinity - let startPoint - interSectionPoints.forEach((interSectionPoint) => { - if (Math.abs(point.x - interSectionPoint.x) < 3 || Math.abs(point.y - interSectionPoint.y) < 3) { - if (distanceBetweenPoints(point, interSectionPoint) < distance) { - startPoint = point - distance = distanceBetweenPoints(point, interSectionPoint) - arrivalPoint = interSectionPoint - } - } - }) - - if (arrivalPoint) { - const line = notInterSectionLines.filter((line) => line.interSectionPoints.includes(arrivalPoint))[0] - - const ridge = new QLine([startPoint.x, startPoint.y, arrivalPoint.x, arrivalPoint.y], { - stroke: 'black', - fontSize: this.fontSize, - }) - - const helpLine = new QLine([line.x1, line.y1, arrivalPoint.x, arrivalPoint.y], { - stroke: 'red', - fontSize: this.fontSize, - }) - - ridgeEndPoints.push(arrivalPoint) - this.addWithUpdate(ridge) - this.addWithUpdate(helpLine) - this.removeWithUpdate(line) - this.canvas.renderAll() - } - }) - - ridgeEndPoints.forEach((point, index) => { - const currentRidgeEndPoint = ridgeEndPoints[index] - const nextRidgeEndPoint = ridgeEndPoints[(index + 1) % ridgeEndPoints.length] - const ridgeConnectLine = new QLine([currentRidgeEndPoint.x, currentRidgeEndPoint.y, nextRidgeEndPoint.x, nextRidgeEndPoint.y], { - fontSize: this.fontSize, - stroke: 'green', - }) - - this.addWithUpdate(ridgeConnectLine) - this.canvas.renderAll() - }) - } - drawHelpLineInHexagon(chon) { - const historyLines = [] - const helpPoints = [] - const notInterSectionLines = [] - const ridge = [] - const maxLength = this.lines.reduce((max, obj) => Math.max(max, obj.length), 0) - this.points.forEach((point, index) => { - const wallPoint = this.wall.points[index] - // 두 점의 좌표 - const x1 = point.x - const y1 = point.y - const x2 = wallPoint.x - const y2 = wallPoint.y - const historyLines = [] - const helpPoints = [] - const notInterSectionLines = [] - const ridge = [] - const maxLength = this.lines.reduce((max, obj) => Math.max(max, obj.length), 0) - this.points.forEach((point, index) => { - const wallPoint = this.wall.points[index] - // 두 점의 좌표 - const x1 = point.x - const y1 = point.y - const x2 = wallPoint.x - const y2 = wallPoint.y - - let newX2, newY2 - - // x1, y1을 기준으로 x2, y2와의 거리를 유지한 새로운 직선 생성 - const angle = Math.atan2(y2 - y1, x2 - x1) - - newX2 = x1 + (maxLength / 2) * Math.cos(angle) - newY2 = y1 + (maxLength / 2) * Math.sin(angle) - - const line = new QLine([x1, y1, newX2, newY2], { - fontSize: this.fontSize, - stroke: 'blue', - idx: index, - }) - historyLines.push(line) - this.canvas.add(line) - - this.canvas.renderAll() - }) - - /** - * 삼각 지붕 - */ - historyLines.forEach((line, index) => { - const prevLine = historyLines[(index - 1 + historyLines.length) % historyLines.length] - - let intersectionPoint = calculateIntersection(line, prevLine) - - if (!intersectionPoint) { - notInterSectionLines.push(line) - return - } - - const helpLine1 = new QLine([prevLine.x1, prevLine.y1, intersectionPoint.x, intersectionPoint.y], { - fontSize: this.fontSize, - stroke: 'red', - }) - - const helpLine2 = new QLine([line.x1, line.y1, intersectionPoint.x, intersectionPoint.y], { - fontSize: this.fontSize, - stroke: 'red', - }) - notInterSectionLines.pop() - helpPoints.push(intersectionPoint) - - this.canvas.add(helpLine1) - this.canvas.add(helpLine2) - this.canvas.remove(prevLine) - this.canvas.remove(line) - this.canvas.renderAll() - }) - // 용마루 - - const ridgePoint = [] - - helpPoints.forEach((helpPoint, index) => { - const closestLine = findClosestLineToPoint(helpPoint, notInterSectionLines) - - // 가까운 선의 중심점 - const centerClosestLinePoint = { - x: (closestLine.x1 + closestLine.x2) / 2, - y: (closestLine.y1 + closestLine.y2) / 2, - } - - const direction = getDirectionByPoint(helpPoint, centerClosestLinePoint) - - let newX, newY - - switch (direction) { - case 'left': { - newX = ((closestLine.x2 - closestLine.x1) * (helpPoint.y - closestLine.y1)) / (closestLine.y2 - closestLine.y1) + closestLine.x1 - newY = helpPoint.y - break - } - case 'right': { - newX = ((closestLine.x2 - closestLine.x1) * (helpPoint.y - closestLine.y1)) / (closestLine.y2 - closestLine.y1) + closestLine.x1 - newY = helpPoint.y - break - } - case 'top': { - newX = helpPoint.x - newY = ((closestLine.y2 - closestLine.y1) * (helpPoint.x - closestLine.x1)) / (closestLine.x2 - closestLine.x1) + closestLine.y1 - break - } - case 'bottom': { - newX = helpPoint.x - newY = ((closestLine.y2 - closestLine.y1) * (helpPoint.x - closestLine.x1)) / (closestLine.x2 - closestLine.x1) + closestLine.y1 - break - } - } - - const ridgeHelpLine = new QLine([closestLine.x1, closestLine.y1, newX, newY], { - fontSize: this.fontSize, - stroke: 'purple', - strokeWidth: 5, - }) - - ridgePoint.push({ x: newX, y: newY }) - - const ridge = new QLine([helpPoint.x, helpPoint.y, newX, newY], { - fontSize: this.fontSize, - stroke: 'skyblue', - strokeWidth: 5, - }) - - this.canvas.add(ridge) - this.canvas.renderAll() - - this.canvas.add(ridgeHelpLine) - this.canvas.remove(closestLine) - this.canvas.renderAll() - }) - - // 용마루 끼리 연결 - for (let i = 0; i < ridgePoint.length; i = i + 2) { - const currentRidgeEndPoint = ridgePoint[i] - const nextRidgeEndPoint = ridgePoint[(i + 1) % ridgePoint.length] - const ridgeConnectLine = new QLine([currentRidgeEndPoint.x, currentRidgeEndPoint.y, nextRidgeEndPoint.x, nextRidgeEndPoint.y], { - fontSize: this.fontSize, - stroke: 'green', - strokeWidth: 5, - }) - this.canvas.add(ridgeConnectLine) - this.canvas.renderAll() - } - - this.canvas.renderAll() - }) - } - - drawHelpLineInOctagon(chon) {} - - getObject() { - return this - } - sou - - toObject(propertiesToInclude) { - return fabric.util.object.extend(this.callSuper('toObject'), { - points: this.points, - fontSize: this.fontSize, - name: this.name, - shape: this.shape, - texts: this.texts, - lines: this.lines, - wall: this.wall, - initPoints: this.initPoints, - initOption: this.initOption, - objects: this.getObjects().map((obj) => obj.toObject(propertiesToInclude)), - pattern: this.pattern, - }) - } - - _set(key, value) { - this.callSuper('_set', key, value) - } -} + }, +}) diff --git a/src/components/fabric/QPolygon3.js b/src/components/fabric/QPolygon3.js new file mode 100644 index 00000000..73f1b700 --- /dev/null +++ b/src/components/fabric/QPolygon3.js @@ -0,0 +1,941 @@ +/** + * 문제 없는경우 제거 + */ + +import { fabric } from 'fabric' +import { + calculateIntersection, + calculateIntersection2, + distanceBetweenPoints, + findClosestLineToPoint, + findTopTwoIndexesByDistance, + getDegreeByChon, + getDirectionByPoint, + getRoofHeight, + getRoofHypotenuse, + sortedPoints, +} from '@/util/canvas-util' +import { QLine } from '@/components/fabric/QLine' +import { drawHelpLineInHexagon } from '@/util/qpolygon-utils' + +export default class QPolygon extends fabric.Group { + type = 'QPolygon' + polygon + points + texts = [] + lines = [] + canvas + fontSize + qCells = [] + name + shape = 0 // 점 6개일때의 shape 모양 + helpPoints = [] + helpLines = [] + + wall + + initPoints + initOption + + constructor(points, options, canvas) { + /*if (points.length !== 4 && points.length !== 6) { + throw new Error('Points must be 4 or 6.') + }*/ + if (!options.fontSize) { + throw new Error('Font size is required.') + } + + // 소수점 전부 제거 + points.forEach((point) => { + point.x = Math.round(point.x) + point.y = Math.round(point.y) + }) + + const sortPoints = sortedPoints(points) + const polygon = new fabric.Polygon(sortPoints, options) + + super([polygon], {}) + + this.fontSize = options.fontSize + this.points = sortPoints + this.polygon = polygon + this.name = options.name + + this.initPoints = points + this.initOption = options + + this.init() + this.addEvent() + this.initLines() + this.setShape() + } + + initLines() { + this.points.forEach((point, i) => { + const nextPoint = this.points[(i + 1) % this.points.length] + const line = new QLine([point.x, point.y, nextPoint.x, nextPoint.y], { + stroke: this.stroke, + strokeWidth: this.strokeWidth, + fontSize: this.fontSize, + idx: i, + direction: getDirectionByPoint(point, nextPoint), + }) + + this.lines.push(line) + }) + } + + init() { + this.addLengthText() + } + + addEvent() { + this.on('scaling', (e) => { + this.updateLengthText() + }) + + this.on('selected', function () { + // 모든 컨트롤 떼기 + + Object.keys(this.controls).forEach((controlKey) => { + if (controlKey !== 'mtr') { + this.setControlVisible(controlKey, false) + } + }) + }) + } + + setWall(wall) { + this.wall = wall + } + + setFontSize(fontSize) { + this.fontSize = fontSize + this.texts.forEach((text) => { + text.set({ fontSize }) + }) + + this.getObjects().forEach((obj) => { + if (obj.type[0] === 'Q') { + obj.setFontSize(fontSize) + } + }) + + this.canvas.add() + } + + addLengthText() { + if (this.texts.length > 0) { + this.texts.forEach((text) => { + this.canvas.remove(text) + }) + this.texts = [] + } + const points = this.points + + points.forEach((start, i) => { + const end = points[(i + 1) % points.length] + const dx = end.x - start.x + const dy = end.y - start.y + const length = Math.sqrt(dx * dx + dy * dy) + + const midPoint = new fabric.Point((start.x + end.x) / 2, (start.y + end.y) / 2) + + // Create new text object if it doesn't exist + const text = new fabric.Text(length.toFixed(0), { + left: midPoint.x, + top: midPoint.y, + fontSize: this.fontSize, + selectable: false, + }) + + this.texts.push(text) + this.canvas.add(text) + }) + + this.canvas.renderAll() + } + + updateLengthText() { + const points = this.getCurrentPoints() + + points.forEach((start, i) => { + const end = points[(i + 1) % points.length] + const dx = end.x - start.x + const dy = end.y - start.y + const length = Math.sqrt(dx * dx + dy * dy) + + // Update the text object with the new length + this.texts[i].set({ text: length.toFixed(0) }) + }) + + this.canvas.renderAll() + } + + fillCell(cell = { width: 50, height: 100, padding: 10 }) { + const points = this.getCurrentPoints() + let bounds + + try { + bounds = fabric.util.makeBoundingBoxFromPoints(points) + } catch (error) { + alert('다각형의 꼭지점이 4개 이상이어야 합니다.') + return + } + + for (let x = bounds.left; x < bounds.left + bounds.width; x += cell.width + cell.padding) { + for (let y = bounds.top; y < bounds.top + bounds.height; y += cell.height + cell.padding) { + const rect = new fabric.Rect({ + left: x, + top: y, + width: cell.width, + height: cell.height, + fill: 'transparent', + stroke: 'black', + selectable: false, + }) + + const rectPoints = [ + new fabric.Point(rect.left, rect.top), + new fabric.Point(rect.left + rect.width, rect.top), + new fabric.Point(rect.left, rect.top + rect.height), + new fabric.Point(rect.left + rect.width, rect.top + rect.height), + ] + + const isInside = rectPoints.every((rectPoint) => this.inPolygon(rectPoint) && this.distanceFromEdge(rectPoint) >= cell.padding) + + if (isInside) { + this.canvas.add(rect) + } + } + } + + this.canvas.renderAll() + } + + /** + * this.lines의 direction이 top 인 line의 모든 합이 bottom 인 line의 모든 합과 같은지 확인 + * this.lines의 direction이 left 인 line의 모든 합이 right 인 line의 모든 합과 같은지 확인 + * return {boolean} + */ + isValid() { + const leftLinesLengthSum = this.lines.filter((line) => line.direction === 'left').reduce((sum, line) => sum + line.length, 0) + const rightLinesLengthSum = this.lines.filter((line) => line.direction === 'right').reduce((sum, line) => sum + line.length, 0) + + const topLinesLengthSum = this.lines.filter((line) => line.direction === 'top').reduce((sum, line) => sum + line.length, 0) + const bottomLinesLengthSum = this.lines.filter((line) => line.direction === 'bottom').reduce((sum, line) => sum + line.length, 0) + + return leftLinesLengthSum === rightLinesLengthSum && topLinesLengthSum === bottomLinesLengthSum + } + + inPolygon(point) { + const vertices = this.getCurrentPoints() + let intersects = 0 + + for (let i = 0; i < vertices.length; i++) { + let vertex1 = vertices[i] + let vertex2 = vertices[(i + 1) % vertices.length] + + if (vertex1.y > vertex2.y) { + let tmp = vertex1 + vertex1 = vertex2 + vertex2 = tmp + } + + if (point.y === vertex1.y || point.y === vertex2.y) { + point.y += 0.01 + } + + if (point.y <= vertex1.y || point.y > vertex2.y) { + continue + } + + let xInt = ((point.y - vertex1.y) * (vertex2.x - vertex1.x)) / (vertex2.y - vertex1.y) + vertex1.x + if (xInt < point.x) { + intersects++ + } + } + + return intersects % 2 === 1 + } + + distanceFromEdge(point) { + const vertices = this.getCurrentPoints() + let minDistance = Infinity + + for (let i = 0; i < vertices.length; i++) { + let vertex1 = vertices[i] + let vertex2 = vertices[(i + 1) % vertices.length] + + const dx = vertex2.x - vertex1.x + const dy = vertex2.y - vertex1.y + + const t = ((point.x - vertex1.x) * dx + (point.y - vertex1.y) * dy) / (dx * dx + dy * dy) + + let closestPoint + if (t < 0) { + closestPoint = vertex1 + } else if (t > 1) { + closestPoint = vertex2 + } else { + closestPoint = new fabric.Point(vertex1.x + t * dx, vertex1.y + t * dy) + } + + const distance = distanceBetweenPoints(point, closestPoint) + if (distance < minDistance) { + minDistance = distance + } + } + + return minDistance + } + + setViewLengthText(boolean) { + this.texts.forEach((text) => { + text.visible = boolean + }) + + this.canvas.renderAll() + } + + getCurrentPoints() { + const scaleX = this.scaleX + const scaleY = this.scaleY + + const left = this.left + const top = this.top + + // 시작점 + const point = this.points[0] + + const movingX = left - point.x * scaleX + const movingY = top - point.y * scaleY + + return this.points.map((point) => { + return { + x: point.x * scaleX + movingX, + y: point.y * scaleY + movingY, + } + }) + } + + fillBackground(pattern) { + this.polygon.set({ fill: pattern }) + this.canvas.requestRenderAll() + } + + // 보조선 그리기 + drawHelpLine(chon = 4) { + drawHelpLineInHexagon(this, chon) + /*if (!this.isValid()) { + return + }*/ + + /*if (this.lines.length === 4) { + this.drawHelpLineInRect(chon) + } else if (this.lines.length === 6 || this.lines.length === 8) { + // TODO : 6각형 + drawHelpLineInHexagon2(this, chon) + } /!* else if (this.lines.length === 8) { + // TODO : 8각형 + this.drawHelpLineInOctagon(chon) + }*!/*/ + } + + /** + * 현재 점 6개만 가능 + */ + setShape() { + let shape = 0 + if (this.lines.length !== 6) { + return + } + //외각선 기준 + const topIndex = findTopTwoIndexesByDistance(this.lines).sort((a, b) => a - b) //배열중에 큰 2값을 가져옴 TODO: 나중에는 인자로 받아서 다각으로 수정 해야됨 + + //일단 배열 6개 짜리 기준의 선 번호 + if (topIndex[0] === 4) { + if (topIndex[1] === 5) { + //1번 + shape = 1 + } + } else if (topIndex[0] === 1) { + //4번 + if (topIndex[1] === 2) { + shape = 4 + } + } else if (topIndex[0] === 0) { + if (topIndex[1] === 1) { + //2번 + shape = 2 + } else if (topIndex[1] === 5) { + //3번 + shape = 3 + } + } + + this.shape = shape + } + + /** + * 현재 점 6개만 가능 + * @returns {number} + */ + getShape() { + return this.shape + } + + drawHelpLineInRect(chon) { + let type = 1 + let smallestLength = Infinity + let maxLength = 0 + + this.lines.forEach((line) => { + if (line.length < smallestLength) { + smallestLength = line.length + } + if (line.length > maxLength) { + maxLength = line.length + } + }) + + // QPolygon 객체의 모든 선들을 가져옵니다. + const lines = [...this.lines] + + // 이 선들을 길이에 따라 정렬합니다. + lines.sort((a, b) => a.length - b.length) + + // 정렬된 배열에서 가장 작은 두 선을 선택합니다. + let smallestLines + + if (smallestLength === maxLength) { + // 정사각형인 경우 0, 2번째 라인이 가장 짧은 라인 + + smallestLines = [lines[0], lines[2]] + } else { + smallestLines = lines.slice(0, 2) + } + + let needPlusLine + let needMinusLine + + const direction = smallestLines[0].direction + + if (direction === 'top' || direction === 'bottom') { + needPlusLine = smallestLines[0].x1 < smallestLines[1].x1 ? smallestLines[0] : smallestLines[1] + needMinusLine = needPlusLine === smallestLines[0] ? smallestLines[1] : smallestLines[0] + + type = 1 // 가로가 긴 사각형 + } + + if (direction === 'left' || direction === 'right') { + needPlusLine = smallestLines[0].y1 < smallestLines[1].y1 ? smallestLines[0] : smallestLines[1] + needMinusLine = needPlusLine === smallestLines[0] ? smallestLines[1] : smallestLines[0] + + type = 2 // 세로가 긴 사각형 + } + + let point1 + let point2 + + if (type === 1) { + point1 = { + x: needPlusLine.x1 + smallestLength / 2, + y: needPlusLine.y1 > needPlusLine.y2 ? needPlusLine.y1 - smallestLength / 2 : needPlusLine.y2 - smallestLength / 2, + } + + point2 = { + x: needMinusLine.x1 - smallestLength / 2, + y: needMinusLine.y1 > needMinusLine.y2 ? needMinusLine.y1 - smallestLength / 2 : needMinusLine.y2 - smallestLength / 2, + } + } else if (type === 2) { + point1 = { + x: needPlusLine.x1 > needPlusLine.x2 ? needPlusLine.x1 - smallestLength / 2 : needPlusLine.x2 - smallestLength / 2, + y: needPlusLine.y1 + smallestLength / 2, + } + + point2 = { + x: needMinusLine.x1 > needMinusLine.x2 ? needMinusLine.x1 - smallestLength / 2 : needMinusLine.x2 - smallestLength / 2, + y: needMinusLine.y1 - smallestLength / 2, + } + } + + // 빗변1 + const realLine1 = new QLine( + [needPlusLine.x1, needPlusLine.y1, point1.x, point1.y], + { fontSize: this.fontSize, stroke: 'black', strokeWidth: 1 }, + getRoofHypotenuse(smallestLength / 2), + ) + + // 빗변2 + const realLine2 = new QLine( + [needPlusLine.x2, needPlusLine.y2, point1.x, point1.y], + { fontSize: this.fontSize, stroke: 'black', strokeWidth: 1 }, + getRoofHypotenuse(smallestLength / 2), + ) + + // 빗변3 + const realLine3 = new QLine( + [needMinusLine.x1, needMinusLine.y1, point2.x, point2.y], + { fontSize: this.fontSize, stroke: 'black', strokeWidth: 1 }, + getRoofHypotenuse(smallestLength / 2), + ) + + // 빗변4 + const realLine4 = new QLine( + [needMinusLine.x2, needMinusLine.y2, point2.x, point2.y], + { fontSize: this.fontSize, stroke: 'black', strokeWidth: 1 }, + getRoofHypotenuse(smallestLength / 2), + ) + + let centerPoint1 + let centerPoint2 + + if (type === 1) { + centerPoint1 = { x: point1.x - smallestLength / 2, y: point1.y } + centerPoint2 = { x: point2.x + smallestLength / 2, y: point2.y } + } else if (type === 2) { + centerPoint1 = { x: point1.x, y: point1.y - smallestLength / 2 } + centerPoint2 = { x: point2.x, y: point2.y + smallestLength / 2 } + } + + // 옆으로 누워있는 지붕의 높이 + const realLine5 = new QLine( + [point1.x, point1.y, centerPoint1.x, centerPoint1.y], + { + fontSize: this.fontSize, + stroke: 'black', + strokeWidth: 1, + strokeDashArray: [5, 5], + }, + getRoofHeight(smallestLength / 2, getDegreeByChon(chon)), + ) + + // 옆으로 누워있는 지붕의 높이 + const realLine6 = new QLine( + [point2.x, point2.y, centerPoint2.x, centerPoint2.y], + { + fontSize: this.fontSize, + stroke: 'black', + strokeWidth: 1, + strokeDashArray: [5, 5], + }, + getRoofHeight(smallestLength / 2, getDegreeByChon(chon)), + ) + + // 용마루 + const ridge = new QLine([point1.x, point1.y, point2.x, point2.y], { + fontSize: this.fontSize, + stroke: 'black', + strokeWidth: 1, + }) + + this.canvas.add(realLine1) + this.canvas.add(realLine2) + this.canvas.add(realLine3) + this.canvas.add(realLine4) + this.canvas.add(realLine5) + this.canvas.add(realLine6) + if (smallestLength !== maxLength) { + // 정사각형이 아닌경우에만 용마루를 추가한다. + this.canvas.add(ridge) + } + } + drawHelpLineInHexagon2(chon) { + const oneSideLines = [...this.lines].map((line) => { + let newX1, newY1, newX2, newY2 + if (line.direction === 'top') { + newX1 = line.x2 + newY1 = line.y2 + newX2 = line.x1 + newY2 = line.y1 + + line.x1 = newX1 + line.y1 = newY1 + line.x2 = newX2 + line.y2 = newY2 + line.direction = 'bottom' + } else if (line.direction === 'left') { + newX1 = line.x2 + newY1 = line.y2 + newX2 = line.x1 + newY2 = line.y1 + + line.x1 = newX1 + line.y1 = newY1 + line.x2 = newX2 + line.y2 = newY2 + line.direction = 'right' + } + return line + }) + + const centerLines = [] + const helpLines = [] + const ridgeStartPoints = [] + + const horizontalLines = oneSideLines.filter((line) => line.direction === 'right') + const verticalLines = oneSideLines.filter((line) => line.direction === 'bottom') + + const horizontalMaxLength = horizontalLines.reduce((max, obj) => Math.max(max, obj.length), 0) + const verticalMaxLength = verticalLines.reduce((max, obj) => Math.max(max, obj.length), 0) + // 모든 가로선의 중심선을 긋는다. + horizontalLines.forEach((line, index) => { + const nextLine = horizontalLines[(index + 1) % horizontalLines.length] + + const startCenterX = Math.max(line.x1, nextLine.x1) + const startCenterY = (line.y1 + nextLine.y1) / 2 + + let endCenterX = line.length >= nextLine.length ? startCenterX + line.length : startCenterX + nextLine.length + const endCenterY = startCenterY + + if (endCenterX > Math.max(line.x2, nextLine.x2)) { + endCenterX = Math.max(line.x2, nextLine.x2) + } + + const centerLine = new QLine([startCenterX, startCenterY, endCenterX, endCenterY], { + fontSize: this.fontSize, + stroke: 'red', + strokeWidth: 1, + direction: 'horizontal', + }) + + // this.addWithUpdate(centerLine) + + centerLines.push(centerLine) + }) + + // 모든 세로선의 중심선을 긋는다. + verticalLines.forEach((line, index) => { + const nextLine = verticalLines[(index + 1) % verticalLines.length] + + const startCenterX = (line.x1 + nextLine.x1) / 2 + const startCenterY = Math.min(line.y1, nextLine.y1) + + const endCenterX = startCenterX + let endCenterY = line.length >= nextLine.length ? startCenterY + line.length : startCenterY + nextLine.length + + if (endCenterY > Math.max(line.y2, nextLine.y2)) { + endCenterY = Math.max(line.y2, nextLine.y2) + } + + const centerLine = new QLine([startCenterX, startCenterY, endCenterX, endCenterY], { + fontSize: this.fontSize, + stroke: 'blue', + strokeWidth: 1, + direction: 'vertical', + }) + // this.addWithUpdate(centerLine) + + centerLines.push(centerLine) + }) + + const maxLength = horizontalMaxLength < verticalMaxLength ? horizontalMaxLength : verticalMaxLength + + this.points.forEach((point, index) => { + const wallPoint = this.wall.points[index] + // 두 점의 좌표 + const x1 = point.x + const y1 = point.y + const x2 = wallPoint.x + const y2 = wallPoint.y + + let newX2, newY2 + + // x1, y1을 기준으로 x2, y2와의 거리를 유지한 새로운 직선 생성 + const angle = Math.atan2(y2 - y1, x2 - x1) + + newX2 = Math.floor(x1 + (maxLength / 2 + 50) * Math.cos(angle)) + newY2 = Math.floor(y1 + (maxLength / 2 + 50) * Math.sin(angle)) + + const line = new QLine([x1, y1, newX2, newY2], { + fontSize: this.fontSize, + stroke: 'green', + idx: index, + }) + this.addWithUpdate(line) + helpLines.push(line) + this.canvas.renderAll() + }) + + helpLines.forEach((line, index) => { + if (line.isAlreadyInterSection) { + return + } + const nextLine = helpLines[(index + 1 + helpLines.length) % helpLines.length] + this.canvas.renderAll() + + let intersectionPoint = calculateIntersection(line, nextLine) + if (!intersectionPoint) { + return + } + + line.set({ isAlreadyInterSection: true }) + nextLine.set({ isAlreadyInterSection: true }) + + const helpLine1 = new QLine([nextLine.x1, nextLine.y1, intersectionPoint.x, intersectionPoint.y], { + fontSize: this.fontSize, + stroke: 'skyblue', + }) + + const helpLine2 = new QLine([line.x1, line.y1, intersectionPoint.x, intersectionPoint.y], { + fontSize: this.fontSize, + stroke: 'skyblue', + }) + + ridgeStartPoints.push(intersectionPoint) + this.addWithUpdate(helpLine1) + this.addWithUpdate(helpLine2) + this.removeWithUpdate(nextLine) + this.removeWithUpdate(line) + this.canvas.renderAll() + }) + + // 안만나는 선들 + const notInterSectionLines = helpLines.filter((line) => !line.isAlreadyInterSection) + const ridgeEndPoints = [] + const interSectionPoints = [] + + notInterSectionLines.forEach((line, index) => { + line.line.set({ strokeWidth: (index + 1) * 5 }) + + centerLines.forEach((centerLine) => { + const interSectionPoint = calculateIntersection2(line, centerLine) + + if (!this.inPolygon(interSectionPoint) || !interSectionPoint) { + return + } + line.interSectionPoints.push(interSectionPoint) + interSectionPoints.push(interSectionPoint) + }) + }) + + ridgeStartPoints.forEach((point, index) => { + let arrivalPoint + let distance = Infinity + let startPoint + interSectionPoints.forEach((interSectionPoint) => { + if (Math.abs(point.x - interSectionPoint.x) < 3 || Math.abs(point.y - interSectionPoint.y) < 3) { + if (distanceBetweenPoints(point, interSectionPoint) < distance) { + startPoint = point + distance = distanceBetweenPoints(point, interSectionPoint) + arrivalPoint = interSectionPoint + } + } + }) + + if (arrivalPoint) { + const line = notInterSectionLines.filter((line) => line.interSectionPoints.includes(arrivalPoint))[0] + + const ridge = new QLine([startPoint.x, startPoint.y, arrivalPoint.x, arrivalPoint.y], { + stroke: 'black', + fontSize: this.fontSize, + }) + + const helpLine = new QLine([line.x1, line.y1, arrivalPoint.x, arrivalPoint.y], { + stroke: 'red', + fontSize: this.fontSize, + }) + + ridgeEndPoints.push(arrivalPoint) + this.addWithUpdate(ridge) + this.addWithUpdate(helpLine) + this.removeWithUpdate(line) + this.canvas.renderAll() + } + }) + + ridgeEndPoints.forEach((point, index) => { + const currentRidgeEndPoint = ridgeEndPoints[index] + const nextRidgeEndPoint = ridgeEndPoints[(index + 1) % ridgeEndPoints.length] + const ridgeConnectLine = new QLine([currentRidgeEndPoint.x, currentRidgeEndPoint.y, nextRidgeEndPoint.x, nextRidgeEndPoint.y], { + fontSize: this.fontSize, + stroke: 'green', + }) + + this.addWithUpdate(ridgeConnectLine) + this.canvas.renderAll() + }) + } + drawHelpLineInHexagon(chon) { + const historyLines = [] + const helpPoints = [] + const notInterSectionLines = [] + const ridge = [] + const maxLength = this.lines.reduce((max, obj) => Math.max(max, obj.length), 0) + this.points.forEach((point, index) => { + const wallPoint = this.wall.points[index] + // 두 점의 좌표 + const x1 = point.x + const y1 = point.y + const x2 = wallPoint.x + const y2 = wallPoint.y + const historyLines = [] + const helpPoints = [] + const notInterSectionLines = [] + const ridge = [] + const maxLength = this.lines.reduce((max, obj) => Math.max(max, obj.length), 0) + this.points.forEach((point, index) => { + const wallPoint = this.wall.points[index] + // 두 점의 좌표 + const x1 = point.x + const y1 = point.y + const x2 = wallPoint.x + const y2 = wallPoint.y + + let newX2, newY2 + + // x1, y1을 기준으로 x2, y2와의 거리를 유지한 새로운 직선 생성 + const angle = Math.atan2(y2 - y1, x2 - x1) + + newX2 = x1 + (maxLength / 2) * Math.cos(angle) + newY2 = y1 + (maxLength / 2) * Math.sin(angle) + + const line = new QLine([x1, y1, newX2, newY2], { + fontSize: this.fontSize, + stroke: 'blue', + idx: index, + }) + historyLines.push(line) + this.canvas.add(line) + + this.canvas.renderAll() + }) + + /** + * 삼각 지붕 + */ + historyLines.forEach((line, index) => { + const prevLine = historyLines[(index - 1 + historyLines.length) % historyLines.length] + + let intersectionPoint = calculateIntersection(line, prevLine) + + if (!intersectionPoint) { + notInterSectionLines.push(line) + return + } + + const helpLine1 = new QLine([prevLine.x1, prevLine.y1, intersectionPoint.x, intersectionPoint.y], { + fontSize: this.fontSize, + stroke: 'red', + }) + + const helpLine2 = new QLine([line.x1, line.y1, intersectionPoint.x, intersectionPoint.y], { + fontSize: this.fontSize, + stroke: 'red', + }) + notInterSectionLines.pop() + helpPoints.push(intersectionPoint) + + this.canvas.add(helpLine1) + this.canvas.add(helpLine2) + this.canvas.remove(prevLine) + this.canvas.remove(line) + this.canvas.renderAll() + }) + // 용마루 + + const ridgePoint = [] + + helpPoints.forEach((helpPoint, index) => { + const closestLine = findClosestLineToPoint(helpPoint, notInterSectionLines) + + // 가까운 선의 중심점 + const centerClosestLinePoint = { + x: (closestLine.x1 + closestLine.x2) / 2, + y: (closestLine.y1 + closestLine.y2) / 2, + } + + const direction = getDirectionByPoint(helpPoint, centerClosestLinePoint) + + let newX, newY + + switch (direction) { + case 'left': { + newX = ((closestLine.x2 - closestLine.x1) * (helpPoint.y - closestLine.y1)) / (closestLine.y2 - closestLine.y1) + closestLine.x1 + newY = helpPoint.y + break + } + case 'right': { + newX = ((closestLine.x2 - closestLine.x1) * (helpPoint.y - closestLine.y1)) / (closestLine.y2 - closestLine.y1) + closestLine.x1 + newY = helpPoint.y + break + } + case 'top': { + newX = helpPoint.x + newY = ((closestLine.y2 - closestLine.y1) * (helpPoint.x - closestLine.x1)) / (closestLine.x2 - closestLine.x1) + closestLine.y1 + break + } + case 'bottom': { + newX = helpPoint.x + newY = ((closestLine.y2 - closestLine.y1) * (helpPoint.x - closestLine.x1)) / (closestLine.x2 - closestLine.x1) + closestLine.y1 + break + } + } + + const ridgeHelpLine = new QLine([closestLine.x1, closestLine.y1, newX, newY], { + fontSize: this.fontSize, + stroke: 'purple', + strokeWidth: 5, + }) + + ridgePoint.push({ x: newX, y: newY }) + + const ridge = new QLine([helpPoint.x, helpPoint.y, newX, newY], { + fontSize: this.fontSize, + stroke: 'skyblue', + strokeWidth: 5, + }) + + this.canvas.add(ridge) + this.canvas.renderAll() + + this.canvas.add(ridgeHelpLine) + this.canvas.remove(closestLine) + this.canvas.renderAll() + }) + + // 용마루 끼리 연결 + for (let i = 0; i < ridgePoint.length; i = i + 2) { + const currentRidgeEndPoint = ridgePoint[i] + const nextRidgeEndPoint = ridgePoint[(i + 1) % ridgePoint.length] + const ridgeConnectLine = new QLine([currentRidgeEndPoint.x, currentRidgeEndPoint.y, nextRidgeEndPoint.x, nextRidgeEndPoint.y], { + fontSize: this.fontSize, + stroke: 'green', + strokeWidth: 5, + }) + this.canvas.add(ridgeConnectLine) + this.canvas.renderAll() + } + + this.canvas.renderAll() + }) + } + + drawHelpLineInOctagon(chon) {} + + getObject() { + return this + } + sou + + toObject(propertiesToInclude) { + return fabric.util.object.extend(this.callSuper('toObject'), { + points: this.points, + fontSize: this.fontSize, + name: this.name, + shape: this.shape, + texts: this.texts, + lines: this.lines, + wall: this.wall, + initPoints: this.initPoints, + initOption: this.initOption, + objects: this.getObjects().map((obj) => obj.toObject(propertiesToInclude)), + pattern: this.pattern, + }) + } + + _set(key, value) { + this.callSuper('_set', key, value) + } +} diff --git a/src/hooks/useCanvas.js b/src/hooks/useCanvas.js index 2ffdf9f9..f3b3901e 100644 --- a/src/hooks/useCanvas.js +++ b/src/hooks/useCanvas.js @@ -4,9 +4,9 @@ import { actionHandler, anchorWrapper, polygonPositionHandler } from '@/util/can import { useRecoilState } from 'recoil' import { canvasSizeState, fontSizeState } from '@/store/canvasAtom' -import QPolygon from '@/components/fabric/QPolygon' import { QLine } from '@/components/fabric/QLine' import QRect from '@/components/fabric/QRect' +import { QPolygon } from '@/components/fabric/QPolygon' export function useCanvas(id) { const [canvas, setCanvas] = useState() @@ -111,38 +111,30 @@ export function useCanvas(id) { fabric.Object.prototype.cornerStrokeColor = '#2BEBC8' fabric.Object.prototype.cornerSize = 6 fabric.QLine = QLine + fabric.QPolygon = QPolygon QPolygon.prototype.canvas = canvas QLine.prototype.canvas = canvas QRect.prototype.canvas = canvas - QLine.fromObject = function (object, callback) { - const { x1, x2, y1, y2 } = object - const instance = new QLine([x1, y1, x2, y2], object, canvas) - canvas?.add(instance) - } + fabric.QLine.fromObject = function (object, callback) { + function _callback(instance) { + delete instance.points + callback && callback(instance) + } + const options = fabric.util.object.clone(object, true) + options.points = [object.x1, object.y1, object.x2, object.y2] - // fabric.QLine.fromObject = fabric.Line.fromObject + fabric.Object._fromObject('QLine', options, _callback, 'points') + } // CustomLine.prototype.canvas = canvas // fabric.QLine = fabric.util.createClass(fabric.Group, {}) - fabric.QPolygon = fabric.util.createClass(fabric.Group, {}) + // Register the custom class with fabric - // fromObject 메서드를 QLine 클래스에 직접 추가 - fabric.QLine.fromObject = function (object, callback) { - const { initOption, initPoints, initLengthTxt } = object - fabric.util.enlivenObjects(object.objects, function (enlivenedObjects) { - return callback(new QLine(initPoints, initOption, initLengthTxt)) - }) - } - - // fromObject 메서드를 QLine 클래스에 직접 추가 fabric.QPolygon.fromObject = function (object, callback) { - const { initOption, initPoints, initLengthTxt } = object - fabric.util.enlivenObjects(object.objects, function (enlivenedObjects) { - return callback(new QPolygon(initPoints, initOption, initLengthTxt)) - }) + fabric.Object._fromObject('QPolygon', object, callback, 'points') } } @@ -567,42 +559,19 @@ export function useCanvas(id) { const addCanvas = () => { // const canvasState = canvas - const objs = canvas + const objs = canvas?.toJSON(['selectable', 'name', 'parentId', 'id', 'length', 'idx', 'direction', 'lines', 'points']) - console.log(objs) const str = JSON.stringify(objs) canvas?.clear() - console.log(str) - console.log(JSON.parse(str)) - - // 역직렬화하여 캔버스에 객체를 다시 추가합니다. setTimeout(() => { - canvas?.loadFromJSON( - JSON.parse(str), - function () { - // 모든 객체가 로드되고 캔버스에 추가된 후 호출됩니다. - canvas?.renderAll() // 캔버스를 다시 그립니다. - console.log('Objects are reloaded and rendered on canvas.') - }, - function (o, object) { - // 각 객체가 로드될 때마다 호출됩니다. - // canvas?.add(object) - canvas?.renderAll() - }, - ) + // 역직렬화하여 캔버스에 객체를 다시 추가합니다. + canvas?.loadFromJSON(JSON.parse(str), function () { + // 모든 객체가 로드되고 캔버스에 추가된 후 호출됩니다. + canvas?.renderAll() // 캔버스를 다시 그립니다. + }) }, 1000) - - /*canvas?.loadFromJSON(JSON.parse(str), () => { - console.log('load done') - })*/ - - /*const stateArr = canvasState.map((state) => state.getObject().getObjects()) - - const newCanvasList = [...canvasList, JSON.stringify(stateArr)] - - setCanvasList(newCanvasList)*/ } const changeCanvas = (idx) => { diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index 18a7a574..b899095d 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -1,12 +1,12 @@ import { useEffect, useRef, useState } from 'react' import QRect from '@/components/fabric/QRect' -import QPolygon from '@/components/fabric/QPolygon' import { findTopTwoIndexesByDistance, getCenterPoint, getDirection, getStartIndex, rearrangeArray } from '@/util/canvas-util' import { useRecoilState } from 'recoil' import { canvasSizeState, fontSizeState, roofPolygonPatternArrayState, roofState, sortedPolygonArray, wallState } from '@/store/canvasAtom' import { QLine } from '@/components/fabric/QLine' import { fabric } from 'fabric' +import { QPolygon } from '@/components/fabric/QPolygon' export const Mode = { DRAW_LINE: 'drawLine', // 기준선 긋기모드 diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index d09b8f21..b3f9529b 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -1,5 +1,5 @@ import { fabric } from 'fabric' -import QPolygon from '@/components/fabric/QPolygon' +import QPolygon from '@/components/fabric/QPolygon3' import { QLine } from '@/components/fabric/QLine' import { calculateDistance, @@ -39,7 +39,6 @@ export const drawHelpLineInHexagon = (polygon, chon) => { let maxLength = 0 polygon.lines = polygon.lines.sort((a, b) => a.length - b.length) - polygon.wall.lines = getOneSideLines(polygon.wall) // 짧은 라인 순서대로 삼각 지붕을 그린다. @@ -59,7 +58,7 @@ export const drawHelpLineInHexagon = (polygon, chon) => { const angle1 = Math.atan2(wallLine.y1 - line.y1, wallLine.x1 - line.x1) const angle2 = Math.atan2(wallLine.y2 - line.y2, wallLine.x2 - line.x2) - const helpLineLength = Math.min(line.length) + const helpLineLength = line.length * 2 const firstX2 = Math.floor(line.x1 + helpLineLength * Math.cos(angle1)) const firstY2 = Math.floor(line.y1 + helpLineLength * Math.sin(angle1)) @@ -69,10 +68,12 @@ export const drawHelpLineInHexagon = (polygon, chon) => { const firstHelpLine = new QLine([line.x1, line.y1, firstX2, firstY2], { fontSize: polygon.fontSize, + stroke: 'skyblue', }) const secondHelpLine = new QLine([line.x2, line.y2, secondX2, secondY2], { fontSize: polygon.fontSize, + stroke: 'skyblue', }) const interSectionPoint = calculateIntersection2(firstHelpLine, secondHelpLine) @@ -262,8 +263,8 @@ export const drawCenterLines = (polygon) => { horizontalLines.forEach((line, index) => { const nextLine = horizontalLines[(index + 1) % horizontalLines.length] - line.line.set({ strokeWidth: 5 }) - nextLine.line.set({ strokeWidth: 5 }) + line.set({ strokeWidth: 5 }) + nextLine.set({ strokeWidth: 5 }) polygon.canvas.renderAll() From a614438b272d726bf7aa27b3980684cff4ed9dc1 Mon Sep 17 00:00:00 2001 From: yjnoh Date: Tue, 30 Jul 2024 15:27:16 +0900 Subject: [PATCH 13/27] =?UTF-8?q?=ED=85=9C=ED=94=8C=EB=A6=BF=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20recoil=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 | 3 +++ src/store/canvasAtom.js | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/src/components/Roof2.jsx b/src/components/Roof2.jsx index de3ae06c..5582d214 100644 --- a/src/components/Roof2.jsx +++ b/src/components/Roof2.jsx @@ -385,6 +385,9 @@ export default function Roof2() { + diff --git a/src/store/canvasAtom.js b/src/store/canvasAtom.js index 91df31ed..a5195f83 100644 --- a/src/store/canvasAtom.js +++ b/src/store/canvasAtom.js @@ -41,3 +41,9 @@ export const roofPolygonPatternArrayState = atom({ default: {}, //object ex) big, mid, sht = {point : [{x1, y1}, {x2, y1}], direction : left or right or top or bottom} dangerouslyAllowMutability: true, }) + +export const templateTypeState = atom({ + key: 'templateType', + default: 1, //1:모임지붕, 2:A타입, 3:B타입 + dangerouslyAllowMutability: true, +}) From 819ba5c8ca0ba0695fde11e8ffd430fe8fd0a145 Mon Sep 17 00:00:00 2001 From: yjnoh Date: Tue, 30 Jul 2024 16:35:00 +0900 Subject: [PATCH 14/27] test --- src/components/Roof2.jsx | 8 ++++-- src/hooks/useMode.js | 54 +++++++++++++++++++++++++++++++++------- src/store/canvasAtom.js | 6 +++++ 3 files changed, 57 insertions(+), 11 deletions(-) diff --git a/src/components/Roof2.jsx b/src/components/Roof2.jsx index 5582d214..473ca435 100644 --- a/src/components/Roof2.jsx +++ b/src/components/Roof2.jsx @@ -385,8 +385,12 @@ export default function Roof2() { -