This commit is contained in:
hyojun.choi 2024-07-31 10:56:43 +09:00
parent 76d9a046a4
commit bf82b7fcae

View File

@ -1,292 +1,248 @@
import { fabric } from 'fabric' import { fabric } from 'fabric'
import QPolygon from '@/components/fabric/QPolygon3'
import { QLine } from '@/components/fabric/QLine' import { QLine } from '@/components/fabric/QLine'
import { calculateIntersection2, distanceBetweenPoints, findClosestPoint } from '@/util/canvas-util' import { calculateIntersection, distanceBetweenPoints, findClosestPoint } from '@/util/canvas-util'
export const defineQPloygon = () => { export const defineQPloygon = () => {
fabric.QPolygon = fabric.util.createClass(fabric.Group, {})
// fromObject 메서드를 QLine 클래스에 직접 추가
fabric.QPolygon.fromObject = function (object, callback) { fabric.QPolygon.fromObject = function (object, callback) {
const { initOption, initPoints, initLengthTxt } = object fabric.Object._fromObject('QPolygon', object, callback, 'points')
fabric.util.enlivenObjects(object.objects, function (enlivenedObjects) {
return callback(new QPolygon(initPoints, object, initLengthTxt))
})
} }
} }
export const drawHelpLineInHexagon = (polygon, chon) => { export const drawHelpLineInHexagon = (polygon, chon) => {
const centerLines = drawCenterLines(polygon) const centerLines = drawCenterLines(polygon)
let helpLines = [] let helpLines = []
const interSectionPoints = []
const tempInterSectionPoints = []
const ridgeStartPoints = [] const ridgeStartPoints = []
const ridgeEndPoints = [] const ridgeEndPoints = []
const centerInterSectionPoints = [] const centerInterSectionPoints = []
let maxLength = 0 // polygon.lines = polygon.lines.sort((a, b) => a.length - b.length)
polygon.lines = polygon.lines.sort((a, b) => a.length - b.length)
polygon.wall.lines = getOneSideLines(polygon.wall) polygon.wall.lines = getOneSideLines(polygon.wall)
// 짧은 라인 순서대로 삼각 지붕을 그린다. const maxLength = Math.max(...polygon.lines.map((line) => line.length))
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)
// line을 이등변 삼각형의 밑변으로 보고 높이를 구한다.
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))
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,
stroke: 'skyblue',
})
const secondHelpLine = new QLine([line.x2, line.y2, secondX2, secondY2], {
fontSize: polygon.fontSize,
stroke: 'skyblue',
})
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) => { polygon.points.forEach((point, index) => {
if (point.alreadyIntersected) {
return
}
const wallPoint = polygon.wall.points[index] const wallPoint = polygon.wall.points[index]
const angle = Math.atan2(wallPoint.y - point.y, wallPoint.x - point.x) 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 degree = fabric.util.radiansToDegrees(angle)
const newY2 = Math.floor(point.y + (maxLength / 2 + 50) * Math.sin(angle))
const newX2 = Math.floor(point.x + maxLength * Math.cos(angle))
const newY2 = Math.floor(point.y + maxLength * Math.sin(angle))
const helpLine = new QLine([point.x, point.y, newX2, newY2], { const helpLine = new QLine([point.x, point.y, newX2, newY2], {
fontSize: polygon.fontSize, fontSize: polygon.fontSize,
stroke: 'green', stroke: 'green',
startPoint: point,
degree: degree,
idx: index,
}) })
const relatedPoints = [] // polygon.canvas?.add(helpLine)
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.push(helpLine)
}) })
helpLines = helpLines.filter((line) => line.relatedPoints?.length > 0) helpLines.forEach((line, index) => {
for (let i = index + 1; i < helpLines.length; i++) {
const nextLine = helpLines[i]
if (!line.connectedPoint) {
line.connectedPoint = null
line.connectedPoints = []
}
if (!nextLine.connectedPoint) {
nextLine.connectedPoint = null
nextLine.connectedPoints = []
}
ridgeStartPoints.forEach((point) => { const interSectionPoint = calculateIntersection(line, nextLine)
point.alreadyIntersected = false
// x 혹은 y가 같으면서 가장 가까운 점을 찾는다. if (
let arrivalPoint interSectionPoint &&
let hipLine polygon.inPolygon(interSectionPoint) &&
let distance = Infinity polygon.wall.inPolygon(interSectionPoint) &&
let startPoint Math.abs(distanceBetweenPoints(line.startPoint, interSectionPoint) - distanceBetweenPoints(nextLine.startPoint, interSectionPoint)) < 2
helpLines.forEach((line) => { ) {
line.relatedPoints.forEach((relatedPoint) => { const area = calculateTriangleArea(line.startPoint, nextLine.startPoint, interSectionPoint)
if (Math.abs(point.x - relatedPoint.x) <= 2 || Math.abs(point.y - relatedPoint.y) <= 2) { const currentLineConnectedPoint = line.connectedPoint
if (distanceBetweenPoints(point, relatedPoint) < distance) { const nextLineConnectedPoint = nextLine.connectedPoint
startPoint = point
distance = distanceBetweenPoints(point, relatedPoint) if (area <= 1) {
hipLine = line return
arrivalPoint = relatedPoint
}
} }
})
})
if (arrivalPoint) { if (currentLineConnectedPoint && currentLineConnectedPoint.area < area) {
hipLine.relatedPoints.forEach((relatedPoint) => { return
if (relatedPoint.x !== arrivalPoint.x && relatedPoint.y !== arrivalPoint.y) {
centerInterSectionPoints.splice(centerInterSectionPoints.indexOf(relatedPoint), 1)
} }
})
helpLines.splice(helpLines.indexOf(hipLine), 1) //startPoint는 line의 startPoint와 nextLine의 startPoint를 비교하여 x가 같은경우 y가 더 작은 값, y가 같은경우 x가 더 작은 값을 선택한다.
const startPoint =
line.startPoint.x === nextLine.startPoint.x
? line.startPoint.y < nextLine.startPoint.y
? line.startPoint
: nextLine.startPoint
: line.startPoint.x < nextLine.startPoint.x
? line.startPoint
: nextLine.startPoint
const helpLine = new QLine([hipLine.x1, hipLine.y1, arrivalPoint.x, arrivalPoint.y], { const endPoint =
fontSize: polygon.fontSize, line.startPoint.x === nextLine.startPoint.x
stroke: 'red', ? line.startPoint.y > nextLine.startPoint.y
}) ? line.startPoint
const ridge = new QLine([startPoint.x, startPoint.y, arrivalPoint.x, arrivalPoint.y], { : nextLine.startPoint
stroke: 'green', : line.startPoint.x > nextLine.startPoint.x
fontSize: polygon.fontSize, ? line.startPoint
}) : nextLine.startPoint
polygon.canvas.add(helpLine) line.connectedPoint = { interSectionPoint, area, startPoint, endPoint }
polygon.canvas.add(ridge) line.connectedPoints.push(interSectionPoint)
nextLine.connectedPoint = { interSectionPoint, area, startPoint, endPoint }
polygon.canvas.renderAll() nextLine.connectedPoints.push(interSectionPoint)
ridgeEndPoints.push(arrivalPoint) }
point.alreadyIntersected = true
} }
}) })
/** helpLines.forEach((line) => {
* 안쓰는 제거 if (line.connectedPoint) {
*/ tempInterSectionPoints.push(line.connectedPoint)
}
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) { // interSectionPoints에서 interSectionPoint가 중복인 값이 있는 경우만 선택한다.
const point = ridgeEndRemainingPoints.shift() tempInterSectionPoints.forEach((point) => {
let isExist = false // intersectionPoint가 중복인 경우
uniqueInterSectionPoints.forEach((uniquePoint) => { const isDuplicated =
const degree = calculateAngle(point, uniquePoint) tempInterSectionPoints.filter((p) => p.interSectionPoint.x === point.interSectionPoint.x && p.interSectionPoint.y === point.interSectionPoint.y)
.length > 1
if (isDuplicated) {
interSectionPoints.push(point)
}
})
if (Math.abs(45 - Math.abs(degree)) <= 5 || Math.abs(135 - Math.abs(degree)) <= 5) { // interSectionPoints에서 interSectionPoint 기준으로 중복을 제거한다.
const line = new QLine([point.x, point.y, uniquePoint.x, uniquePoint.y], { const uniqueInterSectionPoints = Array.from(
stroke: 'purple', new Set(interSectionPoints.map((point) => `${point.interSectionPoint.x},${point.interSectionPoint.y}`)),
fontSize: polygon.fontSize, ).map((key) => {
}) const { interSectionPoint, area, startPoint, endPoint } = interSectionPoints.find(
(point) => `${point.interSectionPoint.x},${point.interSectionPoint.y}` === key,
)
return { interSectionPoint, area, startPoint, endPoint }
})
ridgeEndPoints.push(uniquePoint) uniqueInterSectionPoints.forEach((point) => {
ridgeStartPoints.push(point.interSectionPoint)
ridgeEndPoints.splice(ridgeEndPoints.indexOf(point), 1) const line = new QLine([point.startPoint.x, point.startPoint.y, point.interSectionPoint.x, point.interSectionPoint.y], {
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', stroke: 'purple',
fontSize: polygon.fontSize, fontSize: polygon.fontSize,
name: 'hip',
}) })
const line2 = new QLine([point.endPoint.x, point.endPoint.y, point.interSectionPoint.x, point.interSectionPoint.y], {
stroke: 'purple',
fontSize: polygon.fontSize,
name: 'hip',
})
polygon.hips.push(line)
polygon.hips.push(line2)
polygon.canvas.add(line) polygon.canvas.add(line)
polygon.canvas.renderAll() polygon.canvas.add(line2)
} })
const removedIdx = []
// ridgeEndPoints와 가까운 centerInterSectionPoints를 찾아서 연결한다.
const remainingPoints = centerInterSectionPoints
/*
helpLines.forEach((line) => { helpLines.forEach((line) => {
remainingPoints.forEach((point) => { const connectedPoints = line.connectedPoints
if (line.relatedPoints.includes(point)) { connectedPoints.forEach((connectedPoint) => {
const hip = new QLine([line.x1, line.y1, point.x, point.y], { uniqueInterSectionPoints.forEach((point) => {
stroke: 'red', const interSectionPoint = point.interSectionPoint
fontSize: polygon.fontSize,
})
polygon.canvas.add(hip) if (connectedPoint.x === interSectionPoint.x && connectedPoint.y === interSectionPoint.y) {
polygon.canvas.renderAll() removedIdx.push(line.idx)
}
})
})
})
const notIntersectedLines = helpLines.filter((line) => !removedIdx.includes(line.idx))
notIntersectedLines.forEach((line) => {
centerLines.forEach((centerLine) => {
const interSectionPoint = calculateIntersection(line, centerLine)
if (interSectionPoint && polygon.inPolygon(interSectionPoint) && polygon.wall.inPolygon(interSectionPoint)) {
centerInterSectionPoints.push(interSectionPoint)
} }
}) })
}) })
// centerInterSectionPoints에서 ridgeStartPoints와 x가 같거나 y가 같은것중 가장 가까운 점들을 찾는다.
ridgeStartPoints.forEach((point) => {
const xPoints = centerInterSectionPoints.filter((centerPoint) => Math.abs(centerPoint.x - point.x) < 2)
const yPoints = centerInterSectionPoints.filter((centerPoint) => Math.abs(centerPoint.y - point.y) < 2)
let closestPoint
if (xPoints.length === 0) {
closestPoint = findClosestPoint(point, yPoints)
} else {
closestPoint = findClosestPoint(point, xPoints)
}
if (closestPoint) {
const line = new QLine([point.x, point.y, closestPoint.x, closestPoint.y], {
stroke: 'purple',
fontSize: polygon.fontSize,
name: 'ridge',
})
polygon.ridges.push(line)
polygon.canvas.add(line)
ridgeEndPoints.push(closestPoint)
}
})
// ridgeEndPoints끼리 이어준다.
const remainingPoints = ridgeEndPoints
remainingPoints.forEach((ridgePoint) => {
polygon.points.forEach((point) => {
const degree = calculateAngle(ridgePoint, point)
if (Math.abs(degree) % 45 < 1) {
const line = new QLine([ridgePoint.x, ridgePoint.y, point.x, point.y], {
stroke: 'purple',
fontSize: polygon.fontSize,
name: 'hip',
})
polygon.hips.push(line)
polygon.canvas.add(line)
}
})
})
// centerInterSectionPoints에 남아있는 점들을 가까운 점끼리 연결한다.
while (remainingPoints.length > 0) { while (remainingPoints.length > 0) {
const point = remainingPoints.shift() const point = remainingPoints.shift()
const closestPoint = findClosestPoint(point, remainingPoints) const closestPoint = findClosestPoint(point, remainingPoints)
if (!closestPoint) continue if (!closestPoint) continue
// 마루끼리 연결
const line = new QLine([point.x, point.y, closestPoint.x, closestPoint.y], { const line = new QLine([point.x, point.y, closestPoint.x, closestPoint.y], {
stroke: 'purple', stroke: 'purple',
fontSize: polygon.fontSize, fontSize: polygon.fontSize,
name: 'connectRidge',
}) })
polygon.connectRidges.push(line)
polygon.canvas.add(line) 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 drawHelpLineInHexagon2 = (polygon, chon) => {}
export const drawCenterLines = (polygon) => { export const drawCenterLines = (polygon) => {
const centerLines = [] const centerLines = []
@ -384,3 +340,38 @@ const calculateAngle = (point1, point2) => {
const angleInRadians = Math.atan2(deltaY, deltaX) const angleInRadians = Math.atan2(deltaY, deltaX)
return angleInRadians * (180 / Math.PI) return angleInRadians * (180 / Math.PI)
} }
/**
* 3개의 점을 이용해 직각 이등변 삼각형인지 확인
* @param point1
* @param point2
* @param point3
* @returns {boolean}
*/
const isRightIsoscelesTriangle = (point1, point2, point3) => {
const distance = (p1, p2) => Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2))
const d1 = distance(point1, point2)
const d2 = distance(point2, point3)
const d3 = distance(point3, point1)
const distances = [d1, d2, d3].sort((a, b) => a - b)
// Check if the two smaller distances are equal and the largest distance is the hypotenuse
return distances[0] === distances[1] && Math.abs(Math.pow(distances[0], 2) * 2 - Math.pow(distances[2], 2)) < 1
}
/**
* 세개의 점으로 삼각형의 넓이를 구한다.
* @param point1
* @param point2
* @param point3
* @returns {number}
*/
const calculateTriangleArea = (point1, point2, point3) => {
const { x: x1, y: y1 } = point1
const { x: x2, y: y2 } = point2
const { x: x3, y: y3 } = point3
return Math.abs(x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2)) / 2
}