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 QPolygon from '@/components/fabric/QPolygon3'
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 = () => {
fabric.QPolygon = fabric.util.createClass(fabric.Group, {})
// 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, object, initLengthTxt))
})
fabric.Object._fromObject('QPolygon', object, callback, 'points')
}
}
export const drawHelpLineInHexagon = (polygon, chon) => {
const centerLines = drawCenterLines(polygon)
let helpLines = []
const interSectionPoints = []
const tempInterSectionPoints = []
const ridgeStartPoints = []
const ridgeEndPoints = []
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.lines.forEach((line, index) => {
if (line.length > maxLength) {
maxLength = line.length
}
const wallLine = polygon.wall.lines.filter((wallLine) => wallLine.idx === line.idx)[0]
const maxLength = Math.max(...polygon.lines.map((line) => line.length))
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) => {
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 degree = fabric.util.radiansToDegrees(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], {
fontSize: polygon.fontSize,
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 = 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) => {
point.alreadyIntersected = false
// 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
}
const interSectionPoint = calculateIntersection(line, nextLine)
if (
interSectionPoint &&
polygon.inPolygon(interSectionPoint) &&
polygon.wall.inPolygon(interSectionPoint) &&
Math.abs(distanceBetweenPoints(line.startPoint, interSectionPoint) - distanceBetweenPoints(nextLine.startPoint, interSectionPoint)) < 2
) {
const area = calculateTriangleArea(line.startPoint, nextLine.startPoint, interSectionPoint)
const currentLineConnectedPoint = line.connectedPoint
const nextLineConnectedPoint = nextLine.connectedPoint
if (area <= 1) {
return
}
})
})
if (arrivalPoint) {
hipLine.relatedPoints.forEach((relatedPoint) => {
if (relatedPoint.x !== arrivalPoint.x && relatedPoint.y !== arrivalPoint.y) {
centerInterSectionPoints.splice(centerInterSectionPoints.indexOf(relatedPoint), 1)
if (currentLineConnectedPoint && currentLineConnectedPoint.area < area) {
return
}
})
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], {
fontSize: polygon.fontSize,
stroke: 'red',
})
const ridge = new QLine([startPoint.x, startPoint.y, arrivalPoint.x, arrivalPoint.y], {
stroke: 'green',
fontSize: polygon.fontSize,
})
const endPoint =
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
polygon.canvas.add(helpLine)
polygon.canvas.add(ridge)
polygon.canvas.renderAll()
ridgeEndPoints.push(arrivalPoint)
point.alreadyIntersected = true
line.connectedPoint = { interSectionPoint, area, startPoint, endPoint }
line.connectedPoints.push(interSectionPoint)
nextLine.connectedPoint = { interSectionPoint, area, startPoint, endPoint }
nextLine.connectedPoints.push(interSectionPoint)
}
}
})
/**
* 안쓰는 제거
*/
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 }
helpLines.forEach((line) => {
if (line.connectedPoint) {
tempInterSectionPoints.push(line.connectedPoint)
}
})
while (ridgeEndRemainingPoints.length > 0) {
const point = ridgeEndRemainingPoints.shift()
let isExist = false
uniqueInterSectionPoints.forEach((uniquePoint) => {
const degree = calculateAngle(point, uniquePoint)
// interSectionPoints에서 interSectionPoint가 중복인 값이 있는 경우만 선택한다.
tempInterSectionPoints.forEach((point) => {
// intersectionPoint가 중복인 경우
const isDuplicated =
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) {
const line = new QLine([point.x, point.y, uniquePoint.x, uniquePoint.y], {
stroke: 'purple',
fontSize: polygon.fontSize,
})
// interSectionPoints에서 interSectionPoint 기준으로 중복을 제거한다.
const uniqueInterSectionPoints = Array.from(
new Set(interSectionPoints.map((point) => `${point.interSectionPoint.x},${point.interSectionPoint.y}`)),
).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)
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], {
const line = new QLine([point.startPoint.x, point.startPoint.y, point.interSectionPoint.x, point.interSectionPoint.y], {
stroke: 'purple',
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.renderAll()
}
polygon.canvas.add(line2)
})
const removedIdx = []
// ridgeEndPoints와 가까운 centerInterSectionPoints를 찾아서 연결한다.
const remainingPoints = centerInterSectionPoints
/*
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,
})
const connectedPoints = line.connectedPoints
connectedPoints.forEach((connectedPoint) => {
uniqueInterSectionPoints.forEach((point) => {
const interSectionPoint = point.interSectionPoint
polygon.canvas.add(hip)
polygon.canvas.renderAll()
if (connectedPoint.x === interSectionPoint.x && connectedPoint.y === interSectionPoint.y) {
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) {
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,
name: 'connectRidge',
})
polygon.connectRidges.push(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) => {
const centerLines = []
@ -384,3 +340,38 @@ const calculateAngle = (point1, point2) => {
const angleInRadians = Math.atan2(deltaY, deltaX)
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
}