diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index 3014e43e..ac1f46f7 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -2389,7 +2389,7 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { innerLines.push(drawHipLine(prevHipPoint, canvas, roof, textMode, null, prevDegree, prevDegree)) innerLines.push(drawHipLine(nextHipPoint, canvas, roof, textMode, null, nextDegree, nextDegree)) - innerLines.push(drawRoofLine(gablePoint, canvas, roof, textMode)) + // innerLines.push(drawRoofLine(gablePoint, canvas, roof, textMode)) //양옆이 처마일경우 두개의 선, 아닐때 한개의 선, 좌우가 처마가 아닐때 안그려져야하는데 기존에 그려지는 경우가 있음 이유를 알 수 없음. if (prevLine.attributes.type === LINE_TYPE.WALLLINE.EAVES && nextLine.attributes.type === LINE_TYPE.WALLLINE.EAVES) { @@ -2499,8 +2499,8 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { const currentDegree = getDegreeByChon(currentLine.attributes.pitch) const checkVector = getHalfAngleVector(currentLine, prevLine) const checkPoint = { - x: currentLine.x1 + (checkVector.x * analyze.length) / 2, - y: currentLine.y1 + (checkVector.y * analyze.length) / 2, + x: currentLine.x1 + (checkVector.x * 10) / 2, + y: currentLine.y1 + (checkVector.y * 10) / 2, } let hipVector @@ -2558,74 +2558,6 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { degree: currentDegree, }) }) - - /*//추녀마루 포인트중 겹치는 라인을 찾아 조정한다. - eavesHipLines.forEach((eavesHipLine) => { - const { hipPoint, line1, line2, degree } = eavesHipLine - const checkLine = new fabric.Line(hipPoint, { - stroke: 'blue', - strokeWidth: 2, - parentId: roofId, - name: 'check', - }) - canvas.add(checkLine) - canvas.renderAll() - - const hipEdge = { vertex1: { x: hipPoint[0], y: hipPoint[1] }, vertex2: { x: hipPoint[2], y: hipPoint[3] } } - const intersectPoints = [] - eavesHipLines - .filter((line) => eavesHipLine !== line) - .forEach((hip) => { - const checkPoint = hip.hipPoint - const checkLine1 = hip.line1 - const checkLine2 = hip.line2 - const checkEdge = { vertex1: { x: checkPoint[0], y: checkPoint[1] }, vertex2: { x: checkPoint[2], y: checkPoint[3] } } - const intersect = edgesIntersection(checkEdge, hipEdge) - const checkLine = new fabric.Line(checkPoint, { - stroke: 'green', - strokeWidth: 2, - parentId: roofId, - name: 'check', - }) - canvas.add(checkLine) - canvas.renderAll() - - // 기준선내 겹치는 경우, line1, line2 중에서 비교선의 line1, line2가 겹칠때만 겹쳐질 수 있는 라인으로 판단한다. (샤프논문 참조) - if ( - intersect && - isPointOnLineNew({ x1: hipPoint[0], y1: hipPoint[1], x2: hipPoint[2], y2: hipPoint[3] }, intersect) && - isPointOnLineNew({ x1: checkPoint[0], y1: checkPoint[1], x2: checkPoint[2], y2: checkPoint[3] }, intersect) && - (line1 === checkLine1 || line1 === checkLine2 || line2 === checkLine1 || line2 === checkLine2) - ) { - const dx = hipPoint[0] - intersect.x - const dy = hipPoint[1] - intersect.y - const length = Math.sqrt(dx * dx + dy * dy) - intersectPoints.push({ intersect, hip, length }) - } - }) - intersectPoints.sort((a, b) => a.length - b.length) - if (intersectPoints.length === 0) { - innerLines.push(drawHipLine(hipPoint, canvas, roof, textMode, null, degree, degree)) - } else { - const intersect = intersectPoints[0].intersect - const linePoint = [hipPoint[0], hipPoint[1], intersect.x, intersect.y] - innerLines.push(drawHipLine(linePoint, canvas, roof, textMode, null, degree, degree)) - - //다른라인에서 사용 되지 않게 조정 - eavesHipLines.find((line) => line === eavesHipLine).hipPoint = linePoint - eavesHipLines.find((line) => line === intersectPoints[0].hip).hipPoint = [ - intersectPoints[0].hip.hipPoint[0], - intersectPoints[0].hip.hipPoint[1], - intersect.x, - intersect.y, - ] - } - canvas - .getObjects() - .filter((object) => object.name === 'check') - .forEach((object) => canvas.remove(object)) - canvas.renderAll() - })*/ //4. 반절처(반절마루) 판단, 반절마루는 양옆이 처마여야 한다. //5. 케라바 판단, 케라바는 양옆이 처마여야 한다. @@ -2637,236 +2569,329 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { while (linesAnalysis.length > 0 && iterations < MAX_ITERATIONS) { iterations++ + linesAnalysis.forEach((line) => { + const checkLine = new fabric.Line([line.start.x, line.start.y, line.end.x, line.end.y], { + stroke: 'cyan', + strokeWidth: 3, + parentId: roofId, + name: 'checkAnalysis', + }) + canvas.add(checkLine) + canvas.renderAll() + }) + // 각 가선분의 최단 교점 찾기 const intersections = [] for (let i = 0; i < linesAnalysis.length; i++) { let minDistance = Infinity //최단거리 let intersectPoint = null //교점 좌표 - let intersectIndex = [] //교점 선분의 index + let partners = new Set() //교점 선분의 index for (let j = 0; j < linesAnalysis.length; j++) { if (i === j) continue - - const intersection = lineIntersection(linesAnalysis[i].start, linesAnalysis[i].end, linesAnalysis[j].start, linesAnalysis[j].end) + const intersection = lineIntersection(linesAnalysis[i].start, linesAnalysis[i].end, linesAnalysis[j].start, linesAnalysis[j].end, canvas) if (intersection) { const distance = Math.sqrt((intersection.x - linesAnalysis[i].start.x) ** 2 + (intersection.y - linesAnalysis[i].start.y) ** 2) - if (distance > EPSILON) { - if (distance < minDistance && !almostEqual(distance, minDistance)) { - // console.log('intersection :', intersection, intersectPoint) - // console.log('distance :', distance, minDistance, almostEqual(distance, minDistance)) - minDistance = distance - intersectPoint = intersection - intersectIndex = [j] - } else if (almostEqual(distance, minDistance)) { - intersectIndex.push(j) - } + if (distance < minDistance && !almostEqual(distance, minDistance)) { + minDistance = distance + intersectPoint = intersection + partners = new Set([j]) + } else if (almostEqual(distance, minDistance)) { + partners.add(j) } } } if (intersectPoint) { - intersections.push({ index: i, intersectPoint, intersectIndex, distance: minDistance }) + intersections.push({ index: i, intersectPoint, partners, distance: minDistance }) } } + // 제일 가까운 교점부터 처리 + intersections.sort((a, b) => a.distance - b.distance) + // 교점에 대한 적합 여부 판단 및 처리. let newAnalysis = [] //신규 발생 선 const processed = new Set() //처리된 선 - console.log('intersections : ', intersections) - for (const { index, intersectPoint, intersectIndex } of intersections) { - const check1 = linesAnalysis[index] - const check2 = linesAnalysis[intersectIndex] - const checkLine1 = new fabric.Line([check1.start.x, check1.start.y, check1.end.x, check1.end.y], { - stroke: 'red', - strokeWidth: 2, - parentId: roofId, - name: 'check', - }) - const checkLine2 = new fabric.Line([check2.start.x, check2.start.y, check2.end.x, check2.end.y], { - stroke: 'green', - strokeWidth: 2, - parentId: roofId, - name: 'check', - }) - const checkCircle1 = new fabric.Circle({ left: check1.start.x, top: check1.start.y, radius: 5, fill: 'red', parentId: roofId, name: 'check' }) - const checkCircle2 = new fabric.Circle({ left: check2.start.x, top: check2.start.y, radius: 5, fill: 'red', parentId: roofId, name: 'check' }) - const checkCircle3 = new fabric.Circle({ left: check1.end.x, top: check1.end.y, radius: 5, fill: 'green', parentId: roofId, name: 'check' }) - const checkCircle4 = new fabric.Circle({ left: check2.end.x, top: check2.end.y, radius: 5, fill: 'green', parentId: roofId, name: 'check' }) - canvas.add(checkCircle1, checkCircle2, checkCircle3, checkCircle4) - canvas.add(checkLine1, checkLine2) - canvas.renderAll() - - //교점이 없거나, 이미 처리된 선분이면 처리하지 않는다. - if (!intersectPoint || processed.has(index)) continue - - const partner = intersections.find((p) => p.index === intersectIndex) - //교점이 없거나, 교점 선분이 없으면 처리하지 않는다. - if (!partner || !partner.intersectPoint) continue - - //상호 최단 교점 여부 확인. - if (partner.intersectIndex === index) { - const line1 = linesAnalysis[index] - const line2 = linesAnalysis[intersectIndex] - - //좌,우 선 중 공통 선 존재 확인. - const isSameLine = line1.left === line2.left || line1.left === line2.right || line1.right === line2.left || line1.right === line2.right - - if (isSameLine) { - const point1 = [line1.start.x, line1.start.y, intersectPoint.x, intersectPoint.y] - const point2 = [line2.start.x, line2.start.y, intersectPoint.x, intersectPoint.y] - - if (line1.type === 'hip') { - innerLines.push(drawHipLine(point1, canvas, roof, textMode, null, line1.degree, line1.degree)) - } else if (line1.type === 'ridge') { - innerLines.push(drawRidgeLine(point1, canvas, roof, textMode)) - } else if (line1.type === 'new') { - const isDiagonal = Math.abs(point1[0] - point1[2]) >= 1 && Math.abs(point1[1] - point1[3]) >= 1 - if (isDiagonal) { - innerLines.push(drawHipLine(point1, canvas, roof, textMode, null, line1.degree, line1.degree)) - } else { - innerLines.push(drawRidgeLine(point1, canvas, roof, textMode)) - } - } - - if (line2.type === 'hip') { - innerLines.push(drawHipLine(point2, canvas, roof, textMode, null, line2.degree, line2.degree)) - } else if (line2.type === 'ridge') { - innerLines.push(drawRidgeLine(point2, canvas, roof, textMode)) - } else if (line2.type === 'new') { - const isDiagonal = Math.abs(point2[0] - point2[2]) >= 1 && Math.abs(point2[1] - point2[3]) >= 1 - if (isDiagonal) { - innerLines.push(drawHipLine(point2, canvas, roof, textMode, null, line2.degree, line2.degree)) - } else { - innerLines.push(drawRidgeLine(point2, canvas, roof, textMode)) - } - } - - // 연결점에서 새로운 가선분을 생성 - const relationBaseLines = [line1.left, line1.right, line2.left, line2.right] - const uniqueBaseLines = [...new Set(relationBaseLines)] - - if (uniqueBaseLines.length === 3) { - const linesCounts = new Map() - relationBaseLines.forEach((e) => { - linesCounts.set(e, (linesCounts.get(e) || 0) + 1) - }) - - const uniqueLines = Array.from(linesCounts.entries()) - .filter(([_, count]) => count === 1) - .map(([line, _]) => line) - - if (uniqueLines.length === 2) { - // 두 변의 이등분선 방향 계산 - uniqueLines.sort((a, b) => a - b) - console.log('uniqueLines : ', uniqueLines) - const baseLine1 = baseLines[uniqueLines[0]] - const baseLine2 = baseLines[uniqueLines[1]] - - const checkLine1 = new fabric.Line([baseLine1.x1, baseLine1.y1, baseLine1.x2, baseLine1.y2], { - stroke: 'green', - strokeWidth: 4, - parentId: roofId, - name: 'check', - }) - const checkLine2 = new fabric.Line([baseLine2.x1, baseLine2.y1, baseLine2.x2, baseLine2.y2], { - stroke: 'blue', - strokeWidth: 4, - parentId: roofId, - name: 'check', - }) - canvas.add(checkLine1, checkLine2) - canvas.renderAll() - let bisector - console.log('isParallel(baseLine1, baseLine2)', isParallel(baseLine1, baseLine2)) - if (isParallel(baseLine1, baseLine2)) { - bisector = getBisectLines( - { x1: line1.start.x, x2: line1.end.x, y1: line1.start.y, y2: line1.end.y }, - { x1: line2.start.x, y1: line2.start.y, x2: line2.end.x, y2: line2.end.y }, - ) - } else { - //이등분선 - bisector = getBisectBaseLines(baseLine1, baseLine2, intersectPoint, canvas) - } - - //마주하는 지붕선을 찾는다. - const intersectionsByRoof = [] - const checkEdge = { - vertex1: { x: intersectPoint.x, y: intersectPoint.y }, - vertex2: { x: intersectPoint.x + bisector.x, y: intersectPoint.y + bisector.y }, - } - const checkVector = { x: Math.sign(checkEdge.vertex1.x - checkEdge.vertex2.x), y: Math.sign(checkEdge.vertex1.y - checkEdge.vertex2.y) } - roof.lines.forEach((line) => { - const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } - const is = edgesIntersection(lineEdge, checkEdge) - if (is && isPointOnLineNew(line, is)) { - const distance = Math.sqrt((is.x - intersectPoint.x) ** 2 + (is.y - intersectPoint.y) ** 2) - const isVector = { x: Math.sign(intersectPoint.x - is.x), y: Math.sign(intersectPoint.y - is.y) } - if (isVector.x === checkVector.x && isVector.y === checkVector.y) { - intersectionsByRoof.push({ is, distance }) - } - } - }) - intersectionsByRoof.sort((a, b) => a.distance - b.distance) - //지붕 선과의 교점이 존재 할때 - if (intersectionsByRoof.length > 0) { - const is = intersectionsByRoof[0].is - const checkNewLine = new fabric.Line([intersectPoint.x, intersectPoint.y, is.x, is.y], { - stroke: 'cyan', - strokeWidth: 4, - parentId: roofId, - name: 'check', - }) - canvas.add(checkNewLine) - canvas.renderAll() - newAnalysis.push({ - start: { x: intersectPoint.x, y: intersectPoint.y }, - end: { x: is.x, y: is.y }, - left: uniqueLines[0], - right: uniqueLines[1], - type: 'new', - degree: line1.degree, - }) - } - } - canvas - .getObjects() - .filter((object) => object.name === 'check') - .forEach((object) => canvas.remove(object)) - canvas.renderAll() - } - - processed.add(index) - processed.add(intersectIndex) - } - } + for (const { index, intersectPoint, partners } of intersections) { canvas .getObjects() .filter((object) => object.name === 'check') .forEach((object) => canvas.remove(object)) canvas.renderAll() + let isProceed = false + for (const pIndex of partners) { + console.log('pIndex : ', pIndex) + const check1 = linesAnalysis[index] + const checkLine1 = new fabric.Line([check1.start.x, check1.start.y, check1.end.x, check1.end.y], { + stroke: 'red', + strokeWidth: 2, + parentId: roofId, + name: 'check', + }) + const checkCircle1 = new fabric.Circle({ left: check1.start.x, top: check1.start.y, radius: 5, fill: 'red', parentId: roofId, name: 'check' }) + const checkCircle2 = new fabric.Circle({ left: check1.end.x, top: check1.end.y, radius: 5, fill: 'green', parentId: roofId, name: 'check' }) + canvas.add(checkLine1, checkCircle1, checkCircle2) + canvas.renderAll() + + const check2 = linesAnalysis[pIndex] + const checkLine2 = new fabric.Line([check2.start.x, check2.start.y, check2.end.x, check2.end.y], { + stroke: 'green', + strokeWidth: 2, + parentId: roofId, + name: 'check', + }) + + const checkCircle3 = new fabric.Circle({ left: check2.start.x, top: check2.start.y, radius: 5, fill: 'red', parentId: roofId, name: 'check' }) + const checkCircle4 = new fabric.Circle({ left: check2.end.x, top: check2.end.y, radius: 5, fill: 'green', parentId: roofId, name: 'check' }) + canvas.add(checkLine2, checkCircle3, checkCircle4) + canvas.renderAll() + + console.log('!intersectPoint || processed.has(index)', !intersectPoint, processed.has(index)) + //교점이 없거나, 이미 처리된 선분이면 처리하지 않는다. + if (!intersectPoint || processed.has(index)) continue + + const partner = intersections.find((p) => p.index === pIndex) + //교점이 없거나, 교점 선분이 없으면 처리하지 않는다. + if (!partner || !partner.intersectPoint) continue + + //상호 최단 교점 여부 확인. + if (partner.partners.has(index)) { + const line1 = linesAnalysis[index] + const line2 = linesAnalysis[pIndex] + + //좌,우 선 중 공통 선 존재 확인. + const isSameLine = line1.left === line2.left || line1.left === line2.right || line1.right === line2.left || line1.right === line2.right + if (isSameLine) { + // 현재 선이 처리 되었음을 표기 + isProceed = true + const point1 = [line1.start.x, line1.start.y, intersectPoint.x, intersectPoint.y] + const point2 = [line2.start.x, line2.start.y, intersectPoint.x, intersectPoint.y] + const length1 = Math.sqrt((point1[2] - point1[0]) ** 2 + (point1[3] - point1[1]) ** 2) + const length2 = Math.sqrt((point2[2] - point2[0]) ** 2 + (point2[3] - point2[1]) ** 2) + + if (length1 > 0 && !alreadyPoints(innerLines, point1)) { + if (line1.type === 'hip') { + innerLines.push(drawHipLine(point1, canvas, roof, textMode, null, line1.degree, line1.degree)) + } else if (line1.type === 'ridge') { + innerLines.push(drawRidgeLine(point1, canvas, roof, textMode)) + } else if (line1.type === 'new') { + const isDiagonal = Math.abs(point1[0] - point1[2]) >= 1 && Math.abs(point1[1] - point1[3]) >= 1 + if (isDiagonal) { + innerLines.push(drawHipLine(point1, canvas, roof, textMode, null, line1.degree, line1.degree)) + } else { + innerLines.push(drawRidgeLine(point1, canvas, roof, textMode)) + } + } + } + + if (length2 > 0 && !alreadyPoints(innerLines, point2)) { + if (line2.type === 'hip') { + innerLines.push(drawHipLine(point2, canvas, roof, textMode, null, line2.degree, line2.degree)) + } else if (line2.type === 'ridge') { + innerLines.push(drawRidgeLine(point2, canvas, roof, textMode)) + } else if (line2.type === 'new') { + const isDiagonal = Math.abs(point2[0] - point2[2]) >= 1 && Math.abs(point2[1] - point2[3]) >= 1 + if (isDiagonal) { + innerLines.push(drawHipLine(point2, canvas, roof, textMode, null, line2.degree, line2.degree)) + } else { + innerLines.push(drawRidgeLine(point2, canvas, roof, textMode)) + } + } + } + + // 연결점에서 새로운 가선분을 생성 + const relationBaseLines = [line1.left, line1.right, line2.left, line2.right] + const uniqueBaseLines = [...new Set(relationBaseLines)] + if (uniqueBaseLines.length === 3) { + const linesCounts = new Map() + relationBaseLines.forEach((e) => { + linesCounts.set(e, (linesCounts.get(e) || 0) + 1) + }) + + const uniqueLines = Array.from(linesCounts.entries()) + .filter(([_, count]) => count === 1) + .map(([line, _]) => line) + + if (uniqueLines.length === 2) { + // 두 변의 이등분선 방향 계산 + // uniqueLines.sort((a, b) => a - b) + console.log('uniqueLines : ', uniqueLines) + const baseLine1 = baseLines[uniqueLines[0]] + const baseLine2 = baseLines[uniqueLines[1]] + + const checkLine1 = new fabric.Line([baseLine1.x1, baseLine1.y1, baseLine1.x2, baseLine1.y2], { + stroke: 'yellow', + strokeWidth: 4, + parentId: roofId, + name: 'check', + }) + const checkLine2 = new fabric.Line([baseLine2.x1, baseLine2.y1, baseLine2.x2, baseLine2.y2], { + stroke: 'blue', + strokeWidth: 4, + parentId: roofId, + name: 'check', + }) + canvas.add(checkLine1, checkLine2) + canvas.renderAll() + let bisector + console.log('isParallel(baseLine1, baseLine2)', isParallel(baseLine1, baseLine2)) + if (isParallel(baseLine1, baseLine2)) { + bisector = getBisectLines( + { x1: line1.start.x, x2: line1.end.x, y1: line1.start.y, y2: line1.end.y }, + { x1: line2.start.x, y1: line2.start.y, x2: line2.end.x, y2: line2.end.y }, + ) + } else { + //이등분선 + bisector = getBisectBaseLines(baseLine1, baseLine2, intersectPoint, canvas) + } + + //마주하는 지붕선을 찾는다. + const intersectionsByRoof = [] + const checkEdge = { + vertex1: { x: intersectPoint.x, y: intersectPoint.y }, + vertex2: { x: intersectPoint.x + bisector.x, y: intersectPoint.y + bisector.y }, + } + const checkVector = { + x: Math.sign(checkEdge.vertex1.x - checkEdge.vertex2.x), + y: Math.sign(checkEdge.vertex1.y - checkEdge.vertex2.y), + } + roof.lines.forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const is = edgesIntersection(lineEdge, checkEdge) + if (is && isPointOnLineNew(line, is)) { + const distance = Math.sqrt((is.x - intersectPoint.x) ** 2 + (is.y - intersectPoint.y) ** 2) + const isVector = { x: Math.sign(intersectPoint.x - is.x), y: Math.sign(intersectPoint.y - is.y) } + if (isVector.x === checkVector.x && isVector.y === checkVector.y) { + intersectionsByRoof.push({ is, distance }) + } + } + }) + intersectionsByRoof.sort((a, b) => a.distance - b.distance) + //지붕 선과의 교점이 존재 할때 + if (intersectionsByRoof.length > 0) { + const is = intersectionsByRoof[0].is + const checkNewLine = new fabric.Line([intersectPoint.x, intersectPoint.y, is.x, is.y], { + stroke: 'cyan', + strokeWidth: 4, + parentId: roofId, + name: 'check', + }) + canvas.add(checkNewLine) + canvas.renderAll() + newAnalysis.push({ + start: { x: intersectPoint.x, y: intersectPoint.y }, + end: { x: is.x, y: is.y }, + left: uniqueLines[0], + right: uniqueLines[1], + type: 'new', + degree: line1.degree, + }) + } + } + canvas + .getObjects() + .filter((object) => object.name === 'check') + .forEach((object) => canvas.remove(object)) + canvas.renderAll() + } + processed.add(pIndex) + } + } + canvas + .getObjects() + .filter((object) => object.name === 'check') + .forEach((object) => canvas.remove(object)) + canvas.renderAll() + } + if (isProceed) { + processed.add(index) + break + } } // 처리된 가선분 제외 - linesAnalysis = linesAnalysis.filter((_, index) => !processed.has(index)).concat(newAnalysis) + linesAnalysis = newAnalysis.concat(linesAnalysis.filter((_, index) => !processed.has(index))) console.log('lineAnalysis: ', linesAnalysis) - /* - linesAnalysis.forEach((line) => { - const checkLine = new fabric.Line([line.start.x, line.start.y, line.end.x, line.end.y], { - stroke: 'red', - strokeWidth: 2, - parentId: roofId, - name: 'check', - }) - canvas.add(checkLine) - canvas.renderAll() - })*/ canvas .getObjects() - .filter((object) => object.name === 'check') + .filter((object) => object.name === 'check' || object.name === 'checkAnalysis') .forEach((object) => canvas.remove(object)) canvas.renderAll() // 새로운 가선분이 없을때 종료 if (newAnalysis.length === 0) break } + console.log('lineAnalysis: end ', linesAnalysis) + + // 가선분 중 처리가 안되어있는 붙어있는 라인에 대한 예외처리. + const proceedAnalysis = [] + linesAnalysis.forEach((currentLine) => { + if (proceedAnalysis.find((p) => p === currentLine)) return + //현재와 같으면 제외, 이미 처리된 라인은 제외, 현재와 공통선분이 존재하지 않으면 제외 + linesAnalysis + .filter( + (partnerLine) => + partnerLine !== currentLine && + !proceedAnalysis.find((p) => p === partnerLine) && + (currentLine.left === partnerLine.left || + currentLine.left === partnerLine.right || + partnerLine.left === currentLine.left || + partnerLine.left === currentLine.right), + ) + .forEach((partnerLine) => { + const dx1 = currentLine.end.x - currentLine.start.x + const dy1 = currentLine.end.y - currentLine.start.y + const dx2 = partnerLine.end.x - partnerLine.start.x + const dy2 = partnerLine.end.y - partnerLine.start.y + const denominator = dy2 * dx1 - dx2 * dy1 + const isOpposite = dx1 * dx2 + dy1 * dy2 < 0 + //평행하지 않으면 제외 + if (Math.abs(denominator) > EPSILON) return + + const currentDegree = getDegreeByChon(baseLines[currentLine.left].attributes.pitch) + if (isOpposite) { + const points = [currentLine.start.x, currentLine.start.y, partnerLine.start.x, partnerLine.start.y] + const length = Math.sqrt((points[0] - points[2]) ** 2 + (points[1] - points[3]) ** 2) + + if (length > 0) { + if (currentLine.type === 'hip') { + innerLines.push(drawHipLine(points, canvas, roof, textMode, null, currentDegree, currentDegree)) + } else if (currentLine.type === 'ridge') { + innerLines.push(drawRidgeLine(points, canvas, roof, textMode)) + } else if (currentLine.type === 'new') { + const isDiagonal = Math.abs(points[0] - points[2]) >= 1 && Math.abs(points[1] - points[3]) >= 1 + if (isDiagonal) { + innerLines.push(drawHipLine(points, canvas, roof, textMode, null, currentDegree, currentDegree)) + } else { + innerLines.push(drawRidgeLine(points, canvas, roof, textMode)) + } + } + proceedAnalysis.push(currentLine, partnerLine) + } + } else { + const allPoints = [currentLine.start, currentLine.end, partnerLine.start, partnerLine.end] + let points = [] + allPoints.forEach((point) => { + let count = 0 + allPoints.forEach((p) => { + if (almostEqual(point.x, p.x) && almostEqual(point.y, p.y)) count++ + }) + if (count === 1) points.push(point) + }) + + const length = Math.sqrt((points[0].x - points[1].x) ** 2 + (points[0].y - points[1].y) ** 2) + if (length < EPSILON) return + const isDiagonal = Math.abs(points[0].x - points[1].x) >= 1 && Math.abs(points[0].y - points[1].y) >= 1 + if (isDiagonal) { + innerLines.push( + drawHipLine([points[0].x, points[0].y, points[1].x, points[1].y], canvas, roof, textMode, null, currentDegree, currentDegree), + ) + } else { + innerLines.push(drawRidgeLine([points[0].x, points[0].y, points[1].x, points[1].y], canvas, roof, textMode)) + } + proceedAnalysis.push(currentLine, partnerLine) + } + }) + }) //지붕 innerLines에 추가. roof.innerLines.push(...innerLines, ...ridgeLines) @@ -2886,16 +2911,31 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { * @param line2End */ const lineIntersection = (line1Start, line1End, line2Start, line2End) => { - const denominator = (line2End.y - line2Start.y) * (line1End.x - line1Start.x) - (line2End.x - line2Start.x) * (line1End.y - line1Start.y) - if (Math.abs(denominator) < EPSILON) return null // 평행 + const dx1 = line1End.x - line1Start.x + const dy1 = line1End.y - line1Start.y + const dx2 = line2End.x - line2Start.x + const dy2 = line2End.y - line2Start.y + // const denominator = (line2End.y - line2Start.y) * (line1End.x - line1Start.x) - (line2End.x - line2Start.x) * (line1End.y - line1Start.y) + const denominator = dy2 * dx1 - dx2 * dy1 + if (Math.abs(denominator) < EPSILON) { + console.log('평행') + const isOpposite = dx1 * dx2 + dy1 * dy2 < 0 - const ua = ((line2End.x - line2Start.x) * (line1Start.y - line2Start.y) - (line2End.y - line2Start.y) * (line1Start.x - line2Start.x)) / denominator - const ub = ((line1End.x - line1Start.x) * (line1Start.y - line2Start.y) - (line1End.y - line1Start.y) * (line1Start.x - line2Start.x)) / denominator + const isOverlap = isPointOnLineNew({ x1: line1Start.x, y1: line1Start.y, x2: line1End.x, y2: line1End.y }, { x: line2Start.x, y: line2Start.y }) + + if (isOpposite && isOverlap) { + return { x: line2Start.x, y: line2Start.y } + } + return null + } // 평행 + + const ua = (dx2 * (line1Start.y - line2Start.y) - dy2 * (line1Start.x - line2Start.x)) / denominator + const ub = (dx1 * (line1Start.y - line2Start.y) - dy1 * (line1Start.x - line2Start.x)) / denominator if (ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1) { return { - x: line1Start.x + ua * (line1End.x - line1Start.x), - y: line1Start.y + ua * (line1End.y - line1Start.y), + x: line1Start.x + ua * dx1, + y: line1Start.y + ua * dy1, } } return null @@ -2964,7 +3004,6 @@ const isParallel = (line1, line2) => { * 두 라인의 방향에 따라 이등분선의 vector를 구한다. * @param line1 * @param line2 - * @param polygon */ const getBisectLines = (line1, line2) => { const bisector = { x: 0, y: 0 } @@ -3009,27 +3048,7 @@ const getBisectLines = (line1, line2) => { * @param line2 * @param intersection */ -const getBisectBaseLines = (line1, line2, intersection, canvas) => { - const checkLine1 = new fabric.Line([line1.x1, line1.y1, line1.x2, line1.y2], { - stroke: 'red', - strokeWidth: 4, - name: 'check', - }) - const checkLine2 = new fabric.Line([line2.x1, line2.y1, line2.x2, line2.y2], { - stroke: 'blue', - strokeWidth: 4, - name: 'check', - }) - const checkCircle = new fabric.Circle({ - left: intersection.x, - top: intersection.y, - radius: 5, - fill: 'cyan', - name: 'check', - }) - canvas.add(checkLine1, checkLine2, checkCircle) - canvas.renderAll() - +const getBisectBaseLines = (line1, line2, intersection) => { let bisector = { x: 0, y: 0 } if (isParallel(line1, line2)) return bisector @@ -3037,42 +3056,30 @@ const getBisectBaseLines = (line1, line2, intersection, canvas) => { const lineEdge2 = { vertex1: { x: line2.x1, y: line2.y1 }, vertex2: { x: line2.x2, y: line2.y2 } } const is = edgesIntersection(lineEdge1, lineEdge2) if (is) { - const checkCircle = new fabric.Circle({ - left: is.x, - top: is.y, - radius: 5, - fill: 'pink', - name: 'check', - }) - canvas.add(checkCircle) - canvas.renderAll() bisector = { x: Math.sign(intersection.x - is.x), y: Math.sign(intersection.y - is.y) } } - - canvas.remove(checkLine1, checkLine2, checkCircle) - canvas.renderAll() - return bisector } +/** + * a 와 b 가 epsilon내 오차인지 확인한다. + * @param a + * @param b + * @param epsilon + * @returns {boolean} + */ const almostEqual = (a, b, epsilon = 1e-10) => { if (a === Infinity || b === Infinity) return false - const diff = Math.abs(a - b) - const larger = Math.max(Math.abs(a), Math.abs(b)) - - // 둘 다 0에 가까운 경우 - if (larger < epsilon) { - return diff < epsilon - } - - return diff <= larger * epsilon + return Math.abs(a - b) <= epsilon } +/** + * segment가 fromPoint에서 떨어진 방향을 구한다. + * @param segment + * @param fromPoint + * @returns {{x, y}} + */ const getDirection = (segment, fromPoint) => { - // const dist1 = Math.sqrt(Math.pow(segment.x1 - fromPoint.x, 2) + Math.pow(segment.y1 - fromPoint.y, 2)) - // const dist2 = Math.sqrt(Math.pow(segment.x2 - fromPoint.x, 2) + Math.pow(segment.y2 - fromPoint.y, 2)) - - // const target = dist1 > dist2 ? { x: segment.x1, y: segment.y1 } : { x: segment.x2, y: segment.y2 } const target = { x: segment.x2, y: segment.y2 } const dx = target.x - fromPoint.x @@ -3081,6 +3088,34 @@ const getDirection = (segment, fromPoint) => { return { x: dx / length, y: dy / length } } + +/** + * lines중 points가 이미 존재하는지 확인한다. + * @param lines + * @param points + * @returns {boolean} + */ +const alreadyPoints = (lines, points) => { + let has = false + + lines.forEach((line) => { + const linePoints = [line.x1, line.y1, line.x2, line.y2] + if ( + (almostEqual(linePoints[0], points[0], 1) && + almostEqual(linePoints[1], points[1], 1) && + almostEqual(linePoints[2], points[2], 1) && + almostEqual(linePoints[3], points[3], 1)) || + (almostEqual(linePoints[0], points[2], 1) && + almostEqual(linePoints[1], points[3], 1) && + almostEqual(linePoints[2], points[0], 1) && + almostEqual(linePoints[3], points[1], 1)) + ) { + has = true + } + }) + + return has +} /** * 마루가 있는 지붕을 그린다. * @param roofId