3349 lines
112 KiB
JavaScript
3349 lines
112 KiB
JavaScript
import { fabric } from 'fabric'
|
|
import { QLine } from '@/components/fabric/QLine'
|
|
import { calculateIntersection, distanceBetweenPoints, findClosestPoint, getDirectionByPoint } from '@/util/canvas-util'
|
|
import { QPolygon } from '@/components/fabric/QPolygon'
|
|
import * as turf from '@turf/turf'
|
|
import { POLYGON_TYPE } from '@/common/common'
|
|
|
|
const TWO_PI = Math.PI * 2
|
|
|
|
export const defineQPloygon = () => {
|
|
fabric.QPolygon.fromObject = function (object, callback) {
|
|
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 = []
|
|
|
|
let centerInterSectionPoints = []
|
|
|
|
// polygon.lines = polygon.lines.sort((a, b) => a.length - b.length)
|
|
polygon.wall.lines = getOneSideLines(polygon.wall)
|
|
|
|
const maxLength = Math.max(...polygon.lines.map((line) => line.length))
|
|
|
|
polygon.points.forEach((point, index) => {
|
|
const wallPoint = polygon.wall.points[index]
|
|
|
|
const angle = Math.atan2(wallPoint.y - point.y, wallPoint.x - point.x)
|
|
|
|
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,
|
|
})
|
|
|
|
// polygon.canvas?.add(helpLine)
|
|
|
|
helpLines.push(helpLine)
|
|
})
|
|
|
|
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 = []
|
|
}
|
|
|
|
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 (currentLineConnectedPoint && currentLineConnectedPoint.area < area) {
|
|
return
|
|
}
|
|
|
|
//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 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
|
|
|
|
line.connectedPoint = { interSectionPoint, area, startPoint, endPoint }
|
|
line.connectedPoints.push({ interSectionPoint, area, startPoint, endPoint })
|
|
nextLine.connectedPoint = { interSectionPoint, area, startPoint, endPoint }
|
|
nextLine.connectedPoints.push({ interSectionPoint, area, startPoint, endPoint })
|
|
}
|
|
}
|
|
})
|
|
|
|
helpLines.forEach((line) => {
|
|
if (line.connectedPoint) {
|
|
tempInterSectionPoints.push(line.connectedPoint)
|
|
}
|
|
})
|
|
|
|
// 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)
|
|
}
|
|
})
|
|
|
|
// 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 }
|
|
})
|
|
|
|
uniqueInterSectionPoints.forEach((point) => {
|
|
ridgeStartPoints.push(point.interSectionPoint)
|
|
|
|
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',
|
|
})
|
|
|
|
line.startPoint = point.startPoint
|
|
line.endPoint = point.interSectionPoint
|
|
|
|
line2.startPoint = point.endPoint
|
|
line2.endPoint = point.interSectionPoint
|
|
|
|
polygon.hips.push(line)
|
|
polygon.hips.push(line2)
|
|
|
|
polygon.canvas.add(line)
|
|
polygon.canvas.add(line2)
|
|
})
|
|
|
|
const removedIdx = []
|
|
|
|
helpLines.forEach((line) => {
|
|
const connectedPoints = line.connectedPoints
|
|
connectedPoints.forEach((connectedPoint) => {
|
|
uniqueInterSectionPoints.forEach((point) => {
|
|
const interSectionPoint = point.interSectionPoint
|
|
|
|
if (connectedPoint.interSectionPoint.x === interSectionPoint.x && connectedPoint.interSectionPoint.y === interSectionPoint.y) {
|
|
removedIdx.push(line.idx)
|
|
}
|
|
})
|
|
})
|
|
})
|
|
|
|
let notIntersectedLines = helpLines.filter((line) => !removedIdx.includes(line.idx))
|
|
|
|
notIntersectedLines = notIntersectedLines.map((line) => {
|
|
return { ...line, centerInterSectionPoints: [] }
|
|
})
|
|
|
|
notIntersectedLines.forEach((line) => {
|
|
centerLines.forEach((centerLine) => {
|
|
const interSectionPoint = calculateIntersection(line, centerLine)
|
|
|
|
if (interSectionPoint && polygon.inPolygon(interSectionPoint) && polygon.wall.inPolygon(interSectionPoint)) {
|
|
line.centerInterSectionPoints.push(interSectionPoint)
|
|
interSectionPoint.lineIdx = line.idx
|
|
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 if (yPoints.length === 0) {
|
|
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',
|
|
direction: getDirectionByPoint(point, closestPoint),
|
|
})
|
|
|
|
line.startPoint = point
|
|
line.endPoint = closestPoint
|
|
|
|
polygon.ridges.push(line)
|
|
polygon.canvas.add(line)
|
|
ridgeEndPoints.push(closestPoint)
|
|
|
|
notIntersectedLines = notIntersectedLines.filter((line) => line.idx !== closestPoint.lineIdx)
|
|
}
|
|
})
|
|
|
|
centerInterSectionPoints = []
|
|
notIntersectedLines.forEach((line) => {
|
|
centerInterSectionPoints.push(...line.centerInterSectionPoints)
|
|
})
|
|
|
|
// ridgeEndPoints끼리 이어준다.
|
|
const remainingPoints = [...ridgeEndPoints]
|
|
|
|
// ridgeEndPoint에서 centerInterSectionPoints와 45도인 점을 찾아 이어준다.
|
|
|
|
ridgeEndPoints.forEach((ridgePoint) => {
|
|
const filteredCenterInterSectionPoints = centerInterSectionPoints.filter((centerPoint) => {
|
|
const degree = calculateAngle(ridgePoint, centerPoint)
|
|
return Math.abs(degree) === 45 || Math.abs(degree) === 135
|
|
})[0]
|
|
|
|
if (filteredCenterInterSectionPoints) {
|
|
const line = new QLine([ridgePoint.x, ridgePoint.y, filteredCenterInterSectionPoints.x, filteredCenterInterSectionPoints.y], {
|
|
stroke: 'purple',
|
|
fontSize: polygon.fontSize,
|
|
name: 'hip',
|
|
})
|
|
|
|
if (line.length === 0) {
|
|
return
|
|
}
|
|
|
|
line.startPoint = ridgePoint
|
|
line.endPoint = filteredCenterInterSectionPoints
|
|
|
|
polygon.hips.push(line)
|
|
polygon.canvas.add(line)
|
|
|
|
ridgeStartPoints.push(filteredCenterInterSectionPoints)
|
|
|
|
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)
|
|
}
|
|
})
|
|
}
|
|
})
|
|
|
|
// ridgeEndPoint끼리 연결한다.
|
|
while (remainingPoints.length > 1) {
|
|
const startPoint = remainingPoints.shift()
|
|
const endPoint = remainingPoints.shift()
|
|
|
|
if (!(startPoint.x === endPoint.x && startPoint.y === endPoint.y)) {
|
|
const line = new QLine([startPoint.x, startPoint.y, endPoint.x, endPoint.y], {
|
|
stroke: 'purple',
|
|
fontSize: polygon.fontSize,
|
|
name: 'connectRidge',
|
|
})
|
|
|
|
line.startPoint = startPoint
|
|
line.endPoint = endPoint
|
|
|
|
polygon.connectRidges.push(line)
|
|
|
|
polygon.points.forEach((point) => {
|
|
const degree = calculateAngle(startPoint, point)
|
|
|
|
if (Math.abs(degree) === 45 || Math.abs(degree) === 135) {
|
|
const line = new QLine([startPoint.x, startPoint.y, point.x, point.y], {
|
|
stroke: 'purple',
|
|
fontSize: polygon.fontSize,
|
|
name: 'hip',
|
|
})
|
|
|
|
line.startPoint = startPoint
|
|
line.endPoint = point
|
|
|
|
polygon.hips.push(line)
|
|
polygon.canvas.add(line)
|
|
}
|
|
})
|
|
|
|
polygon.points.forEach((point) => {
|
|
const degree = calculateAngle(endPoint, point)
|
|
|
|
if (Math.abs(degree) === 45 || Math.abs(degree) === 135) {
|
|
const line = new QLine([endPoint.x, endPoint.y, point.x, point.y], {
|
|
stroke: 'purple',
|
|
fontSize: polygon.fontSize,
|
|
name: 'hip',
|
|
})
|
|
|
|
line.startPoint = endPoint
|
|
line.endPoint = point
|
|
|
|
polygon.hips.push(line)
|
|
polygon.canvas.add(line)
|
|
}
|
|
})
|
|
|
|
polygon.canvas.add(line)
|
|
} else {
|
|
polygon.points.forEach((point) => {
|
|
const degree = calculateAngle(startPoint, point)
|
|
|
|
if (Math.abs(degree) === 45 || Math.abs(degree) === 135) {
|
|
const line = new QLine([startPoint.x, startPoint.y, point.x, point.y], {
|
|
stroke: 'purple',
|
|
fontSize: polygon.fontSize,
|
|
name: 'hip',
|
|
})
|
|
|
|
line.startPoint = startPoint
|
|
line.endPoint = point
|
|
|
|
polygon.hips.push(line)
|
|
polygon.canvas.add(line)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
export const drawCenterLines = (polygon) => {
|
|
const centerLines = []
|
|
|
|
const oneSideLines = polygon.lines.map((line) => getOneSideLine(line))
|
|
|
|
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]
|
|
|
|
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
|
|
newY1 = line.y2
|
|
newX2 = line.x1
|
|
newY2 = line.y1
|
|
|
|
line.x1 = newX1
|
|
line.y1 = newY1
|
|
line.x2 = newX2
|
|
line.y2 = newY2
|
|
line.direction = 'bottom'
|
|
line.startPoint = { x: newX1, y: newY1 }
|
|
line.endPoint = { x: newX2, y: newY2 }
|
|
} 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'
|
|
line.startPoint = { x: newX1, y: newY1 }
|
|
line.endPoint = { x: newX2, y: newY2 }
|
|
}
|
|
return line
|
|
})
|
|
}
|
|
export 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)
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
}
|
|
|
|
// polygon을 나눈다.
|
|
export const dividePolygon = (polygon) => {
|
|
let hips = polygon.hips
|
|
const ridges = polygon.ridges.map((ridge) => getOneSideLine(ridge))
|
|
const connectRidges = polygon.connectRidges
|
|
const polygonLines = polygon.lines
|
|
|
|
hips.forEach((hip) => {
|
|
// hips의 startPoint와 endPoint를 polygon의 points와 비교하여 같은 점이 endPoint일 경우 startPoint로 변경한다.
|
|
const startPoint = polygon.points.find((point) => point.x === hip.endPoint.x && point.y === hip.endPoint.y)
|
|
if (startPoint) {
|
|
const temp = hip.startPoint
|
|
hip.startPoint = hip.endPoint
|
|
hip.endPoint = temp
|
|
}
|
|
})
|
|
hips = [...hips, ...connectRidges]
|
|
|
|
polygonLines.forEach((line, index) => {
|
|
let ridge
|
|
|
|
const startPoint = line.startPoint
|
|
const endPoint = line.endPoint
|
|
let polygonPoints = []
|
|
|
|
polygonPoints.push(startPoint)
|
|
|
|
polygonPoints.push(endPoint)
|
|
|
|
const startHip = hips.find((hip) => hip.startPoint.x === startPoint.x && hip.startPoint.y === startPoint.y)
|
|
const endHip = hips.find((hip) => hip.startPoint.x === endPoint.x && hip.startPoint.y === endPoint.y)
|
|
|
|
if (!startHip || !endHip) {
|
|
return
|
|
}
|
|
|
|
if (startHip && endHip && startHip.endPoint.x === endHip.endPoint.x && startHip.endPoint.y === endHip.endPoint.y) {
|
|
polygonPoints.push(startHip.endPoint)
|
|
|
|
const newPolygon = new QPolygon(polygonPoints, {
|
|
fontSize: polygon.fontSize,
|
|
parentId: polygon.id,
|
|
name: 'roof',
|
|
selectable: false,
|
|
stroke: 'black',
|
|
fill: 'transparent',
|
|
strokeWidth: 3,
|
|
})
|
|
|
|
polygon.canvas.add(newPolygon)
|
|
return
|
|
}
|
|
|
|
let connectedRidge
|
|
const restRidgeConnection = connectRidges[0]
|
|
|
|
if (!restRidgeConnection || restRidgeConnection.length === 0) {
|
|
connectedRidge = ridges.find(
|
|
(ridge) =>
|
|
(ridge.startPoint.x === startHip.endPoint.x &&
|
|
ridge.startPoint.y === startHip.endPoint.y &&
|
|
ridge.endPoint.x === endHip.endPoint.x &&
|
|
ridge.endPoint.y === endHip.endPoint.y) ||
|
|
(ridge.startPoint.x === endHip.endPoint.x &&
|
|
ridge.startPoint.y === endHip.endPoint.y &&
|
|
ridge.endPoint.x === startHip.endPoint.x &&
|
|
ridge.endPoint.y === startHip.endPoint.y),
|
|
)
|
|
} else {
|
|
connectedRidge = ridges.find(
|
|
(ridge) =>
|
|
(ridge.startPoint.x === startHip.endPoint.x && ridge.startPoint.y === startHip.endPoint.y) ||
|
|
(ridge.endPoint.x === startHip.endPoint.x && ridge.endPoint.y === startHip.endPoint.y),
|
|
)
|
|
}
|
|
|
|
const hipStartPoint = startHip.endPoint
|
|
const hipEndPoint = endHip.endPoint
|
|
|
|
if (connectedRidge.startPoint.x === hipStartPoint.x && connectedRidge.startPoint.y === hipStartPoint.y) {
|
|
if (connectedRidge.endPoint.x === hipEndPoint.x && connectedRidge.endPoint.y === hipEndPoint.y) {
|
|
polygonPoints.push(connectedRidge.endPoint)
|
|
polygonPoints.push(connectedRidge.startPoint)
|
|
|
|
const newPolygon = new QPolygon(polygonPoints, {
|
|
fontSize: polygon.fontSize,
|
|
parentId: polygon.id,
|
|
name: 'roof',
|
|
selectable: false,
|
|
stroke: 'black',
|
|
fill: 'transparent',
|
|
strokeWidth: 3,
|
|
})
|
|
|
|
polygon.canvas.add(newPolygon)
|
|
return
|
|
}
|
|
} else if (connectedRidge.endPoint.x === hipStartPoint.x && connectedRidge.endPoint.y === hipStartPoint.y) {
|
|
if (connectedRidge.startPoint.x === hipEndPoint.x && connectedRidge.startPoint.y === hipEndPoint.y) {
|
|
polygonPoints.push(connectedRidge.startPoint)
|
|
polygonPoints.push(connectedRidge.endPoint)
|
|
|
|
const newPolygon = new QPolygon(polygonPoints, {
|
|
fontSize: polygon.fontSize,
|
|
parentId: polygon.id,
|
|
name: 'roof',
|
|
selectable: false,
|
|
stroke: 'black',
|
|
fill: 'transparent',
|
|
strokeWidth: 3,
|
|
sort: true,
|
|
})
|
|
|
|
polygon.canvas.add(newPolygon)
|
|
return
|
|
}
|
|
}
|
|
|
|
// 지붕이 꺾여있는 경우
|
|
|
|
if (
|
|
(restRidgeConnection.startPoint.x === startHip.endPoint.x && restRidgeConnection.startPoint.y === startHip.endPoint.y) ||
|
|
(restRidgeConnection.endPoint.x === startHip.endPoint.x && restRidgeConnection.endPoint.y === startHip.endPoint.y)
|
|
) {
|
|
polygonPoints = [startPoint, startHip.endPoint]
|
|
let lastPoint
|
|
|
|
if (restRidgeConnection.startPoint.x === startHip.endPoint.x && restRidgeConnection.startPoint.y === startHip.endPoint.y) {
|
|
lastPoint = restRidgeConnection.endPoint
|
|
polygonPoints.push(restRidgeConnection.endPoint)
|
|
} else {
|
|
lastPoint = restRidgeConnection.startPoint
|
|
polygonPoints.push(restRidgeConnection.startPoint)
|
|
}
|
|
|
|
connectedRidge = ridges.find(
|
|
(ridge) =>
|
|
(ridge.startPoint.x === lastPoint.x && ridge.startPoint.y === lastPoint.y) ||
|
|
(ridge.endPoint.x === lastPoint.x && ridge.endPoint.y === lastPoint.y),
|
|
)
|
|
|
|
if (connectedRidge.startPoint.x === lastPoint.x && connectedRidge.startPoint.y === lastPoint.y) {
|
|
polygonPoints.push(connectedRidge.endPoint)
|
|
} else {
|
|
polygonPoints.push(connectedRidge.startPoint)
|
|
}
|
|
|
|
polygonPoints.push(endPoint)
|
|
} else {
|
|
polygonPoints = [endPoint, endHip.endPoint]
|
|
let lastPoint
|
|
|
|
if (restRidgeConnection.startPoint.x === endHip.endPoint.x && restRidgeConnection.startPoint.y === endHip.endPoint.y) {
|
|
lastPoint = restRidgeConnection.endPoint
|
|
polygonPoints.push(restRidgeConnection.endPoint)
|
|
} else {
|
|
lastPoint = restRidgeConnection.startPoint
|
|
polygonPoints.push(restRidgeConnection.startPoint)
|
|
}
|
|
|
|
connectedRidge = ridges.find(
|
|
(ridge) =>
|
|
(ridge.startPoint.x === lastPoint.x && ridge.startPoint.y === lastPoint.y) ||
|
|
(ridge.endPoint.x === lastPoint.x && ridge.endPoint.y === lastPoint.y),
|
|
)
|
|
|
|
if (connectedRidge.startPoint.x === startHip.endPoint.x && connectedRidge.startPoint.y === startHip.endPoint.y) {
|
|
lastPoint = connectedRidge.startPoint
|
|
polygonPoints.push(connectedRidge.startPoint)
|
|
} else {
|
|
lastPoint = connectedRidge.endPoint
|
|
polygonPoints.push(connectedRidge.endPoint)
|
|
}
|
|
|
|
polygonPoints.push(startPoint)
|
|
}
|
|
|
|
const newPolygon = new QPolygon(polygonPoints, {
|
|
fontSize: polygon.fontSize,
|
|
parentId: polygon.id,
|
|
name: 'roof',
|
|
selectable: false,
|
|
stroke: 'black',
|
|
fill: 'transparent',
|
|
strokeWidth: 3,
|
|
})
|
|
|
|
polygon.canvas.add(newPolygon)
|
|
})
|
|
}
|
|
|
|
const getOneSideLine = (line) => {
|
|
// left, top 방향의 line은 right, bottom 방향의 line으로 변경한다.
|
|
const newLine = { ...line }
|
|
let newX1, newY1, newX2, newY2
|
|
if (newLine.direction === 'top') {
|
|
newX1 = newLine.x2
|
|
newY1 = newLine.y2
|
|
newX2 = newLine.x1
|
|
newY2 = newLine.y1
|
|
|
|
newLine.x1 = newX1
|
|
newLine.y1 = newY1
|
|
newLine.x2 = newX2
|
|
newLine.y2 = newY2
|
|
newLine.direction = 'bottom'
|
|
newLine.startPoint = { x: newX1, y: newY1 }
|
|
newLine.endPoint = { x: newX2, y: newY2 }
|
|
} else if (line.direction === 'left') {
|
|
newX1 = newLine.x2
|
|
newY1 = newLine.y2
|
|
newX2 = newLine.x1
|
|
newY2 = newLine.y1
|
|
|
|
newLine.x1 = newX1
|
|
newLine.y1 = newY1
|
|
newLine.x2 = newX2
|
|
newLine.y2 = newY2
|
|
newLine.direction = 'right'
|
|
newLine.startPoint = { x: newX1, y: newY1 }
|
|
newLine.endPoint = { x: newX2, y: newY2 }
|
|
}
|
|
|
|
return newLine
|
|
}
|
|
|
|
function inwardEdgeNormal(vertex1, vertex2) {
|
|
// Assuming that polygon vertices are in clockwise order
|
|
const dx = vertex2.x - vertex1.x
|
|
const dy = vertex2.y - vertex1.y
|
|
const edgeLength = Math.sqrt(dx * dx + dy * dy)
|
|
|
|
return {
|
|
x: -dy / edgeLength,
|
|
y: dx / edgeLength,
|
|
}
|
|
}
|
|
|
|
function outwardEdgeNormal(vertex1, vertex2) {
|
|
var n = inwardEdgeNormal(vertex1, vertex2)
|
|
|
|
return {
|
|
x: -n.x,
|
|
y: -n.y,
|
|
}
|
|
}
|
|
|
|
function createPolygon(vertices) {
|
|
const edges = []
|
|
let minX = vertices.length > 0 ? vertices[0].x : undefined
|
|
let minY = vertices.length > 0 ? vertices[0].y : undefined
|
|
let maxX = minX
|
|
let maxY = minY
|
|
|
|
for (let i = 0; i < vertices.length; i++) {
|
|
const vertex1 = vertices[i]
|
|
const vertex2 = vertices[(i + 1) % vertices.length]
|
|
|
|
const outwardNormal = outwardEdgeNormal(vertex1, vertex2)
|
|
|
|
const inwardNormal = inwardEdgeNormal(vertex1, vertex2)
|
|
|
|
const edge = {
|
|
vertex1,
|
|
vertex2,
|
|
index: i,
|
|
outwardNormal,
|
|
inwardNormal,
|
|
}
|
|
|
|
edges.push(edge)
|
|
|
|
const x = vertices[i].x
|
|
const y = vertices[i].y
|
|
minX = Math.min(x, minX)
|
|
minY = Math.min(y, minY)
|
|
maxX = Math.max(x, maxX)
|
|
maxY = Math.max(y, maxY)
|
|
}
|
|
|
|
return {
|
|
vertices,
|
|
edges,
|
|
minX,
|
|
minY,
|
|
maxX,
|
|
maxY,
|
|
}
|
|
}
|
|
|
|
// based on http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/, edgeA => "line a", edgeB => "line b"
|
|
|
|
function edgesIntersection(edgeA, edgeB) {
|
|
const den =
|
|
(edgeB.vertex2.y - edgeB.vertex1.y) * (edgeA.vertex2.x - edgeA.vertex1.x) -
|
|
(edgeB.vertex2.x - edgeB.vertex1.x) * (edgeA.vertex2.y - edgeA.vertex1.y)
|
|
|
|
if (den == 0) {
|
|
return null // lines are parallel or coincident
|
|
}
|
|
|
|
const ua =
|
|
((edgeB.vertex2.x - edgeB.vertex1.x) * (edgeA.vertex1.y - edgeB.vertex1.y) -
|
|
(edgeB.vertex2.y - edgeB.vertex1.y) * (edgeA.vertex1.x - edgeB.vertex1.x)) /
|
|
den
|
|
|
|
const ub =
|
|
((edgeA.vertex2.x - edgeA.vertex1.x) * (edgeA.vertex1.y - edgeB.vertex1.y) -
|
|
(edgeA.vertex2.y - edgeA.vertex1.y) * (edgeA.vertex1.x - edgeB.vertex1.x)) /
|
|
den
|
|
|
|
// Edges are not intersecting but the lines defined by them are
|
|
const isIntersectionOutside = ua < 0 || ub < 0 || ua > 1 || ub > 1
|
|
|
|
return {
|
|
x: edgeA.vertex1.x + ua * (edgeA.vertex2.x - edgeA.vertex1.x),
|
|
y: edgeA.vertex1.y + ua * (edgeA.vertex2.y - edgeA.vertex1.y),
|
|
isIntersectionOutside,
|
|
}
|
|
}
|
|
|
|
function appendArc(arcSegments, vertices, center, radius, startVertex, endVertex, isPaddingBoundary) {
|
|
var startAngle = Math.atan2(startVertex.y - center.y, startVertex.x - center.x)
|
|
var endAngle = Math.atan2(endVertex.y - center.y, endVertex.x - center.x)
|
|
|
|
if (startAngle < 0) {
|
|
startAngle += TWO_PI
|
|
}
|
|
|
|
if (endAngle < 0) {
|
|
endAngle += TWO_PI
|
|
}
|
|
|
|
const angle = startAngle > endAngle ? startAngle - endAngle : startAngle + TWO_PI - endAngle
|
|
const angleStep = (isPaddingBoundary ? -angle : TWO_PI - angle) / arcSegments
|
|
|
|
vertices.push(startVertex)
|
|
|
|
for (let i = 1; i < arcSegments; ++i) {
|
|
const angle = startAngle + angleStep * i
|
|
|
|
const vertex = {
|
|
x: center.x + Math.cos(angle) * radius,
|
|
y: center.y + Math.sin(angle) * radius,
|
|
}
|
|
|
|
vertices.push(vertex)
|
|
}
|
|
|
|
vertices.push(endVertex)
|
|
}
|
|
|
|
function createOffsetEdge(edge, dx, dy) {
|
|
return {
|
|
vertex1: {
|
|
x: edge.vertex1.x + dx,
|
|
y: edge.vertex1.y + dy,
|
|
},
|
|
vertex2: {
|
|
x: edge.vertex2.x + dx,
|
|
y: edge.vertex2.y + dy,
|
|
},
|
|
}
|
|
}
|
|
|
|
function createMarginPolygon(polygon, offset, arcSegments = 0) {
|
|
const offsetEdges = []
|
|
|
|
for (let i = 0; i < polygon.edges.length; i++) {
|
|
const edge = polygon.edges[i]
|
|
const dx = edge.outwardNormal.x * offset
|
|
const dy = edge.outwardNormal.y * offset
|
|
offsetEdges.push(createOffsetEdge(edge, dx, dy))
|
|
}
|
|
|
|
const vertices = []
|
|
|
|
for (let i = 0; i < offsetEdges.length; i++) {
|
|
const thisEdge = offsetEdges[i]
|
|
const prevEdge = offsetEdges[(i + offsetEdges.length - 1) % offsetEdges.length]
|
|
const vertex = edgesIntersection(prevEdge, thisEdge)
|
|
|
|
if (vertex && (!vertex.isIntersectionOutside || arcSegments < 1)) {
|
|
vertices.push({
|
|
x: vertex.x,
|
|
y: vertex.y,
|
|
})
|
|
} else {
|
|
const arcCenter = polygon.edges[i].vertex1
|
|
|
|
appendArc(arcSegments, vertices, arcCenter, offset, prevEdge.vertex2, thisEdge.vertex1, false)
|
|
}
|
|
}
|
|
|
|
const marginPolygon = createPolygon(vertices)
|
|
|
|
marginPolygon.offsetEdges = offsetEdges
|
|
|
|
return marginPolygon
|
|
}
|
|
|
|
function createPaddingPolygon(polygon, offset, arcSegments = 0) {
|
|
const offsetEdges = []
|
|
|
|
for (let i = 0; i < polygon.edges.length; i++) {
|
|
const edge = polygon.edges[i]
|
|
const dx = edge.inwardNormal.x * offset
|
|
const dy = edge.inwardNormal.y * offset
|
|
offsetEdges.push(createOffsetEdge(edge, dx, dy))
|
|
}
|
|
|
|
const vertices = []
|
|
|
|
for (let i = 0; i < offsetEdges.length; i++) {
|
|
const thisEdge = offsetEdges[i]
|
|
const prevEdge = offsetEdges[(i + offsetEdges.length - 1) % offsetEdges.length]
|
|
const vertex = edgesIntersection(prevEdge, thisEdge)
|
|
if (vertex && (!vertex.isIntersectionOutside || arcSegments < 1)) {
|
|
vertices.push({
|
|
x: vertex.x,
|
|
y: vertex.y,
|
|
})
|
|
} else {
|
|
const arcCenter = polygon.edges[i].vertex1
|
|
|
|
appendArc(arcSegments, vertices, arcCenter, offset, prevEdge.vertex2, thisEdge.vertex1, true)
|
|
}
|
|
}
|
|
|
|
const paddingPolygon = createPolygon(vertices)
|
|
|
|
paddingPolygon.offsetEdges = offsetEdges
|
|
|
|
return paddingPolygon
|
|
}
|
|
|
|
export default function offsetPolygon(vertices, offset) {
|
|
const polygon = createPolygon(vertices)
|
|
const arcSegments = 0
|
|
|
|
const originPolygon = new QPolygon(vertices, { fontSize: 0 })
|
|
|
|
if (offset > 0) {
|
|
let result = createMarginPolygon(polygon, offset, arcSegments).vertices
|
|
const allPointsOutside = result.every((point) => !originPolygon.inPolygon(point))
|
|
|
|
if (allPointsOutside) {
|
|
return createMarginPolygon(polygon, offset, arcSegments).vertices
|
|
} else {
|
|
return createPaddingPolygon(polygon, offset, arcSegments).vertices
|
|
}
|
|
} else {
|
|
let result = createPaddingPolygon(polygon, offset, arcSegments).vertices
|
|
const allPointsInside = result.every((point) => originPolygon.inPolygon(point))
|
|
|
|
if (allPointsInside) {
|
|
return createPaddingPolygon(polygon, offset, arcSegments).vertices
|
|
} else {
|
|
return createMarginPolygon(polygon, offset, arcSegments).vertices
|
|
}
|
|
}
|
|
}
|
|
|
|
export const splitPolygonWithLines = (polygon) => {
|
|
const roofs = []
|
|
const allLines = [...polygon.innerLines]
|
|
|
|
allLines.forEach((line) => {
|
|
line.startPoint = { x: line.x1, y: line.y1 }
|
|
line.endPoint = { x: line.x2, y: line.y2 }
|
|
})
|
|
|
|
// allLines에 x1,y1,x2,y2를 비교해서 중복되는 값을 제거한다.
|
|
allLines.forEach((line, index) => {
|
|
const startPoint = line.startPoint
|
|
const endPoint = line.endPoint
|
|
|
|
allLines.forEach((line2, index2) => {
|
|
if (index !== index2) {
|
|
if (
|
|
(isSamePoint(startPoint, line2.startPoint) && isSamePoint(endPoint, line2.endPoint)) ||
|
|
(isSamePoint(endPoint, line2.startPoint) && isSamePoint(startPoint, line2.endPoint))
|
|
) {
|
|
allLines.splice(index2, 1)
|
|
}
|
|
}
|
|
})
|
|
})
|
|
|
|
/**
|
|
* 좌표 테스트용
|
|
*/
|
|
/*allLines.forEach((line) => {
|
|
const text = new fabric.Text(`(${line.startPoint.x},${line.startPoint.y})`, {
|
|
left: line.startPoint.x,
|
|
top: line.startPoint.y,
|
|
fontSize: 15,
|
|
})
|
|
|
|
polygon.canvas.add(text)
|
|
polygon.canvas.renderAll()
|
|
|
|
const text2 = new fabric.Text(`(${line.endPoint.x},${line.endPoint.y})`, {
|
|
left: line.endPoint.x,
|
|
top: line.endPoint.y,
|
|
fontSize: 15,
|
|
})
|
|
|
|
polygon.canvas.add(text2)
|
|
polygon.canvas.renderAll()
|
|
})
|
|
|
|
polygon.points.forEach((point, index) => {
|
|
const text = new fabric.Text(`(${point.x},${point.y})`, {
|
|
left: point.x,
|
|
top: point.y,
|
|
fontSize: 15,
|
|
})
|
|
|
|
polygon.canvas.add(text)
|
|
polygon.canvas.renderAll()
|
|
})*/
|
|
/**
|
|
* 좌표 테스트용 끝
|
|
*/
|
|
|
|
polygon.points.forEach((point, index) => {
|
|
allLines.forEach((line) => {
|
|
if (line.endPoint.x === point.x && line.endPoint.y === point.y) {
|
|
const temp = line.startPoint
|
|
line.startPoint = line.endPoint
|
|
line.endPoint = temp
|
|
}
|
|
})
|
|
})
|
|
|
|
polygon.points.forEach((point, index) => {
|
|
const routes = []
|
|
|
|
// 시작점은 시작 hip라인의 출발점
|
|
const startPoint = point
|
|
// 도착점은 마지막 hip라인의 끝나는 점
|
|
const endPoint = polygon.points[(index + 1) % polygon.points.length]
|
|
|
|
const startLine = allLines.find((line) => line.startPoint.x === startPoint.x && line.startPoint.y === startPoint.y)
|
|
const endLine = allLines.find((line) => line.startPoint.x === endPoint.x && line.startPoint.y === endPoint.y)
|
|
|
|
const arrivalPoint = endLine.endPoint
|
|
routes.push(startLine.startPoint)
|
|
routes.push(startLine.endPoint)
|
|
|
|
//hip끼리 만나는 경우는 아무것도 안해도됨
|
|
if (!isSamePoint(startLine.endPoint, arrivalPoint)) {
|
|
// polygon line까지 추가
|
|
const allLinesCopy = [...allLines, ...polygon.lines]
|
|
// hip이 만나지 않는 경우 갈 수 있는 길을 다 돌아야함
|
|
let currentPoint = startLine.endPoint
|
|
let currentLine = startLine
|
|
let movedLines = []
|
|
let subMovedLines = []
|
|
while (!isSamePoint(currentPoint, arrivalPoint)) {
|
|
// startHip에서 만나는 출발선 두개. 두개의 선을 출발하여 arrivalPoint에 도착할 때 까지 count를 세고, 더 낮은 count를 가진 길을 선택한다.
|
|
let connectedLines = allLinesCopy.filter((line) => isSamePoint(line.startPoint, currentPoint) || isSamePoint(line.endPoint, currentPoint))
|
|
|
|
connectedLines = connectedLines.filter((line) => line !== currentLine)
|
|
|
|
connectedLines = connectedLines.filter((line) => !subMovedLines.includes(line))
|
|
|
|
//마지막 선이 endLine의 startPoint와 같은경우 그 전까지 movedLine을 제거한다.
|
|
const endLineMeetLineCnt = connectedLines.filter((line) => {
|
|
return isSamePoint(line.endPoint, endLine.startPoint) || isSamePoint(line.startPoint, endLine.startPoint)
|
|
}).length
|
|
|
|
if (endLineMeetLineCnt !== 0) {
|
|
movedLines.push(subMovedLines)
|
|
|
|
console.log(movedLines, index)
|
|
}
|
|
|
|
connectedLines = connectedLines.filter((line) => {
|
|
return !isSamePoint(line.endPoint, endLine.startPoint) && !isSamePoint(line.startPoint, endLine.startPoint)
|
|
})
|
|
|
|
if (connectedLines.length === 0) {
|
|
return
|
|
}
|
|
|
|
let tempPoints = []
|
|
|
|
for (let i = 0; i < connectedLines.length; i++) {
|
|
if (isSamePoint(connectedLines[i].startPoint, currentPoint)) {
|
|
tempPoints.push({ point: connectedLines[i].endPoint, index: i, line: connectedLines[i] })
|
|
} else {
|
|
tempPoints.push({ point: connectedLines[i].startPoint, index: i, line: connectedLines[i] })
|
|
}
|
|
}
|
|
|
|
//tempPoints에서 arrivalPoint와 가장 가까운 점을 찾는다.
|
|
let minDistance = Number.MAX_SAFE_INTEGER
|
|
let minIndex = 0
|
|
tempPoints.forEach((tempPoint, index) => {
|
|
const distance = Math.sqrt(Math.pow(tempPoint.point.x - arrivalPoint.x, 2) + Math.pow(tempPoint.point.y - arrivalPoint.y, 2))
|
|
if (distance < minDistance) {
|
|
minDistance = distance
|
|
minIndex = tempPoint.index
|
|
}
|
|
})
|
|
|
|
currentPoint = tempPoints[minIndex].point
|
|
currentLine = tempPoints[minIndex].line
|
|
if (currentLine !== startLine) {
|
|
subMovedLines.push(currentLine)
|
|
}
|
|
routes.push(currentPoint)
|
|
}
|
|
}
|
|
|
|
routes.push(endLine.startPoint)
|
|
roofs.push(routes)
|
|
})
|
|
|
|
// 중복 제거
|
|
roofs.forEach((roofPoint, index) => {
|
|
const samePointLengthRoofPoints = roofs.filter((roof) => roof.length === roofPoint.length && roof !== roofPoint)
|
|
|
|
samePointLengthRoofPoints.forEach((samePointRoof) => {
|
|
if (arraysHaveSamePoints(samePointRoof, roofPoint)) {
|
|
roofs.splice(roofs.indexOf(samePointRoof), 1)
|
|
}
|
|
})
|
|
})
|
|
|
|
roofs.forEach((roofPoint, index) => {
|
|
let defense
|
|
const direction = getDirectionByPoint(roofPoint[0], roofPoint[roofPoint.length - 1])
|
|
|
|
switch (direction) {
|
|
case 'top':
|
|
defense = 'east'
|
|
break
|
|
case 'right':
|
|
defense = 'south'
|
|
break
|
|
case 'bottom':
|
|
defense = 'west'
|
|
break
|
|
case 'left':
|
|
defense = 'north'
|
|
break
|
|
}
|
|
|
|
const roof = new QPolygon(roofPoint, {
|
|
fontSize: polygon.fontSize,
|
|
stroke: 'black',
|
|
fill: 'transparent',
|
|
strokeWidth: 3,
|
|
name: POLYGON_TYPE.ROOF,
|
|
selectable: true,
|
|
defense: defense,
|
|
})
|
|
|
|
polygon.canvas.add(roof)
|
|
polygon.canvas.renderAll()
|
|
})
|
|
}
|
|
|
|
const isSamePoint = (a, b) => {
|
|
return a.x === b.x && a.y === b.y
|
|
}
|
|
|
|
/**
|
|
* Calculate the angle between two lines.
|
|
* @param {Object} line1 - The first line defined by two points {x1, y1} and {x2, y2}.
|
|
* @param {Object} line2 - The second line defined by two points {x1, y1} and {x2, y2}.
|
|
* @returns {number} - The angle between the two lines in degrees.
|
|
*/
|
|
function calculateAngleBetweenLines(line1, line2) {
|
|
const { x1: x1_1, y1: y1_1, x2: x2_1, y2: y2_1 } = line1
|
|
const { x1: x1_2, y1: y1_2, x2: x2_2, y2: y2_2 } = line2
|
|
|
|
// Calculate direction vectors
|
|
const vector1 = { x: x2_1 - x1_1, y: y2_1 - y1_1 }
|
|
const vector2 = { x: x2_2 - x1_2, y: y2_2 - y1_2 }
|
|
|
|
// Calculate dot product and magnitudes
|
|
const dotProduct = vector1.x * vector2.x + vector1.y * vector2.y
|
|
const magnitude1 = Math.sqrt(vector1.x * vector1.x + vector1.y * vector1.y)
|
|
const magnitude2 = Math.sqrt(vector2.x * vector2.x + vector2.y * vector2.y)
|
|
|
|
// Calculate the cosine of the angle
|
|
const cosTheta = dotProduct / (magnitude1 * magnitude2)
|
|
|
|
// Calculate the angle in radians and then convert to degrees
|
|
const angleInRadians = Math.acos(cosTheta)
|
|
|
|
return (angleInRadians * 180) / Math.PI
|
|
}
|
|
|
|
export const drawHippedRoof = (polygon, chon) => {
|
|
const hasNonParallelLines = polygon.lines.filter((line) => line.x1 !== line.x2 && line.y1 !== line.y2)
|
|
if (hasNonParallelLines.length > 0) {
|
|
alert('대각선이 존재합니다.')
|
|
return
|
|
}
|
|
|
|
drawRidgeRoof(polygon, chon)
|
|
drawHips(polygon)
|
|
connectLinePoint(polygon)
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param polygon
|
|
* @param chon
|
|
*/
|
|
const drawRidgeRoof = (polygon, chon) => {
|
|
const walls = polygon.wall.lines // 외벽의 라인
|
|
const roofs = polygon.lines // 지붕의 라인
|
|
let ridgeRoof = []
|
|
|
|
roofs.forEach((currentRoof, index) => {
|
|
let prevRoof,
|
|
nextRoof,
|
|
currentWall = walls[index]
|
|
|
|
prevRoof = index === 0 ? walls[walls.length - 1] : walls[index - 1]
|
|
nextRoof = index === walls.length - 1 ? walls[0] : index === walls.length ? walls[1] : walls[index + 1]
|
|
|
|
if (prevRoof.direction !== nextRoof.direction && currentWall.length <= currentRoof.length) {
|
|
ridgeRoof.push({ index: index, roof: currentRoof, length: currentRoof.length })
|
|
}
|
|
})
|
|
|
|
// 지붕의 길이가 짧은 순으로 정렬
|
|
ridgeRoof.sort((a, b) => a.length - b.length)
|
|
|
|
ridgeRoof.forEach((item) => {
|
|
if (getMaxRidge(roofs.length) > polygon.ridges.length) {
|
|
let index = item.index,
|
|
beforePrevRoof,
|
|
prevRoof,
|
|
currentRoof = item.roof,
|
|
nextRoof,
|
|
afterNextRoof
|
|
let startXPoint, startYPoint, endXPoint, endYPoint
|
|
|
|
prevRoof = index === 0 ? roofs[walls.length - 1] : roofs[index - 1]
|
|
nextRoof = index === roofs.length - 1 ? roofs[0] : index === roofs.length ? roofs[1] : roofs[index + 1]
|
|
|
|
beforePrevRoof = index <= 1 ? roofs[roofs.length - 2 + index] : roofs[index - 2]
|
|
afterNextRoof = index >= roofs.length - 2 ? roofs[(index + 2) % roofs.length] : roofs[index + 2]
|
|
|
|
const anotherRoof = roofs.filter((roof) => roof !== currentRoof && roof !== prevRoof && roof !== nextRoof)
|
|
|
|
let xEqualInnerLines = anotherRoof.filter((roof) => roof.x1 === roof.x2 && isInnerLine(prevRoof, currentRoof, nextRoof, roof)), //x가 같은 내부선
|
|
yEqualInnerLines = anotherRoof.filter((roof) => roof.y1 === roof.y2 && isInnerLine(prevRoof, currentRoof, nextRoof, roof)) //y가 같은 내부선
|
|
|
|
let ridgeBaseLength = currentRoof.length / 2, // 지붕의 기반 길이
|
|
ridgeMaxLength = Math.min(prevRoof.length, nextRoof.length), // 지붕의 최대 길이. 이전, 다음 벽 중 짧은 길이
|
|
ridgeAcrossLength = Math.max(prevRoof.length, nextRoof.length) - currentRoof.length // 맞은편 벽까지의 길이 - 지붕의 기반 길이
|
|
|
|
let acrossRoof = anotherRoof
|
|
.filter((roof) => {
|
|
if (roof.x1 === roof.x2) {
|
|
if ((nextRoof.direction === 'right' && roof.x1 > currentRoof.x1) || (nextRoof.direction === 'left' && roof.x1 < currentRoof.x1)) {
|
|
return roof
|
|
}
|
|
}
|
|
if (roof.y1 === roof.y2) {
|
|
if ((nextRoof.direction === 'top' && roof.y1 < currentRoof.y1) || (nextRoof.direction === 'bottom' && roof.y1 > currentRoof.y1)) {
|
|
return roof
|
|
}
|
|
}
|
|
})
|
|
.reduce((prev, current) => {
|
|
let hasBetweenRoof = false
|
|
if (current.x1 === current.x2) {
|
|
hasBetweenRoof = roofs
|
|
.filter((roof) => roof !== current && roof !== currentRoof)
|
|
.some((line) => {
|
|
let currentY2 = currentRoof.y2
|
|
if (yEqualInnerLines.length > 0) {
|
|
yEqualInnerLines.forEach((line) => {
|
|
currentY2 = Math.abs(currentRoof.y1 - currentY2) < Math.abs(currentRoof.y1 - line.y1) ? currentY2 : line.y1
|
|
})
|
|
}
|
|
const isY1Between = (line.y1 > currentRoof.y1 && line.y1 < currentY2) || (line.y1 > currentY2 && line.y1 < currentRoof.y1)
|
|
const isY2Between = (line.y2 > currentRoof.y1 && line.y2 < currentY2) || (line.y2 > currentY2 && line.y2 < currentRoof.y1)
|
|
const isX1Between = (line.x1 > currentRoof.x1 && line.x1 < current.x1) || (line.x1 > currentRoof.x1 && line.x1 < current.x1)
|
|
const isX2Between = (line.x2 > currentRoof.x1 && line.x2 < current.x1) || (line.x2 > currentRoof.x1 && line.x2 < current.x1)
|
|
return isY1Between && isY2Between && isX1Between && isX2Between
|
|
})
|
|
}
|
|
if (current.y1 === current.y2) {
|
|
hasBetweenRoof = walls
|
|
.filter((roof) => roof !== current && roof !== currentRoof)
|
|
.some((line) => {
|
|
let currentX2 = currentRoof.x2
|
|
if (xEqualInnerLines.length > 0) {
|
|
xEqualInnerLines.forEach((line) => {
|
|
currentX2 = Math.abs(currentRoof.x1 - currentX2) < Math.abs(currentRoof.x1 - line.x1) ? currentX2 : line.x1
|
|
})
|
|
}
|
|
const isX1Between = (line.x1 > currentRoof.x1 && line.x1 < currentX2) || (line.x1 > currentX2 && line.x1 < currentRoof.x1)
|
|
const isX2Between = (line.x2 > currentRoof.x1 && line.x2 < currentX2) || (line.x2 > currentX2 && line.x2 < currentRoof.x1)
|
|
const isY1Between = (line.y1 > currentRoof.y1 && line.y1 < current.y1) || (line.y1 > currentRoof.y1 && line.y1 < current.y1)
|
|
const isY2Between = (line.y2 > currentRoof.y1 && line.y2 < current.y1) || (line.y2 > currentRoof.y1 && line.y2 < current.y1)
|
|
|
|
return isX1Between && isX2Between && isY1Between && isY2Between
|
|
})
|
|
}
|
|
|
|
if (prev !== undefined) {
|
|
if (currentRoof.x1 === currentRoof.x2) {
|
|
return Math.abs(currentRoof.y1 - prev.y1) > Math.abs(currentRoof.y1 - current.y1) ? prev : current
|
|
}
|
|
if (currentRoof.y1 === currentRoof.y2) {
|
|
return Math.abs(currentRoof.x1 - prev.x1) > Math.abs(currentRoof.x1 - current.x1) ? prev : current
|
|
}
|
|
} else {
|
|
if (!hasBetweenRoof) {
|
|
if (currentRoof.x1 === currentRoof.x2) {
|
|
return Math.sign(currentRoof.y1 - currentRoof.y2) !== Math.sign(current.y1 - current.y2) ? current : undefined
|
|
}
|
|
if (currentRoof.y1 === currentRoof.y2) {
|
|
return Math.sign(currentRoof.x1 - currentRoof.x2) !== Math.sign(current.x1 - current.x2) ? current : undefined
|
|
}
|
|
return undefined
|
|
} else {
|
|
return undefined
|
|
}
|
|
}
|
|
}, undefined)
|
|
|
|
if (acrossRoof !== undefined) {
|
|
if (currentRoof.x1 === currentRoof.x2) {
|
|
if (ridgeAcrossLength < Math.abs(currentRoof.x1 - acrossRoof.x1)) {
|
|
ridgeAcrossLength = Math.abs(currentRoof.x1 - acrossRoof.x1) - currentRoof.length
|
|
}
|
|
}
|
|
if (currentRoof.y1 === currentRoof.y2) {
|
|
if (ridgeAcrossLength < Math.abs(currentRoof.y1 - acrossRoof.y1)) {
|
|
ridgeAcrossLength = Math.abs(currentRoof.y1 - acrossRoof.y1) - currentRoof.length
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ridgeBaseLength > 0 && ridgeMaxLength > 0 && ridgeAcrossLength > 0) {
|
|
let ridgeLength = Math.min(ridgeMaxLength, ridgeAcrossLength)
|
|
if (currentRoof.x1 === currentRoof.x2) {
|
|
startXPoint = currentRoof.x1 + (nextRoof.direction === 'right' ? 1 : -1) * ridgeBaseLength
|
|
startYPoint = currentRoof.y1 + (currentRoof.direction === 'top' ? -1 : 1) * ridgeBaseLength
|
|
endXPoint = startXPoint + (nextRoof.direction === 'right' ? 1 : -1) * ridgeLength
|
|
endYPoint = startYPoint
|
|
let adjustY
|
|
if (currentRoof.direction === 'top') {
|
|
if (afterNextRoof.direction === 'bottom' && beforePrevRoof.direction === 'bottom') {
|
|
adjustY =
|
|
Math.abs(currentRoof.x1 - afterNextRoof.x1) < Math.abs(currentRoof.x1 - beforePrevRoof.x1) ? afterNextRoof.y2 : beforePrevRoof.y1
|
|
} else if (afterNextRoof.direction === 'bottom' && afterNextRoof.y2 > currentRoof.y2 && afterNextRoof.y2 < currentRoof.y1) {
|
|
adjustY = afterNextRoof.y2
|
|
} else if (beforePrevRoof.direction === 'bottom' && beforePrevRoof.y1 > currentRoof.y2 && beforePrevRoof.y1 < currentRoof.y1) {
|
|
adjustY = beforePrevRoof.y1
|
|
}
|
|
if (adjustY) {
|
|
startYPoint = currentRoof.y1 - Math.abs(currentRoof.y1 - adjustY) / 2
|
|
endYPoint = startYPoint
|
|
}
|
|
}
|
|
if (currentRoof.direction === 'bottom') {
|
|
if (afterNextRoof.direction === 'top' && beforePrevRoof.direction === 'top') {
|
|
adjustY =
|
|
Math.abs(currentRoof.x1 - afterNextRoof.x1) < Math.abs(currentRoof.x1 - beforePrevRoof.x1) ? afterNextRoof.y2 : beforePrevRoof.y1
|
|
} else if (afterNextRoof.direction === 'top' && afterNextRoof.y2 < currentRoof.y2 && afterNextRoof.y2 > currentRoof.y1) {
|
|
adjustY = afterNextRoof.y2
|
|
} else if (beforePrevRoof.direction === 'top' && beforePrevRoof.y1 < currentRoof.y2 && beforePrevRoof.y1 > currentRoof.y1) {
|
|
adjustY = beforePrevRoof.y1
|
|
}
|
|
if (adjustY) {
|
|
startYPoint = currentRoof.y1 + Math.abs(currentRoof.y1 - adjustY) / 2
|
|
endYPoint = startYPoint
|
|
}
|
|
}
|
|
if (yEqualInnerLines.length > 0) {
|
|
yEqualInnerLines.reduce((prev, current) => {
|
|
if (prev !== undefined) {
|
|
return Math.abs(currentRoof.y1 - prev.y1) < Math.abs(currentRoof.y1 - current.y1) ? prev : current
|
|
} else {
|
|
return current
|
|
}
|
|
}, undefined)
|
|
startYPoint =
|
|
Math.abs(currentRoof.y1 - startYPoint) * 2 <= Math.abs(currentRoof.y1 - yEqualInnerLines[0].y1)
|
|
? startYPoint
|
|
: Math.abs(currentRoof.y1 - yEqualInnerLines[0].y1)
|
|
endYPoint = startYPoint
|
|
ridgeAcrossLength = Math.max(prevRoof.length, nextRoof.length) - Math.abs(currentRoof.y1 - startYPoint) * 2
|
|
if (
|
|
//yEqualInnerLines 이 다음 벽보다 안쪽에 있을때
|
|
Math.abs(currentRoof.y1 - yEqualInnerLines[0].y1) <= Math.abs(currentRoof.y1 - nextRoof.y1) &&
|
|
Math.abs(currentRoof.x1 - yEqualInnerLines[0].x2) >= Math.abs(currentRoof.x1 - nextRoof.x2)
|
|
) {
|
|
ridgeMaxLength = Math.abs(currentRoof.x1 - yEqualInnerLines[0].x2)
|
|
}
|
|
ridgeLength = Math.min(ridgeMaxLength, ridgeAcrossLength)
|
|
startXPoint = currentRoof.x1 + (nextRoof.direction === 'right' ? 1 : -1) * Math.abs(currentRoof.y1 - startYPoint)
|
|
endXPoint = startXPoint + (nextRoof.direction === 'right' ? 1 : -1) * ridgeLength
|
|
}
|
|
}
|
|
if (currentRoof.y1 === currentRoof.y2) {
|
|
startXPoint = currentRoof.x1 + (currentRoof.direction === 'left' ? -1 : 1) * ridgeBaseLength
|
|
startYPoint = currentRoof.y1 + (nextRoof.direction === 'bottom' ? 1 : -1) * ridgeBaseLength
|
|
endXPoint = startXPoint
|
|
endYPoint = startYPoint + (nextRoof.direction === 'bottom' ? 1 : -1) * ridgeLength
|
|
|
|
let adjustX
|
|
if (currentRoof.direction === 'right') {
|
|
if (afterNextRoof.direction === 'left' && beforePrevRoof.direction === 'left') {
|
|
adjustX =
|
|
Math.abs(currentRoof.y1 - afterNextRoof.y1) < Math.abs(currentRoof.y1 - beforePrevRoof.y1) ? afterNextRoof.x2 : beforePrevRoof.x1
|
|
} else if (afterNextRoof.direction === 'left' && afterNextRoof.x2 < currentRoof.x2 && afterNextRoof.x2 > currentRoof.x1) {
|
|
adjustX = afterNextRoof.x2
|
|
} else if (beforePrevRoof.direction === 'left' && beforePrevRoof.x1 < currentRoof.x2 && beforePrevRoof.x1 > currentRoof.x1) {
|
|
adjustX = beforePrevRoof.x1
|
|
}
|
|
if (adjustX) {
|
|
startXPoint = currentRoof.x1 + Math.abs(currentRoof.x1 - adjustX) / 2
|
|
endXPoint = startXPoint
|
|
}
|
|
}
|
|
if (currentRoof.direction === 'left') {
|
|
if (afterNextRoof.direction === 'right' && beforePrevRoof.direction === 'right') {
|
|
adjustX =
|
|
Math.abs(currentRoof.y1 - afterNextRoof.y1) < Math.abs(currentRoof.y1 - beforePrevRoof.y1) ? afterNextRoof.x2 : beforePrevRoof.x1
|
|
} else if (afterNextRoof.direction === 'right' && afterNextRoof.x2 > currentRoof.x2 && afterNextRoof.x2 < currentRoof.x1) {
|
|
adjustX = afterNextRoof.x2
|
|
} else if (beforePrevRoof.direction === 'right' && beforePrevRoof.x1 > currentRoof.x2 && beforePrevRoof.x1 < currentRoof.x1) {
|
|
adjustX = beforePrevRoof.x1
|
|
}
|
|
if (adjustX) {
|
|
startXPoint = currentRoof.x1 - Math.abs(currentRoof.x1 - adjustX) / 2
|
|
endXPoint = startXPoint
|
|
}
|
|
}
|
|
if (xEqualInnerLines.length > 0) {
|
|
xEqualInnerLines.reduce((prev, current) => {
|
|
if (prev !== undefined) {
|
|
return Math.abs(currentRoof.x1 - prev.x1) < Math.abs(currentRoof.x1 - current.x1) ? prev : current
|
|
} else {
|
|
return current
|
|
}
|
|
}, undefined)
|
|
startXPoint =
|
|
Math.abs(currentRoof.x1 - startXPoint) * 2 <= Math.abs(currentRoof.x1 - xEqualInnerLines[0].x1)
|
|
? startXPoint
|
|
: Math.abs(currentRoof.x1 - xEqualInnerLines[0].x1)
|
|
endXPoint = startXPoint
|
|
ridgeAcrossLength = Math.max(prevRoof.length, nextRoof.length) - Math.abs(currentRoof.x1 - startXPoint) * 2
|
|
if (
|
|
//xEqualInnerLines 이 다음 벽보다 안쪽에 있을때
|
|
Math.abs(currentRoof.x1 - xEqualInnerLines[0].x1) <= Math.abs(currentRoof.x1 - nextRoof.x1) &&
|
|
Math.abs(currentRoof.y1 - xEqualInnerLines[0].y2) >= Math.abs(currentRoof.y1 - nextRoof.y2)
|
|
) {
|
|
ridgeMaxLength = Math.abs(currentRoof.y1 - xEqualInnerLines[0].y2)
|
|
}
|
|
ridgeLength = Math.min(ridgeMaxLength, ridgeAcrossLength)
|
|
startYPoint = currentRoof.y1 + (nextRoof.direction === 'bottom' ? 1 : -1) * Math.abs(currentRoof.x1 - startXPoint)
|
|
endYPoint = startYPoint + (nextRoof.direction === 'bottom' ? 1 : -1) * ridgeLength
|
|
}
|
|
}
|
|
}
|
|
const currentWall = walls[index]
|
|
if (currentWall.attributes.type === 'gable') {
|
|
if (currentRoof.x1 === currentRoof.x2) {
|
|
startXPoint = currentRoof.x1
|
|
}
|
|
if (currentRoof.y1 === currentRoof.y2) {
|
|
startYPoint = currentRoof.y1
|
|
}
|
|
}
|
|
|
|
// 마루 그리기
|
|
if (startXPoint !== undefined && startYPoint !== undefined && endXPoint !== undefined && endYPoint !== undefined) {
|
|
const ridge = new QLine(
|
|
[Math.min(startXPoint, endXPoint), Math.min(startYPoint, endYPoint), Math.max(startXPoint, endXPoint), Math.max(startYPoint, endYPoint)],
|
|
{
|
|
fontSize: polygon.fontSize,
|
|
stroke: 'blue',
|
|
strokeWidth: 1,
|
|
name: 'ridgeLine',
|
|
},
|
|
)
|
|
polygon.canvas.add(ridge)
|
|
polygon.ridges.push(ridge)
|
|
polygon.innerLines.push(ridge)
|
|
}
|
|
}
|
|
})
|
|
|
|
//겹쳐지는 마루는 하나로 합침
|
|
polygon.ridges.forEach((ridge, index) => {
|
|
polygon.ridges
|
|
.filter((ridge2) => !(ridge.x1 === ridge2.x1 && ridge.y1 === ridge2.y1 && ridge.x2 === ridge2.x2 && ridge.y2 === ridge2.y2))
|
|
.forEach((ridge2) => {
|
|
let overlap = segmentsOverlap(ridge, ridge2)
|
|
if (overlap) {
|
|
let x1 = Math.min(ridge.x1, ridge2.x1, ridge.x2, ridge2.x2)
|
|
let x2 = Math.max(ridge.x1, ridge2.x1, ridge.x2, ridge2.x2)
|
|
let y1 = Math.min(ridge.y1, ridge2.y1, ridge.y2, ridge2.y2)
|
|
let y2 = Math.max(ridge.y1, ridge2.y1, ridge.y2, ridge2.y2)
|
|
const newRidge = new QLine([x1, y1, x2, y2], {
|
|
fontSize: polygon.fontSize,
|
|
stroke: 'blue',
|
|
strokeWidth: 1,
|
|
})
|
|
polygon.canvas.remove(ridge)
|
|
polygon.canvas.remove(ridge2)
|
|
polygon.ridges = polygon.ridges.filter((r) => !(ridge.x1 === r.x1 && ridge.y1 === r.y1 && ridge.x2 === r.x2 && ridge.y2 === r.y2))
|
|
polygon.ridges = polygon.ridges.filter((r) => !(ridge2.x1 === r.x1 && ridge2.y1 === r.y1 && ridge2.x2 === r.x2 && ridge2.y2 === r.y2))
|
|
polygon.innerLines = polygon.innerLines.filter((r) => !(ridge.x1 === r.x1 && ridge.y1 === r.y1 && ridge.x2 === r.x2 && ridge.y2 === r.y2))
|
|
polygon.innerLines = polygon.innerLines.filter(
|
|
(r) => !(ridge2.x1 === r.x1 && ridge2.y1 === r.y1 && ridge2.x2 === r.x2 && ridge2.y2 === r.y2),
|
|
)
|
|
|
|
polygon.canvas.add(newRidge)
|
|
polygon.ridges.push(newRidge)
|
|
polygon.innerLines.push(newRidge)
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
/**
|
|
* line 이 세 라인 사이에 존재하는지 확인한다.
|
|
* @param prevLine
|
|
* @param currentLine
|
|
* @param nextLine
|
|
* @param line
|
|
*/
|
|
const isInnerLine = (prevLine, currentLine, nextLine, line) => {
|
|
let inside = false
|
|
let minX = Math.min(currentLine.x1, currentLine.x2, prevLine.x1, nextLine.x2)
|
|
let maxX = Math.max(currentLine.x1, currentLine.x2, prevLine.x1, nextLine.x2)
|
|
let minY = Math.min(currentLine.y1, currentLine.y2, prevLine.y1, nextLine.y2)
|
|
let maxY = Math.max(currentLine.y1, currentLine.y2, prevLine.y1, nextLine.y2)
|
|
|
|
if (minX < line.x1 && line.x1 < maxX && minY < line.y1 && line.y1 < maxY && minX < line.x2 && line.x2 < maxX && minY < line.y2 && line.y2 < maxY) {
|
|
inside = true
|
|
}
|
|
|
|
return inside
|
|
}
|
|
|
|
/**
|
|
* 두 선분이 겹치는지 확인
|
|
* @param line1
|
|
* @param line2
|
|
* @returns {boolean}
|
|
*/
|
|
const segmentsOverlap = (line1, line2) => {
|
|
if (line1.y1 === line1.y2 && line2.y1 === line2.y2 && line1.y1 === line2.y1) {
|
|
if ((line1.x1 <= line2.x1 && line1.x2 >= line2.x1) || (line1.x1 <= line2.x2 && line1.x2 >= line2.x2)) {
|
|
return true
|
|
}
|
|
}
|
|
if (line1.x1 === line1.x2 && line2.x1 === line2.x2 && line1.x1 === line2.x1) {
|
|
if ((line1.y1 <= line2.y1 && line1.y2 >= line2.y1) || (line1.y1 <= line2.y2 && line1.y2 >= line2.y2)) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
const drawHips = (polygon) => {
|
|
/*
|
|
마루에서 시작되는 hip을 먼저 그립니다.
|
|
*/
|
|
polygon.ridges.forEach((ridge) => {
|
|
let leftTop, rightTop, leftBottom, rightBottom
|
|
if (ridge.y1 === ridge.y2) {
|
|
//왼쪽 좌표 기준 225, 315도 방향 라인확인
|
|
leftTop = polygon.lines
|
|
.filter((line) => line.x1 < ridge.x1 && line.y1 < ridge.y1 && Math.abs(line.x1 - ridge.x1) === Math.abs(line.y1 - ridge.y1))
|
|
.reduce((prev, current) => {
|
|
if (prev === undefined) {
|
|
return current
|
|
} else {
|
|
return Math.min(ridge.x1 - current.x1) < Math.min(ridge.x1 - prev.x1) ? current : prev
|
|
}
|
|
}, undefined)
|
|
|
|
leftBottom = polygon.lines
|
|
.filter((line) => line.x1 < ridge.x1 && line.y1 > ridge.y1 && Math.abs(line.x1 - ridge.x1) === Math.abs(line.y1 - ridge.y1))
|
|
.reduce((prev, current) => {
|
|
if (prev === undefined) {
|
|
return current
|
|
} else {
|
|
return Math.min(ridge.x1 - current.x1) < Math.min(ridge.x1 - prev.x1) ? current : prev
|
|
}
|
|
}, undefined)
|
|
|
|
//오른쪽 좌표 기준 45, 135도 방향 라인확인
|
|
rightTop = polygon.lines
|
|
.filter((line) => line.x1 > ridge.x2 && line.y1 < ridge.y2 && Math.abs(line.x1 - ridge.x2) === Math.abs(line.y1 - ridge.y2))
|
|
.reduce((prev, current) => {
|
|
if (prev === undefined) {
|
|
return current
|
|
} else {
|
|
return Math.min(current.x1 - ridge.x2) < Math.min(prev.x1 - ridge.x2) ? current : prev
|
|
}
|
|
}, undefined)
|
|
|
|
rightBottom = polygon.lines
|
|
.filter((line) => line.x1 > ridge.x2 && line.y1 > ridge.y2 && Math.abs(line.x1 - ridge.x2) === Math.abs(line.y1 - ridge.y2))
|
|
.reduce((prev, current) => {
|
|
if (prev === undefined) {
|
|
return current
|
|
} else {
|
|
return Math.min(current.x1 - ridge.x2) < Math.min(prev.x1 - ridge.x2) ? current : prev
|
|
}
|
|
}, undefined)
|
|
if (leftTop !== undefined) {
|
|
let isRidgePointOnLine = false
|
|
polygon.ridges.forEach((r) => {
|
|
if (
|
|
r.x1 < ridge.x1 &&
|
|
r.y1 < ridge.y1 &&
|
|
(r.y1 - rightTop.y1) * (ridge.x1 - rightTop.x1) === (r.x1 - rightTop.x1) * (ridge.y1 - rightTop.y1)
|
|
) {
|
|
isRidgePointOnLine = true
|
|
}
|
|
if (
|
|
r.x2 < ridge.x1 &&
|
|
r.y2 < ridge.y1 &&
|
|
(r.y2 - rightTop.y1) * (ridge.x1 - rightTop.x1) === (r.x2 - rightTop.x1) * (ridge.y1 - rightTop.y1)
|
|
) {
|
|
isRidgePointOnLine = true
|
|
}
|
|
})
|
|
if (!isRidgePointOnLine) {
|
|
const hip = new QLine([leftTop.x1, leftTop.y1, ridge.x1, ridge.y1], {
|
|
fontSize: polygon.fontSize,
|
|
stroke: 'red',
|
|
strokeWidth: 1,
|
|
name: 'hipLine',
|
|
})
|
|
polygon.canvas.add(hip)
|
|
polygon.hips.push(hip)
|
|
polygon.innerLines.push(hip)
|
|
}
|
|
}
|
|
if (leftBottom !== undefined) {
|
|
let isRidgePointOnLine = false
|
|
polygon.ridges.forEach((r) => {
|
|
if (
|
|
r.x1 < ridge.x1 &&
|
|
r.y1 > ridge.y1 &&
|
|
(r.y1 - rightTop.y1) * (ridge.x1 - rightTop.x1) === (r.x1 - rightTop.x1) * (ridge.y1 - rightTop.y1)
|
|
) {
|
|
isRidgePointOnLine = true
|
|
}
|
|
if (
|
|
r.x2 < ridge.x1 &&
|
|
r.y2 > ridge.y1 &&
|
|
(r.y2 - rightTop.y1) * (ridge.x1 - rightTop.x1) === (r.x2 - rightTop.x1) * (ridge.y1 - rightTop.y1)
|
|
) {
|
|
isRidgePointOnLine = true
|
|
}
|
|
})
|
|
if (!isRidgePointOnLine) {
|
|
const hip = new QLine([leftBottom.x1, leftBottom.y1, ridge.x1, ridge.y1], {
|
|
fontSize: polygon.fontSize,
|
|
stroke: 'red',
|
|
strokeWidth: 1,
|
|
name: 'hipLine',
|
|
})
|
|
polygon.canvas.add(hip)
|
|
polygon.hips.push(hip)
|
|
polygon.innerLines.push(hip)
|
|
}
|
|
}
|
|
if (rightTop !== undefined) {
|
|
let isRidgePointOnLine = false
|
|
polygon.ridges.forEach((r) => {
|
|
if (
|
|
r.x1 > ridge.x2 &&
|
|
r.y1 < ridge.y2 &&
|
|
(r.y1 - rightTop.y1) * (ridge.x2 - rightTop.x1) === (r.x1 - rightTop.x1) * (ridge.y2 - rightTop.y1)
|
|
) {
|
|
isRidgePointOnLine = true
|
|
}
|
|
if (
|
|
r.x2 > ridge.x2 &&
|
|
r.y2 < ridge.y2 &&
|
|
(r.y2 - rightTop.y1) * (ridge.x2 - rightTop.x1) === (r.x2 - rightTop.x1) * (ridge.y2 - rightTop.y1)
|
|
) {
|
|
isRidgePointOnLine = true
|
|
}
|
|
})
|
|
if (!isRidgePointOnLine) {
|
|
const hip = new QLine([rightTop.x1, rightTop.y1, ridge.x2, ridge.y2], {
|
|
fontSize: polygon.fontSize,
|
|
stroke: 'red',
|
|
strokeWidth: 1,
|
|
name: 'hipLine',
|
|
})
|
|
polygon.canvas.add(hip)
|
|
polygon.hips.push(hip)
|
|
polygon.innerLines.push(hip)
|
|
}
|
|
}
|
|
if (rightBottom !== undefined) {
|
|
let isRidgePointOnLine = false
|
|
polygon.ridges.forEach((r) => {
|
|
if (
|
|
r.x1 > ridge.x2 &&
|
|
r.y1 > ridge.y2 &&
|
|
(r.y1 - rightBottom.y1) * (ridge.x2 - rightBottom.x1) === (r.x1 - rightBottom.x1) * (ridge.y2 - rightBottom.y1)
|
|
) {
|
|
isRidgePointOnLine = true
|
|
}
|
|
if (
|
|
r.x2 > ridge.x2 &&
|
|
r.y2 > ridge.y2 &&
|
|
(r.y2 - rightBottom.y1) * (ridge.x2 - rightBottom.x1) === (r.x2 - rightBottom.x1) * (ridge.y2 - rightBottom.y1)
|
|
) {
|
|
isRidgePointOnLine = true
|
|
}
|
|
})
|
|
if (!isRidgePointOnLine) {
|
|
const hip = new QLine([rightBottom.x1, rightBottom.y1, ridge.x2, ridge.y2], {
|
|
fontSize: polygon.fontSize,
|
|
stroke: 'red',
|
|
strokeWidth: 1,
|
|
name: 'hipLine',
|
|
})
|
|
polygon.canvas.add(hip)
|
|
polygon.hips.push(hip)
|
|
polygon.innerLines.push(hip)
|
|
}
|
|
}
|
|
}
|
|
if (ridge.x1 === ridge.x2) {
|
|
//위쪽 좌표 기준 45, 315도 방향 라인확인
|
|
leftTop = polygon.lines
|
|
.filter((line) => line.x1 < ridge.x1 && line.y1 < ridge.y1 && Math.abs(line.x1 - ridge.x1) === Math.abs(line.y1 - ridge.y1))
|
|
.reduce((prev, current) => {
|
|
if (prev === undefined) {
|
|
return current
|
|
} else {
|
|
return Math.min(ridge.y1 - current.y1) < Math.min(ridge.y1 - prev.y1) ? current : prev
|
|
}
|
|
}, undefined)
|
|
|
|
rightTop = polygon.lines
|
|
.filter((line) => line.x1 > ridge.x1 && line.y1 < ridge.y1 && Math.abs(line.x1 - ridge.x1) === Math.abs(line.y1 - ridge.y1))
|
|
.reduce((prev, current) => {
|
|
if (prev === undefined) {
|
|
return current
|
|
} else {
|
|
return Math.min(ridge.y1 - current.y1) < Math.min(ridge.y1 - prev.y1) ? current : prev
|
|
}
|
|
}, undefined)
|
|
|
|
//아래쪽 좌표 기준 135, 225도 방향 라인확인
|
|
leftBottom = polygon.lines
|
|
.filter((line) => line.x1 < ridge.x2 && line.y1 > ridge.y2 && Math.abs(line.x1 - ridge.x2) === Math.abs(line.y1 - ridge.y2))
|
|
.reduce((prev, current) => {
|
|
if (prev === undefined) {
|
|
return current
|
|
} else {
|
|
return Math.min(current.y1 - ridge.y2) < Math.min(prev.y1 - ridge.y2) ? current : prev
|
|
}
|
|
}, undefined)
|
|
|
|
rightBottom = polygon.lines
|
|
.filter((line) => line.x1 > ridge.x2 && line.y1 > ridge.y2 && Math.abs(line.x1 - ridge.x2) === Math.abs(line.y1 - ridge.y2))
|
|
.reduce((prev, current) => {
|
|
if (prev === undefined) {
|
|
return current
|
|
} else {
|
|
return Math.min(current.y1 - ridge.y2) < Math.min(prev.y1 - ridge.y2) ? current : prev
|
|
}
|
|
}, undefined)
|
|
|
|
if (leftTop !== undefined) {
|
|
let isRidgePointOnLine = false
|
|
polygon.ridges.forEach((r) => {
|
|
if (r.x1 < ridge.x1 && r.y1 < ridge.y1) {
|
|
if ((r.y1 - leftTop.y1) * (ridge.x1 - leftTop.x1) === (r.x1 - leftTop.x1) * (ridge.y1 - leftTop.y1)) {
|
|
isRidgePointOnLine = true
|
|
}
|
|
}
|
|
if (r.x2 < ridge.x1 && r.y2 < ridge.y1) {
|
|
if ((r.y2 - leftTop.y1) * (ridge.x1 - leftTop.x1) === (r.x2 - leftTop.x1) * (ridge.y1 - leftTop.y1)) {
|
|
isRidgePointOnLine = true
|
|
}
|
|
}
|
|
})
|
|
if (!isRidgePointOnLine) {
|
|
const hip = new QLine([leftTop.x1, leftTop.y1, ridge.x1, ridge.y1], {
|
|
fontSize: polygon.fontSize,
|
|
stroke: 'red',
|
|
strokeWidth: 1,
|
|
name: 'hipLine',
|
|
})
|
|
polygon.canvas.add(hip)
|
|
polygon.hips.push(hip)
|
|
polygon.innerLines.push(hip)
|
|
}
|
|
}
|
|
if (rightTop !== undefined) {
|
|
let isRidgePointOnLine = false
|
|
polygon.ridges.forEach((r) => {
|
|
if (r.x1 > ridge.x1 && r.y1 < ridge.y1) {
|
|
if ((r.y1 - rightTop.y1) * (ridge.x1 - rightTop.x1) === (r.x1 - rightTop.x1) * (ridge.y1 - rightTop.y1)) {
|
|
isRidgePointOnLine = true
|
|
}
|
|
}
|
|
if (r.x2 > ridge.x1 && r.y2 < ridge.y1) {
|
|
if ((r.y2 - rightTop.y1) * (ridge.x1 - rightTop.x1) === (r.x2 - rightTop.x1) * (ridge.y1 - rightTop.y1)) {
|
|
isRidgePointOnLine = true
|
|
}
|
|
}
|
|
})
|
|
if (!isRidgePointOnLine) {
|
|
const hip = new QLine([rightTop.x1, rightTop.y1, ridge.x1, ridge.y1], {
|
|
fontSize: polygon.fontSize,
|
|
stroke: 'red',
|
|
strokeWidth: 1,
|
|
name: 'hipLine',
|
|
})
|
|
polygon.canvas.add(hip)
|
|
polygon.hips.push(hip)
|
|
polygon.innerLines.push(hip)
|
|
}
|
|
}
|
|
if (leftBottom !== undefined) {
|
|
let isRidgePointOnLine = false
|
|
polygon.ridges.forEach((r) => {
|
|
if (
|
|
r.x1 < ridge.x2 &&
|
|
r.y1 > ridge.y2 &&
|
|
(r.y1 - leftBottom.y1) * (ridge.x2 - leftBottom.x1) === (r.x1 - leftBottom.x1) * (ridge.y2 - leftBottom.y1)
|
|
) {
|
|
isRidgePointOnLine = true
|
|
}
|
|
if (
|
|
r.x2 < ridge.x2 &&
|
|
r.y2 > ridge.y2 &&
|
|
(r.y2 - leftBottom.y1) * (ridge.x2 - leftBottom.x1) === (r.x2 - leftBottom.x1) * (ridge.y2 - leftBottom.y1)
|
|
) {
|
|
isRidgePointOnLine = true
|
|
}
|
|
})
|
|
if (!isRidgePointOnLine) {
|
|
const hip = new QLine([leftBottom.x1, leftBottom.y1, ridge.x2, ridge.y2], {
|
|
fontSize: polygon.fontSize,
|
|
stroke: 'red',
|
|
strokeWidth: 1,
|
|
name: 'hipLine',
|
|
})
|
|
polygon.canvas.add(hip)
|
|
polygon.hips.push(hip)
|
|
polygon.innerLines.push(hip)
|
|
}
|
|
}
|
|
if (rightBottom !== undefined) {
|
|
let isRidgePointOnLine = false
|
|
polygon.ridges.forEach((r) => {
|
|
if (
|
|
r.x1 < ridge.x2 &&
|
|
r.y1 > ridge.y2 &&
|
|
(r.y1 - rightBottom.y1) * (ridge.x2 - rightBottom.x1) === (r.x1 - rightBottom.x1) * (ridge.y2 - rightBottom.y1)
|
|
) {
|
|
isRidgePointOnLine = true
|
|
}
|
|
if (
|
|
r.x2 < ridge.x2 &&
|
|
r.y2 > ridge.y2 &&
|
|
(r.y2 - rightBottom.y1) * (ridge.x2 - rightBottom.x1) === (r.x2 - rightBottom.x1) * (ridge.y2 - rightBottom.y1)
|
|
) {
|
|
isRidgePointOnLine = true
|
|
}
|
|
})
|
|
if (!isRidgePointOnLine) {
|
|
const hip = new QLine([rightBottom.x1, rightBottom.y1, ridge.x2, ridge.y2], {
|
|
fontSize: polygon.fontSize,
|
|
stroke: 'red',
|
|
strokeWidth: 1,
|
|
name: 'hipLine',
|
|
})
|
|
polygon.canvas.add(hip)
|
|
polygon.hips.push(hip)
|
|
polygon.innerLines.push(hip)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|
|
// 가장 가까운 마루를 확인하여 그릴 수 있는 라인이 존재하면 먼저 그린다.
|
|
let prevLine, currentLine, nextLine
|
|
polygon.lines.forEach((value, index) => {
|
|
if (index === 0) {
|
|
prevLine = polygon.lines[polygon.lines.length - 1]
|
|
} else {
|
|
prevLine = polygon.lines[index - 1]
|
|
}
|
|
currentLine = polygon.lines[index]
|
|
|
|
if (index === polygon.lines.length - 1) {
|
|
nextLine = polygon.lines[0]
|
|
} else if (index === polygon.lines.length) {
|
|
nextLine = polygon.lines[1]
|
|
} else {
|
|
nextLine = polygon.lines[index + 1]
|
|
}
|
|
|
|
if (!isAlreadyHip(polygon, currentLine)) {
|
|
let dVector = getDirectionForDegree(prevLine, currentLine)
|
|
let nearRidge
|
|
|
|
switch (dVector) {
|
|
case 45:
|
|
nearRidge = polygon.ridges
|
|
.filter(
|
|
(ridge) =>
|
|
((currentLine.x1 < ridge.x1 && currentLine.y1 > ridge.y1) || (currentLine.x1 < ridge.x2 && currentLine.y1 > ridge.y2)) &&
|
|
(Math.abs(currentLine.x1 - ridge.x1) === Math.abs(currentLine.y1 - ridge.y1) ||
|
|
Math.abs(currentLine.x1 - ridge.x2) === Math.abs(currentLine.y1 - ridge.y2)),
|
|
)
|
|
.reduce((prev, current) => {
|
|
if (prev !== undefined) {
|
|
if (
|
|
currentLine.x1 < current.x1 &&
|
|
currentLine.y1 > current.y1 &&
|
|
Math.abs(currentLine.x1 - current.x1) === Math.abs(currentLine.y1 - current.y1)
|
|
) {
|
|
return Math.min(Math.abs(current.x1 - currentLine.x1)) < Math.min(Math.abs(prev.x - currentLine.x1))
|
|
? {
|
|
x: current.x1,
|
|
y: current.y1,
|
|
}
|
|
: prev
|
|
} else if (
|
|
currentLine.x1 < current.x2 &&
|
|
currentLine.y1 > current.y2 &&
|
|
Math.abs(currentLine.x1 - current.x2) === Math.abs(currentLine.y1 - current.y2)
|
|
) {
|
|
return Math.min(Math.abs(current.x2 - currentLine.x1)) < Math.min(Math.abs(prev.x - currentLine.x1))
|
|
? {
|
|
x: current.x2,
|
|
y: current.y2,
|
|
}
|
|
: prev
|
|
} else {
|
|
return prev
|
|
}
|
|
} else {
|
|
if (
|
|
currentLine.x1 < current.x1 &&
|
|
currentLine.y1 > current.y1 &&
|
|
Math.abs(currentLine.x1 - current.x1) === Math.abs(currentLine.y1 - current.y1)
|
|
) {
|
|
return { x: current.x1, y: current.y1 }
|
|
} else if (
|
|
currentLine.x1 < current.x2 &&
|
|
currentLine.y1 > current.y2 &&
|
|
Math.abs(currentLine.x1 - current.x2) === Math.abs(currentLine.y1 - current.y2)
|
|
) {
|
|
return { x: current.x2, y: current.y2 }
|
|
} else {
|
|
return undefined
|
|
}
|
|
}
|
|
}, undefined)
|
|
break
|
|
case 135:
|
|
nearRidge = polygon.ridges
|
|
.filter(
|
|
(ridge) =>
|
|
((currentLine.x1 < ridge.x1 && currentLine.y1 < ridge.y1) || (currentLine.x1 < ridge.x2 && currentLine.y1 < ridge.y2)) &&
|
|
(Math.abs(currentLine.x1 - ridge.x1) === Math.abs(currentLine.y1 - ridge.y1) ||
|
|
Math.abs(currentLine.x1 - ridge.x2) === Math.abs(currentLine.y1 - ridge.y2)),
|
|
)
|
|
.reduce((prev, current) => {
|
|
if (prev !== undefined) {
|
|
if (
|
|
currentLine.x1 < current.x1 &&
|
|
currentLine.y1 < current.y1 &&
|
|
Math.abs(currentLine.x1 - current.x1) === Math.abs(currentLine.y1 - current.y1)
|
|
) {
|
|
return Math.min(Math.abs(current.x1 - currentLine.x1)) < Math.min(Math.abs(prev.x - currentLine.x1))
|
|
? {
|
|
x: current.x1,
|
|
y: current.y1,
|
|
}
|
|
: prev
|
|
} else if (
|
|
currentLine.x1 < current.x2 &&
|
|
currentLine.y1 < current.y2 &&
|
|
Math.abs(currentLine.x1 - current.x2) === Math.abs(currentLine.y1 - current.y2)
|
|
) {
|
|
return Math.min(Math.abs(current.x1 - currentLine.x1)) < Math.min(Math.abs(prev.x - currentLine.x1))
|
|
? {
|
|
x: current.x2,
|
|
y: current.y2,
|
|
}
|
|
: prev
|
|
} else {
|
|
return prev
|
|
}
|
|
} else {
|
|
if (
|
|
currentLine.x1 < current.x1 &&
|
|
currentLine.y1 < current.y1 &&
|
|
Math.abs(currentLine.x1 - current.x1) === Math.abs(currentLine.y1 - current.y1)
|
|
) {
|
|
return { x: current.x1, y: current.y1 }
|
|
} else if (
|
|
currentLine.x1 < current.x2 &&
|
|
currentLine.y1 < current.y2 &&
|
|
Math.abs(currentLine.x1 - current.x2) === Math.abs(currentLine.y1 - current.y2)
|
|
) {
|
|
return { x: current.x2, y: current.y2 }
|
|
} else {
|
|
return undefined
|
|
}
|
|
}
|
|
}, undefined)
|
|
break
|
|
case 225:
|
|
nearRidge = polygon.ridges
|
|
.filter(
|
|
(ridge) =>
|
|
((currentLine.x1 > ridge.x1 && currentLine.y1 < ridge.y1) || (currentLine.x1 > ridge.x2 && currentLine.y1 < ridge.y2)) &&
|
|
(Math.abs(currentLine.x1 - ridge.x1) === Math.abs(currentLine.y1 - ridge.y1) ||
|
|
Math.abs(currentLine.x1 - ridge.x2) === Math.abs(currentLine.y1 - ridge.y2)),
|
|
)
|
|
.reduce((prev, current) => {
|
|
if (prev !== undefined) {
|
|
if (
|
|
currentLine.x1 > current.x1 &&
|
|
currentLine.y1 < current.y1 &&
|
|
Math.abs(currentLine.x1 - current.x1) === Math.abs(currentLine.y1 - current.y1)
|
|
) {
|
|
return Math.min(Math.abs(current.x1 - currentLine.x1)) < Math.min(Math.abs(prev.x - currentLine.x1))
|
|
? {
|
|
x: current.x1,
|
|
y: current.y1,
|
|
}
|
|
: prev
|
|
} else if (
|
|
currentLine.x1 > current.x2 &&
|
|
currentLine.y1 < current.y2 &&
|
|
Math.abs(currentLine.x1 - current.x2) === Math.abs(currentLine.y1 - current.y2)
|
|
) {
|
|
return Math.min(Math.abs(current.x1 - currentLine.x1)) < Math.min(Math.abs(prev.x - currentLine.x1))
|
|
? {
|
|
x: current.x2,
|
|
y: current.y2,
|
|
}
|
|
: prev
|
|
} else {
|
|
return prev
|
|
}
|
|
} else {
|
|
if (
|
|
currentLine.x1 > current.x1 &&
|
|
currentLine.y1 < current.y1 &&
|
|
Math.abs(currentLine.x1 - current.x1) === Math.abs(currentLine.y1 - current.y1)
|
|
) {
|
|
return { x: current.x1, y: current.y1 }
|
|
} else if (
|
|
currentLine.x1 > current.x2 &&
|
|
currentLine.y1 < current.y2 &&
|
|
Math.abs(currentLine.x1 - current.x2) === Math.abs(currentLine.y1 - current.y2)
|
|
) {
|
|
return { x: current.x2, y: current.y2 }
|
|
} else {
|
|
return undefined
|
|
}
|
|
}
|
|
}, undefined)
|
|
break
|
|
case 315:
|
|
nearRidge = polygon.ridges
|
|
.filter(
|
|
(ridge) =>
|
|
((currentLine.x1 > ridge.x1 && currentLine.y1 > ridge.y1) || (currentLine.x1 > ridge.x2 && currentLine.y1 > ridge.y2)) &&
|
|
(Math.abs(currentLine.x1 - ridge.x1) === Math.abs(currentLine.y1 - ridge.y1) ||
|
|
Math.abs(currentLine.x1 - ridge.x2) === Math.abs(currentLine.y1 - ridge.y2)),
|
|
)
|
|
.reduce((prev, current) => {
|
|
if (prev !== undefined) {
|
|
if (
|
|
currentLine.x1 > current.x1 &&
|
|
currentLine.y1 > current.y1 &&
|
|
Math.abs(currentLine.x1 - current.x1) === Math.abs(currentLine.y1 - current.y1)
|
|
) {
|
|
return Math.min(Math.abs(current.x1 - currentLine.x1)) < Math.min(Math.abs(prev.x - currentLine.x1))
|
|
? {
|
|
x: current.x1,
|
|
y: current.y1,
|
|
}
|
|
: prev
|
|
} else if (
|
|
currentLine.x1 > current.x2 &&
|
|
currentLine.y1 > current.y2 &&
|
|
Math.abs(currentLine.x1 - current.x2) === Math.abs(currentLine.y1 - current.y2)
|
|
) {
|
|
return Math.min(Math.abs(current.x1 - currentLine.x1)) < Math.min(Math.abs(prev.x - currentLine.x1))
|
|
? {
|
|
x: current.x2,
|
|
y: current.y2,
|
|
}
|
|
: prev
|
|
} else {
|
|
return prev
|
|
}
|
|
} else {
|
|
if (
|
|
currentLine.x1 > current.x1 &&
|
|
currentLine.y1 > current.y1 &&
|
|
Math.abs(currentLine.x1 - current.x1) === Math.abs(currentLine.y1 - current.y1)
|
|
) {
|
|
return { x: current.x1, y: current.y1 }
|
|
} else if (
|
|
currentLine.x1 > current.x2 &&
|
|
currentLine.y1 > current.y2 &&
|
|
Math.abs(currentLine.x1 - current.x2) === Math.abs(currentLine.y1 - current.y2)
|
|
) {
|
|
return { x: current.x2, y: current.y2 }
|
|
} else {
|
|
return undefined
|
|
}
|
|
}
|
|
}, undefined)
|
|
break
|
|
}
|
|
|
|
if (nearRidge !== undefined) {
|
|
let endXPoint, endYPoint
|
|
let minX, maxX, minY, maxY
|
|
|
|
switch (dVector) {
|
|
case 45:
|
|
endXPoint = nearRidge.x
|
|
endYPoint = nearRidge.y
|
|
minX = Math.min(currentLine.x1, nearRidge.x)
|
|
minY = Math.min(currentLine.y1, nearRidge.y)
|
|
maxX = Math.max(currentLine.x1, nearRidge.x)
|
|
maxY = Math.max(currentLine.y1, nearRidge.y)
|
|
break
|
|
case 135:
|
|
endXPoint = nearRidge.x
|
|
endYPoint = nearRidge.y
|
|
minX = Math.min(currentLine.x1, nearRidge.x)
|
|
minY = Math.min(currentLine.y1, nearRidge.y)
|
|
maxX = Math.max(currentLine.x1, nearRidge.x)
|
|
maxY = Math.max(currentLine.y1, nearRidge.y)
|
|
break
|
|
case 225:
|
|
endXPoint = nearRidge.x
|
|
endYPoint = nearRidge.y
|
|
minX = Math.min(currentLine.x1, nearRidge.x)
|
|
minY = Math.min(currentLine.y1, nearRidge.y)
|
|
maxX = Math.max(currentLine.x1, nearRidge.x)
|
|
maxY = Math.max(currentLine.y1, nearRidge.y)
|
|
break
|
|
case 315:
|
|
endXPoint = nearRidge.x
|
|
endYPoint = nearRidge.y
|
|
minX = Math.min(currentLine.x1, nearRidge.x)
|
|
minY = Math.min(currentLine.y1, nearRidge.y)
|
|
maxX = Math.max(currentLine.x1, nearRidge.x)
|
|
maxY = Math.max(currentLine.y1, nearRidge.y)
|
|
break
|
|
}
|
|
|
|
let lineCoordinate = [
|
|
{ x: minX, y: minY },
|
|
{ x: minX, y: maxY },
|
|
{ x: maxX, y: maxY },
|
|
{ x: maxX, y: minY },
|
|
]
|
|
|
|
let innerPoint = polygon.lines.filter((line) => {
|
|
if (getPointInPolygon(lineCoordinate, { x: line.x1, y: line.y1 })) {
|
|
return line
|
|
}
|
|
})
|
|
|
|
if (innerPoint <= 0) {
|
|
const hip = new QLine([currentLine.x1, currentLine.y1, endXPoint, endYPoint], {
|
|
fontSize: polygon.fontSize,
|
|
stroke: 'red',
|
|
strokeWidth: 1,
|
|
name: 'hipLine',
|
|
})
|
|
polygon.canvas.add(hip)
|
|
polygon.hips.push(hip)
|
|
polygon.innerLines.push(hip)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|
|
// 마루와 연결되지 않은 hip을 그린다.
|
|
/*polygon.lines.forEach((line, index) => {
|
|
if (!isAlreadyHip(polygon, line)) {
|
|
console.log(' 확인 : ', line)
|
|
let prevLine, currentLine, nextLine
|
|
if (index === 0) {
|
|
prevLine = polygon.lines[polygon.lines.length - 1]
|
|
} else {
|
|
prevLine = polygon.lines[index - 1]
|
|
}
|
|
currentLine = polygon.lines[index]
|
|
|
|
if (index === polygon.lines.length - 1) {
|
|
nextLine = polygon.lines[0]
|
|
} else if (index === polygon.lines.length) {
|
|
nextLine = polygon.lines[1]
|
|
} else {
|
|
nextLine = polygon.lines[index + 1]
|
|
}
|
|
|
|
let endXPoint, endYPoint
|
|
let dVector = getDirectionForDegree(prevLine, currentLine)
|
|
|
|
let minX = Math.min(currentLine.x1, currentLine.x2, prevLine.x1, nextLine.x2)
|
|
let maxX = Math.max(currentLine.x1, currentLine.x2, prevLine.x1, nextLine.x2)
|
|
let minY = Math.min(currentLine.y1, currentLine.y2, prevLine.y1, nextLine.y2)
|
|
let maxY = Math.max(currentLine.y1, currentLine.y2, prevLine.y1, nextLine.y2)
|
|
|
|
let lineCoordinate = [
|
|
{ x: minX, y: minY },
|
|
{ x: minX, y: maxY },
|
|
{ x: maxX, y: maxY },
|
|
{ x: maxX, y: minY },
|
|
]
|
|
|
|
let acrossLine = getAcrossLine(polygon, currentLine, dVector)
|
|
let hypotenuse, adjacent
|
|
console.log(acrossLine)
|
|
|
|
if (getLineDirection(prevLine) === getLineDirection(nextLine)) {
|
|
hypotenuse = Math.round(getRoofHypotenuse(Math.abs(currentLine.x1 - acrossLine.x1) / 2))
|
|
} else {
|
|
hypotenuse = Math.min(
|
|
Math.round(getRoofHypotenuse(currentLine.length / 2)),
|
|
Math.round(getRoofHypotenuse(Math.abs(currentLine.x1 - acrossLine.x1) / 2)),
|
|
)
|
|
}
|
|
adjacent = getAdjacent(hypotenuse)
|
|
|
|
switch (dVector) {
|
|
case 45:
|
|
endXPoint = currentLine.x1 + adjacent
|
|
endYPoint = currentLine.y1 - adjacent
|
|
break
|
|
case 135:
|
|
endXPoint = currentLine.x1 + adjacent
|
|
endYPoint = currentLine.y1 + adjacent
|
|
break
|
|
case 225:
|
|
endXPoint = currentLine.x1 - adjacent
|
|
endYPoint = currentLine.y1 + adjacent
|
|
break
|
|
case 315:
|
|
endXPoint = currentLine.x1 - adjacent
|
|
endYPoint = currentLine.y1 - adjacent
|
|
break
|
|
}
|
|
|
|
const hip = new QLine([currentLine.x1, currentLine.y1, endXPoint, endYPoint], {
|
|
fontSize: polygon.fontSize,
|
|
stroke: 'red',
|
|
strokeWidth: 1,
|
|
name: 'hipLine',
|
|
})
|
|
polygon.canvas.add(hip)
|
|
polygon.hips.push(hip)
|
|
polygon.innerLines.push(hip)
|
|
}
|
|
})*/
|
|
}
|
|
|
|
const getPointInPolygon = (polygon, point, isInclude = false) => {
|
|
let inside = false
|
|
let minX = Math.min(polygon[0].x, polygon[1].x, polygon[2].x, polygon[3].x),
|
|
maxX = Math.max(polygon[0].x, polygon[1].x, polygon[2].x, polygon[3].x),
|
|
minY = Math.min(polygon[0].y, polygon[1].y, polygon[2].y, polygon[3].y),
|
|
maxY = Math.max(polygon[0].y, polygon[1].y, polygon[2].y, polygon[3].y)
|
|
if (!isInclude && minX < point.x && point.x < maxX && minY < point.y && point.y < maxY) {
|
|
inside = true
|
|
}
|
|
if (isInclude && minX <= point.x && point.x <= maxX && minY <= point.y && point.y <= maxY) {
|
|
inside = true
|
|
}
|
|
return inside
|
|
}
|
|
|
|
/**
|
|
* 라인과 마주하는 다른 라인과의 가장 가까운 거리를 구한다.
|
|
* @param polygon
|
|
* @param currentLine 현재 라인
|
|
* @param dVector 현재 라인의 방향
|
|
* @returns {*[]|null}
|
|
*/
|
|
const getAcrossLine = (polygon, currentLine, dVector) => {
|
|
let acrossLine
|
|
console.log('dVector : ', dVector)
|
|
switch (dVector) {
|
|
case 45:
|
|
acrossLine = polygon.lines
|
|
.filter((line) => line.x1 > currentLine.x1 && line.y1 <= currentLine.y1)
|
|
.reduce((prev, current) => {
|
|
if (prev.length > 0) {
|
|
return Math.abs(currentLine.x1 - current.x1) < Math.abs(currentLine.x1 - prev.x1) ? current : prev
|
|
} else {
|
|
return current
|
|
}
|
|
}, [])
|
|
break
|
|
case 135:
|
|
acrossLine = polygon.lines
|
|
.filter((line) => line.x1 > currentLine.x1 && line.y1 >= currentLine.y1)
|
|
.reduce((prev, current) => {
|
|
if (prev.length > 0) {
|
|
return Math.abs(currentLine.x1 - current.x1) < Math.abs(currentLine.x1 - prev.x1) ? current : prev
|
|
} else {
|
|
return current
|
|
}
|
|
}, [])
|
|
break
|
|
case 225:
|
|
acrossLine = polygon.lines
|
|
.filter((line) => line.x1 < currentLine.x1 && line.y1 >= currentLine.y1)
|
|
.reduce((prev, current) => {
|
|
if (prev.length > 0) {
|
|
return Math.abs(currentLine.x1 - current.x1) < Math.abs(currentLine.x1 - prev.x1) ? current : prev
|
|
} else {
|
|
return current
|
|
}
|
|
}, [])
|
|
break
|
|
case 315:
|
|
acrossLine = polygon.lines
|
|
.filter((line) => line.x1 < currentLine.x1 && line.y1 <= currentLine.y1)
|
|
.reduce((prev, current) => {
|
|
if (prev.length > 0) {
|
|
return Math.abs(currentLine.x1 - current.x1) < Math.abs(currentLine.x1 - prev.x1) ? current : prev
|
|
} else {
|
|
return current
|
|
}
|
|
}, [])
|
|
break
|
|
}
|
|
return acrossLine
|
|
}
|
|
|
|
/*
|
|
추녀마루(hip) 중복방지를 위해 마루와 함께 그려진 추녀마루를 확인한다
|
|
*/
|
|
const isAlreadyHip = (polygon, line) => {
|
|
let isAlreadyHip = false
|
|
polygon.hips.forEach((hip) => {
|
|
if (line.x1 === hip.x1 && line.y1 === hip.y1) {
|
|
isAlreadyHip = true
|
|
}
|
|
})
|
|
return isAlreadyHip
|
|
}
|
|
|
|
/*
|
|
3개 이상 이어지지 않은 라인 포인트 계산
|
|
모임지붕에서 point는 3개 이상의 라인과 접해야 함.
|
|
*/
|
|
const connectLinePoint = (polygon) => {
|
|
// 연결되지 않은 모든 라인의 포인트를 구한다.
|
|
let missedPoints = []
|
|
//마루
|
|
polygon.ridges.forEach((ridge) => {
|
|
if (ridge.x1 === ridge.x2) {
|
|
if (
|
|
polygon.lines
|
|
.filter((roof) => roof.y1 === roof.y2)
|
|
.filter((roof) => roof.y1 === ridge.y1 || roof.y1 === ridge.y2 || roof.y2 === ridge.y1 || roof.y2 === ridge.y2).length > 0
|
|
) {
|
|
return
|
|
}
|
|
}
|
|
if (ridge.y1 === ridge.y2) {
|
|
if (
|
|
polygon.lines
|
|
.filter((roof) => roof.x1 === roof.x2)
|
|
.filter((roof) => roof.x1 === ridge.x1 || roof.x1 === ridge.x2 || roof.x2 === ridge.x1 || roof.x2 === ridge.x2).length > 0
|
|
) {
|
|
return
|
|
}
|
|
}
|
|
if (polygon.hips.filter((hip) => hip.x2 === ridge.x1 && hip.y2 === ridge.y1).length < 2) {
|
|
missedPoints.push({ x: ridge.x1, y: ridge.y1 })
|
|
}
|
|
if (polygon.hips.filter((hip) => hip.x2 === ridge.x2 && hip.y2 === ridge.y2).length < 2) {
|
|
missedPoints.push({ x: ridge.x2, y: ridge.y2 })
|
|
}
|
|
})
|
|
|
|
//추녀마루
|
|
polygon.hips.forEach((hip) => {
|
|
let count = 0
|
|
count += polygon.ridges.filter((ridge) => (ridge.x1 === hip.x2 && ridge.y1 === hip.y2) || (ridge.x2 === hip.x2 && ridge.y2 === hip.y2)).length
|
|
|
|
count += polygon.hips.filter((hip2) => (hip2.x1 === hip.x2 && hip2.y1 === hip.y2) || (hip2.x2 === hip.x2 && hip2.y2 === hip.y2)).length
|
|
if (count < 3) {
|
|
missedPoints.push({ x: hip.x2, y: hip.y2 })
|
|
}
|
|
})
|
|
|
|
let missedLine = []
|
|
|
|
//중복포인트제거
|
|
missedPoints = [...new Set(missedPoints.map((line) => JSON.stringify(line)))].map((line) => JSON.parse(line))
|
|
|
|
missedPoints.forEach((p1) => {
|
|
let p2 = missedPoints
|
|
.filter((p) => p.x !== p1.x && p.y !== p1.y)
|
|
.reduce((prev, current) => {
|
|
if (prev !== undefined) {
|
|
return Math.sqrt(Math.pow(Math.abs(current.x - p1.x), 2) + Math.pow(Math.abs(current.y - p1.y), 2)) <
|
|
Math.sqrt(Math.pow(Math.abs(prev.x - p1.x), 2) + Math.pow(Math.abs(prev.y - p1.y), 2))
|
|
? current
|
|
: prev
|
|
} else {
|
|
return current
|
|
}
|
|
}, undefined)
|
|
if (p2 !== undefined) {
|
|
if (p1.x < p2.x && p1.y < p2.y) {
|
|
missedLine.push({ x1: p1.x, y1: p1.y, x2: p2.x, y2: p2.y })
|
|
}
|
|
if (p1.x > p2.x && p1.y < p2.y) {
|
|
missedLine.push({ x1: p2.x, y1: p2.y, x2: p1.x, y2: p1.y })
|
|
}
|
|
if (p1.x > p2.x && p1.y > p2.y) {
|
|
missedLine.push({ x1: p2.x, y1: p2.y, x2: p1.x, y2: p1.y })
|
|
}
|
|
if (p1.x < p2.x && p1.y > p2.y) {
|
|
missedLine.push({ x1: p1.x, y1: p1.y, x2: p2.x, y2: p2.y })
|
|
}
|
|
}
|
|
})
|
|
|
|
//중복라인제거
|
|
missedLine = [...new Set(missedLine.map((line) => JSON.stringify(line)))].map((line) => JSON.parse(line))
|
|
|
|
missedLine.forEach((p, index) => {
|
|
const line = new QLine([p.x1, p.y1, p.x2, p.y2], {
|
|
fontSize: polygon.fontSize,
|
|
stroke: 'purple',
|
|
strokeWidth: 1,
|
|
})
|
|
polygon.canvas.add(line)
|
|
polygon.innerLines.push(line)
|
|
})
|
|
|
|
missedPoints = []
|
|
missedLine = []
|
|
|
|
polygon.innerLines.forEach((line, index) => {
|
|
if (
|
|
polygon.innerLines.filter(
|
|
(innerLine) => (line.x2 === innerLine.x1 && line.y2 === innerLine.y1) || (line.x2 === innerLine.x2 && line.y2 === innerLine.y2),
|
|
).length < 3
|
|
) {
|
|
missedPoints.push({ x: line.x2, y: line.y2 })
|
|
}
|
|
})
|
|
|
|
missedPoints = [...new Set(missedPoints.map((line) => JSON.stringify(line)))].map((line) => JSON.parse(line))
|
|
|
|
missedPoints.forEach((p1) => {
|
|
let p2 = missedPoints
|
|
.filter((p) => !(p.x === p1.x && p.y === p1.y))
|
|
.reduce((prev, current) => {
|
|
if (prev !== undefined) {
|
|
return Math.abs(current.x - p1.x) + Math.abs(current.y - p1.y) < Math.abs(prev.x - p1.x) + Math.abs(prev.y - p1.y) ? current : prev
|
|
} else {
|
|
return current
|
|
}
|
|
}, undefined)
|
|
|
|
if (p2 !== undefined) {
|
|
if (p1.x === p2.x && p1.y < p2.y) {
|
|
missedLine.push({ x1: p1.x, y1: p1.y, x2: p2.x, y2: p2.y })
|
|
}
|
|
if (p1.x === p2.x && p1.y > p2.y) {
|
|
missedLine.push({ x1: p1.x, y1: p2.y, x2: p2.x, y2: p1.y })
|
|
}
|
|
if (p1.x < p2.x && p1.y === p2.y) {
|
|
missedLine.push({ x1: p1.x, y1: p1.y, x2: p2.x, y2: p2.y })
|
|
}
|
|
if (p1.x > p2.x && p1.y === p2.y) {
|
|
missedLine.push({ x1: p2.x, y1: p1.y, x2: p1.x, y2: p2.y })
|
|
}
|
|
}
|
|
})
|
|
|
|
//중복라인제거
|
|
missedLine = [...new Set(missedLine.map((line) => JSON.stringify(line)))].map((line) => JSON.parse(line))
|
|
|
|
missedLine.forEach((p, index) => {
|
|
const line = new QLine([p.x1, p.y1, p.x2, p.y2], {
|
|
fontSize: polygon.fontSize,
|
|
stroke: 'purple',
|
|
strokeWidth: 1,
|
|
})
|
|
polygon.canvas.add(line)
|
|
polygon.innerLines.push(line)
|
|
})
|
|
|
|
//마지막으로 연결되지 않고 떨어져있는 마루를 확인한다.
|
|
let missedRidge = []
|
|
polygon.ridges.forEach((ridge) => {
|
|
let lineCheck1 = polygon.innerLines.filter((line) => {
|
|
if (
|
|
!(line.x1 === ridge.x1 && line.y1 === ridge.y1 && line.x2 === ridge.x2 && line.y2 === ridge.y2) &&
|
|
((line.x1 === ridge.x1 && line.y1 === ridge.y1) || (line.x2 === ridge.x1 && line.y2 === ridge.y1))
|
|
) {
|
|
return line
|
|
}
|
|
})
|
|
|
|
let lineCheck2 = polygon.innerLines.filter((line) => {
|
|
if (
|
|
!(line.x1 === ridge.x1 && line.y1 === ridge.y1 && line.x2 === ridge.x2 && line.y2 === ridge.y2) &&
|
|
((line.x1 === ridge.x2 && line.y1 === ridge.y2) || (line.x2 === ridge.x2 && line.y2 === ridge.y2))
|
|
) {
|
|
return line
|
|
}
|
|
})
|
|
if (lineCheck1.length === 0 || lineCheck2.length === 0) {
|
|
missedRidge.push(ridge)
|
|
}
|
|
})
|
|
|
|
missedRidge.forEach((ridge) => {
|
|
let missedRidge2 = missedRidge.filter(
|
|
(ridge2) => !(ridge.x1 === ridge2.x1 && ridge.y1 === ridge2.y1 && ridge.x2 === ridge2.x2 && ridge.y2 === ridge2.y2),
|
|
)
|
|
|
|
missedRidge2.forEach((ridge2) => {
|
|
let overlap = false
|
|
if (ridge.x1 === ridge.x2 && ridge2.x1 === ridge2.x2 && ridge2.x1 === ridge.x1) {
|
|
overlap = true
|
|
}
|
|
if (ridge.y1 === ridge.y2 && ridge2.y1 === ridge2.y2 && ridge2.y1 === ridge.y1) {
|
|
overlap = true
|
|
}
|
|
if (overlap) {
|
|
let x1 = Math.min(ridge.x1, ridge2.x1, ridge.x2, ridge2.x2)
|
|
let x2 = Math.max(ridge.x1, ridge2.x1, ridge.x2, ridge2.x2)
|
|
let y1 = Math.min(ridge.y1, ridge2.y1, ridge.y2, ridge2.y2)
|
|
let y2 = Math.max(ridge.y1, ridge2.y1, ridge.y2, ridge2.y2)
|
|
const newRidge = new QLine([x1, y1, x2, y2], {
|
|
fontSize: polygon.fontSize,
|
|
stroke: 'blue',
|
|
strokeWidth: 1,
|
|
name: 'ridgeLine',
|
|
})
|
|
if (polygon.ridges.filter((r) => newRidge.x1 === r.x1 && newRidge.y1 === r.y1 && newRidge.x2 === r.x2 && newRidge.y2 === r.y2).length === 0) {
|
|
polygon.canvas.remove(ridge)
|
|
polygon.canvas.remove(ridge2)
|
|
polygon.ridges = polygon.ridges.filter((r) => !(ridge.x1 === r.x1 && ridge.y1 === r.y1 && ridge.x2 === r.x2 && ridge.y2 === r.y2))
|
|
polygon.ridges = polygon.ridges.filter((r) => !(ridge2.x1 === r.x1 && ridge2.y1 === r.y1 && ridge2.x2 === r.x2 && ridge2.y2 === r.y2))
|
|
polygon.innerLines = polygon.innerLines.filter((r) => !(ridge.x1 === r.x1 && ridge.y1 === r.y1 && ridge.x2 === r.x2 && ridge.y2 === r.y2))
|
|
polygon.innerLines = polygon.innerLines.filter(
|
|
(r) => !(ridge2.x1 === r.x1 && ridge2.y1 === r.y1 && ridge2.x2 === r.x2 && ridge2.y2 === r.y2),
|
|
)
|
|
|
|
polygon.canvas.add(newRidge)
|
|
polygon.ridges.push(newRidge)
|
|
polygon.innerLines.push(newRidge)
|
|
}
|
|
}
|
|
})
|
|
})
|
|
|
|
console.log('polygon : ', polygon)
|
|
}
|
|
|
|
/*
|
|
최대 생성 마루 갯수
|
|
*/
|
|
const getMaxRidge = (length) => {
|
|
return (length - 4) / 2 + 1
|
|
}
|
|
|
|
/*
|
|
두 라인의 사잇각 계산
|
|
*/
|
|
const getDirectionForDegree = (line1, line2) => {
|
|
let degree = getLineDirection(line1) + getLineDirection(line2)
|
|
let vector
|
|
|
|
switch (degree) {
|
|
case 'rb':
|
|
vector = 45
|
|
break
|
|
case 'br':
|
|
vector = 45
|
|
break
|
|
case 'lb':
|
|
vector = 135
|
|
break
|
|
case 'bl':
|
|
vector = 135
|
|
break
|
|
case 'lt':
|
|
vector = 225
|
|
break
|
|
case 'tl':
|
|
vector = 225
|
|
break
|
|
case 'rt':
|
|
vector = 315
|
|
break
|
|
case 'tr':
|
|
vector = 315
|
|
break
|
|
}
|
|
|
|
return vector
|
|
}
|
|
|
|
/*
|
|
현재 라인의 방향을 계산
|
|
*/
|
|
const getLineDirection = (line) => {
|
|
let x1, x2, y1, y2, xp, yp
|
|
x1 = Math.round(line.x1)
|
|
x2 = Math.round(line.x2)
|
|
y1 = Math.round(line.y1)
|
|
y2 = Math.round(line.y2)
|
|
|
|
xp = x1 - x2
|
|
yp = y1 - y2
|
|
|
|
if (xp === 0) {
|
|
if (yp < 0) {
|
|
return 'b'
|
|
} else {
|
|
return 't'
|
|
}
|
|
}
|
|
if (yp === 0) {
|
|
if (xp < 0) {
|
|
return 'r'
|
|
} else {
|
|
return 'l'
|
|
}
|
|
}
|
|
}
|
|
|
|
export const changeAllHipAndGableRoof = (polygon, offset, canvas) => {
|
|
const roof = polygon.filter((p) => p.name === 'roofBase')[0] // 지붕
|
|
const roofLines = roof.lines // 지붕의 라인
|
|
const ridges = roof.ridges // 마루의 라인
|
|
const hips = roof.hips // 추녀마루의 라인
|
|
|
|
console.log('roofLines : ', roofLines)
|
|
|
|
ridges.forEach((ridge) => {
|
|
let ridgeHip1 = hips.filter((hip) => hip.x2 === ridge.x1 && hip.y2 === ridge.y1)
|
|
let ridgeHip2 = hips.filter((hip) => hip.x2 === ridge.x2 && hip.y2 === ridge.y2)
|
|
let gableLines = []
|
|
if (ridgeHip1.length > 1) {
|
|
let x1 = ridgeHip1[0].x1,
|
|
y1 = ridgeHip1[0].y1,
|
|
x2 = ridgeHip1[1].x1,
|
|
y2 = ridgeHip1[1].y1
|
|
roofLines.filter((roofLine) => {
|
|
if (
|
|
(roofLine.x1 === x1 && roofLine.y1 === y1 && roofLine.x2 === x2 && roofLine.y2 === y2) ||
|
|
(roofLine.x1 === x2 && roofLine.y1 === y2 && roofLine.x2 === x1 && roofLine.y2 === y1)
|
|
) {
|
|
gableLines.push(setHipAndGableRoof(roof, ridge, ridgeHip1[0], ridgeHip1[1], offset, canvas))
|
|
}
|
|
})
|
|
}
|
|
if (ridgeHip2.length > 1) {
|
|
let x1 = ridgeHip2[0].x1,
|
|
y1 = ridgeHip2[0].y1,
|
|
x2 = ridgeHip2[1].x1,
|
|
y2 = ridgeHip2[1].y1
|
|
roofLines.filter((roofLine) => {
|
|
if (
|
|
(roofLine.x1 === x1 && roofLine.y1 === y1 && roofLine.x2 === x2 && roofLine.y2 === y2) ||
|
|
(roofLine.x1 === x2 && roofLine.y1 === y2 && roofLine.x2 === x1 && roofLine.y2 === y1)
|
|
) {
|
|
gableLines.push(setHipAndGableRoof(roof, ridge, ridgeHip2[0], ridgeHip2[1], offset, canvas))
|
|
}
|
|
})
|
|
}
|
|
gableLines.forEach((gableLine) => {
|
|
roof.innerLines.push(gableLine)
|
|
})
|
|
})
|
|
|
|
// splitPolygonWithLines(roof)
|
|
}
|
|
|
|
/**
|
|
* 모임지붕 -> 팔작지붕 변경
|
|
* @param roof
|
|
* @param ridge
|
|
* @param hip1
|
|
* @param hip2
|
|
* @param offset
|
|
* @param canvas
|
|
* @returns {*}
|
|
*/
|
|
const setHipAndGableRoof = (roof, ridge, hip1, hip2, offset, canvas) => {
|
|
let x1 = hip1.x1,
|
|
y1 = hip1.y1
|
|
let gableLine, diffOffset
|
|
if (ridge.direction === 'top') {
|
|
if (ridge.y1 > y1 && ridge.y2 > y1) {
|
|
offset = Math.abs(ridge.y1 - y1) - offset
|
|
offset = offset < 0 ? 0 : offset
|
|
ridge.set({
|
|
x1: ridge.x1,
|
|
y1: ridge.y1,
|
|
x2: ridge.x2,
|
|
y2: ridge.y2 + offset,
|
|
})
|
|
|
|
gableLine = new QLine([ridge.x2 - offset, ridge.y2, ridge.x2 + offset, ridge.y2], {
|
|
fontSize: roof.fontSize,
|
|
stroke: 'blue',
|
|
strokeWidth: 1,
|
|
name: 'gableLine',
|
|
})
|
|
canvas?.add(gableLine)
|
|
|
|
hip1.set({
|
|
x1: hip1.x1,
|
|
y1: hip1.y1,
|
|
x2: hip1.x1 < hip1.x2 ? hip1.x2 - offset : hip1.x2 + offset,
|
|
y2: hip1.y2 + offset,
|
|
})
|
|
|
|
hip2.set({
|
|
x1: hip2.x1,
|
|
y1: hip2.y1,
|
|
x2: hip2.x1 < hip2.x2 ? hip2.x2 - offset : hip2.x2 + offset,
|
|
y2: hip2.y2 + offset,
|
|
})
|
|
}
|
|
if (ridge.y1 < y1 && ridge.y2 < y1) {
|
|
offset = Math.abs(ridge.y2 - y1) - offset
|
|
offset = offset < 0 ? 0 : offset
|
|
ridge.set({
|
|
x1: ridge.x1,
|
|
y1: ridge.y1 - offset,
|
|
x2: ridge.x2,
|
|
y2: ridge.y2,
|
|
})
|
|
gableLine = new QLine([ridge.x1 - offset, ridge.y1, ridge.x1 + offset, ridge.y1], {
|
|
fontSize: roof.fontSize,
|
|
stroke: 'blue',
|
|
strokeWidth: 1,
|
|
name: 'gableLine',
|
|
})
|
|
canvas?.add(gableLine)
|
|
|
|
hip1.set({
|
|
x1: hip1.x1,
|
|
y1: hip1.y1,
|
|
x2: hip1.x1 < hip1.x2 ? hip1.x2 - offset : hip1.x2 + offset,
|
|
y2: hip1.y2 - offset,
|
|
})
|
|
|
|
hip2.set({
|
|
x1: hip2.x1,
|
|
y1: hip2.y1,
|
|
x2: hip2.x1 < hip2.x2 ? hip2.x2 - offset : hip2.x2 + offset,
|
|
y2: hip2.y2 - offset,
|
|
})
|
|
}
|
|
}
|
|
if (ridge.direction === 'bottom') {
|
|
if (ridge.y1 > y1 && ridge.y2 > y1) {
|
|
offset = Math.abs(ridge.y1 - y1) - offset
|
|
offset = offset < 0 ? 0 : offset
|
|
ridge.set({
|
|
x1: ridge.x1,
|
|
y1: ridge.y1 - offset,
|
|
x2: ridge.x2,
|
|
y2: ridge.y2,
|
|
})
|
|
gableLine = new QLine([ridge.x1 - offset, ridge.y1, ridge.x1 + offset, ridge.y1], {
|
|
fontSize: roof.fontSize,
|
|
stroke: 'blue',
|
|
strokeWidth: 1,
|
|
name: 'gableLine',
|
|
})
|
|
canvas?.add(gableLine)
|
|
|
|
hip1.set({
|
|
x1: hip1.x1,
|
|
y1: hip1.y1,
|
|
x2: hip1.x1 < hip1.x2 ? hip1.x2 - offset : hip1.x2 + offset,
|
|
y2: hip1.y2 - offset,
|
|
})
|
|
|
|
hip2.set({
|
|
x1: hip2.x1,
|
|
y1: hip2.y1,
|
|
x2: hip2.x1 < hip2.x2 ? hip2.x2 - offset : hip2.x2 + offset,
|
|
y2: hip2.y2 - offset,
|
|
})
|
|
}
|
|
if (ridge.y1 < y1 && ridge.y2 < y1) {
|
|
offset = Math.abs(ridge.y2 - y1) - offset
|
|
offset = offset < 0 ? 0 : offset
|
|
ridge.set({
|
|
x1: ridge.x1,
|
|
y1: ridge.y1,
|
|
x2: ridge.x2,
|
|
y2: ridge.y2 + offset,
|
|
})
|
|
|
|
gableLine = new QLine([ridge.x2 - offset, ridge.y2, ridge.x2 + offset, ridge.y2], {
|
|
fontSize: roof.fontSize,
|
|
stroke: 'blue',
|
|
strokeWidth: 1,
|
|
name: 'gableLine',
|
|
})
|
|
canvas?.add(gableLine)
|
|
|
|
hip1.set({
|
|
x1: hip1.x1,
|
|
y1: hip1.y1,
|
|
x2: hip1.x1 < hip1.x2 ? hip1.x2 - offset : hip1.x2 + offset,
|
|
y2: hip1.y2 + offset,
|
|
})
|
|
|
|
hip2.set({
|
|
x1: hip2.x1,
|
|
y1: hip2.y1,
|
|
x2: hip2.x1 < hip2.x2 ? hip2.x2 - offset : hip2.x2 + offset,
|
|
y2: hip2.y2 + offset,
|
|
})
|
|
}
|
|
}
|
|
if (ridge.direction === 'right') {
|
|
if (ridge.x1 > x1 && ridge.x2 > x1) {
|
|
offset = Math.abs(ridge.x1 - x1) - offset
|
|
offset = offset < 0 ? 0 : offset
|
|
ridge.set({
|
|
x1: ridge.x1 - offset,
|
|
y1: ridge.y1,
|
|
x2: ridge.x2,
|
|
y2: ridge.y2,
|
|
})
|
|
gableLine = new QLine([ridge.x1, ridge.y1 - offset, ridge.x1, ridge.y1 + offset], {
|
|
fontSize: roof.fontSize,
|
|
stroke: 'blue',
|
|
strokeWidth: 1,
|
|
name: 'gableLine',
|
|
})
|
|
canvas?.add(gableLine)
|
|
|
|
hip1.set({
|
|
x1: hip1.x1,
|
|
y1: hip1.y1,
|
|
x2: hip1.x2 - offset,
|
|
y2: hip1.y1 < hip1.y2 ? hip1.y2 - offset : hip1.y2 + offset,
|
|
})
|
|
|
|
hip2.set({
|
|
x1: hip2.x1,
|
|
y1: hip2.y1,
|
|
x2: hip2.x2 - offset,
|
|
y2: hip2.y1 < hip2.y2 ? hip2.y2 - offset : hip2.y2 + offset,
|
|
})
|
|
}
|
|
if (ridge.x1 < x1 && ridge.x2 < x1) {
|
|
offset = Math.abs(ridge.x2 - x1) - offset
|
|
offset = offset < 0 ? 0 : offset
|
|
ridge.set({
|
|
x1: ridge.x1,
|
|
y1: ridge.y1,
|
|
x2: ridge.x2 + offset,
|
|
y2: ridge.y2,
|
|
})
|
|
gableLine = new QLine([ridge.x2, ridge.y2 - offset, ridge.x2, ridge.y2 + offset], {
|
|
fontSize: roof.fontSize,
|
|
stroke: 'blue',
|
|
strokeWidth: 1,
|
|
name: 'gableLine',
|
|
})
|
|
canvas?.add(gableLine)
|
|
|
|
hip1.set({
|
|
x1: hip1.x1,
|
|
y1: hip1.y1,
|
|
x2: hip1.x2 + offset,
|
|
y2: hip1.y1 < hip1.y2 ? hip1.y2 - offset : hip1.y2 + offset,
|
|
})
|
|
|
|
hip2.set({
|
|
x1: hip2.x1,
|
|
y1: hip2.y1,
|
|
x2: hip2.x2 + offset,
|
|
y2: hip2.y1 < hip2.y2 ? hip2.y2 - offset : hip2.y2 + offset,
|
|
})
|
|
}
|
|
}
|
|
if (ridge.direction === 'left') {
|
|
if (ridge.x1 > x1 && ridge.x2 > x1) {
|
|
offset = Math.abs(ridge.x1 - x1) - offset
|
|
offset = offset < 0 ? 0 : offset
|
|
ridge.set({
|
|
x1: ridge.x1,
|
|
y1: ridge.y1,
|
|
x2: ridge.x2 + offset,
|
|
y2: ridge.y2,
|
|
})
|
|
gableLine = new QLine([ridge.x2, ridge.y2 - offset, ridge.x2, ridge.y2 + offset], {
|
|
fontSize: roof.fontSize,
|
|
stroke: 'blue',
|
|
strokeWidth: 1,
|
|
name: 'gableLine',
|
|
})
|
|
canvas?.add(gableLine)
|
|
|
|
hip1.set({
|
|
x1: hip1.x1,
|
|
y1: hip1.y1,
|
|
x2: hip1.x2 + offset,
|
|
y2: hip1.y1 < hip1.y2 ? hip1.y2 - offset : hip1.y2 + offset,
|
|
})
|
|
|
|
hip2.set({
|
|
x1: hip2.x1,
|
|
y1: hip2.y1,
|
|
x2: hip2.x2 + offset,
|
|
y2: hip2.y1 < hip2.y2 ? hip2.y2 - offset : hip2.y2 + offset,
|
|
})
|
|
}
|
|
if (ridge.x1 < x1 && ridge.x2 < x1) {
|
|
offset = Math.abs(ridge.x2 - x1) - offset
|
|
offset = offset < 0 ? 0 : offset
|
|
ridge.set({
|
|
x1: ridge.x1 - offset,
|
|
y1: ridge.y1,
|
|
x2: ridge.x2,
|
|
y2: ridge.y2,
|
|
})
|
|
gableLine = new QLine([ridge.x1, ridge.y1 - offset, ridge.x1, ridge.y1 + offset], {
|
|
fontSize: roof.fontSize,
|
|
stroke: 'blue',
|
|
strokeWidth: 1,
|
|
name: 'gableLine',
|
|
})
|
|
canvas?.add(gableLine)
|
|
|
|
hip1.set({
|
|
x1: hip1.x1,
|
|
y1: hip1.y1,
|
|
x2: hip1.x2 - offset,
|
|
y2: hip1.y1 < hip1.y2 ? hip1.y2 - offset : hip1.y2 + offset,
|
|
})
|
|
|
|
hip2.set({
|
|
x1: hip2.x1,
|
|
y1: hip2.y1,
|
|
x2: hip2.x2 - offset,
|
|
y2: hip2.y1 < hip2.y2 ? hip2.y2 - offset : hip2.y2 + offset,
|
|
})
|
|
}
|
|
}
|
|
canvas?.renderAll()
|
|
return gableLine
|
|
}
|
|
|
|
function arePointsEqual(point1, point2) {
|
|
return point1.x === point2.x && point1.y === point2.y
|
|
}
|
|
|
|
function arraysHaveSamePoints(array1, array2) {
|
|
if (array1.length !== array2.length) return false
|
|
|
|
const sortedArray1 = array1.slice().sort((a, b) => a.x - b.x || a.y - b.y)
|
|
const sortedArray2 = array2.slice().sort((a, b) => a.x - b.x || a.y - b.y)
|
|
|
|
for (let i = 0; i < sortedArray1.length; i++) {
|
|
if (!arePointsEqual(sortedArray1[i], sortedArray2[i])) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
export const toGeoJSON = (pointsArray) => {
|
|
// 객체 배열을 GeoJSON 형식의 좌표 배열로 변환
|
|
const coordinates = pointsArray.map((point) => [point.x, point.y])
|
|
|
|
// 닫힌 다각형을 만들기 위해 첫 번째 점을 마지막에 추가
|
|
coordinates.push([pointsArray[0].x, pointsArray[0].y])
|
|
|
|
return coordinates
|
|
}
|
|
|
|
export const inPolygon = (polygonPoints, rectPoints) => {
|
|
const polygonCoordinates = toGeoJSON(polygonPoints)
|
|
const rectCoordinates = toGeoJSON(rectPoints)
|
|
|
|
const polygonFeature = turf.polygon([polygonCoordinates])
|
|
const rectFeature = turf.polygon([rectCoordinates])
|
|
|
|
// 사각형의 모든 꼭짓점이 다각형 내부에 있는지 확인
|
|
const allPointsInsidePolygon = rectCoordinates.every((coord) => {
|
|
const point = turf.point(coord)
|
|
return turf.booleanPointInPolygon(point, polygonFeature)
|
|
})
|
|
|
|
// 다각형의 모든 점이 사각형 내부에 있지 않은지 확인
|
|
const noPolygonPointsInsideRect = polygonCoordinates.every((coord) => {
|
|
const point = turf.point(coord)
|
|
return !turf.booleanPointInPolygon(point, rectFeature)
|
|
})
|
|
|
|
return allPointsInsidePolygon && noPolygonPointsInsideRect
|
|
}
|
|
|
|
/**
|
|
* poolygon의 방향에 따라 화살표를 추가한다.
|
|
* @param polygon
|
|
*/
|
|
export const drawDirectionArrow = (polygon) => {
|
|
const direction = polygon.direction
|
|
if (!direction) {
|
|
return
|
|
}
|
|
|
|
polygon.canvas
|
|
.getObjects()
|
|
.filter((obj) => obj.name === 'directionText' && obj.parent === polygon.arrow)
|
|
.forEach((obj) => polygon.canvas.remove(obj))
|
|
|
|
let arrow = null
|
|
let points = []
|
|
|
|
if (polygon.arrow) {
|
|
polygon.canvas.remove(polygon.arrow)
|
|
}
|
|
|
|
let centerPoint = { x: polygon.width / 2 + polygon.left, y: polygon.height / 2 + polygon.top }
|
|
let stickeyPoint
|
|
|
|
const polygonMaxX = Math.max(...polygon.getCurrentPoints().map((point) => point.x))
|
|
const polygonMinX = Math.min(...polygon.getCurrentPoints().map((point) => point.x))
|
|
const polygonMaxY = Math.max(...polygon.getCurrentPoints().map((point) => point.y))
|
|
const polygonMinY = Math.min(...polygon.getCurrentPoints().map((point) => point.y))
|
|
|
|
switch (direction) {
|
|
case 'north':
|
|
points = [
|
|
{ x: centerPoint.x, y: polygonMinY - 20 },
|
|
{ x: centerPoint.x + 20, y: polygonMinY - 20 },
|
|
{ x: centerPoint.x + 20, y: polygonMinY - 50 },
|
|
{ x: centerPoint.x + 50, y: polygonMinY - 50 },
|
|
{ x: centerPoint.x, y: polygonMinY - 80 },
|
|
{ x: centerPoint.x - 50, y: polygonMinY - 50 },
|
|
{ x: centerPoint.x - 20, y: polygonMinY - 50 },
|
|
{ x: centerPoint.x - 20, y: polygonMinY - 20 },
|
|
]
|
|
|
|
stickeyPoint = { x: centerPoint.x, y: polygonMinY - 80 }
|
|
break
|
|
case 'south':
|
|
points = [
|
|
{ x: centerPoint.x, y: polygonMaxY + 20 },
|
|
{ x: centerPoint.x + 20, y: polygonMaxY + 20 },
|
|
{ x: centerPoint.x + 20, y: polygonMaxY + 50 },
|
|
{ x: centerPoint.x + 50, y: polygonMaxY + 50 },
|
|
{ x: centerPoint.x, y: polygonMaxY + 80 },
|
|
{ x: centerPoint.x - 50, y: polygonMaxY + 50 },
|
|
{ x: centerPoint.x - 20, y: polygonMaxY + 50 },
|
|
{ x: centerPoint.x - 20, y: polygonMaxY + 20 },
|
|
]
|
|
stickeyPoint = { x: centerPoint.x, y: polygonMaxY + 80 }
|
|
break
|
|
case 'west':
|
|
points = [
|
|
{ x: polygonMinX - 20, y: centerPoint.y },
|
|
{ x: polygonMinX - 20, y: centerPoint.y + 20 },
|
|
{ x: polygonMinX - 50, y: centerPoint.y + 20 },
|
|
{ x: polygonMinX - 50, y: centerPoint.y + 50 },
|
|
{ x: polygonMinX - 80, y: centerPoint.y },
|
|
{ x: polygonMinX - 50, y: centerPoint.y - 50 },
|
|
{ x: polygonMinX - 50, y: centerPoint.y - 20 },
|
|
{ x: polygonMinX - 20, y: centerPoint.y - 20 },
|
|
]
|
|
|
|
stickeyPoint = { x: polygonMinX - 80, y: centerPoint.y }
|
|
break
|
|
case 'east':
|
|
points = [
|
|
{ x: polygonMaxX + 20, y: centerPoint.y },
|
|
{ x: polygonMaxX + 20, y: centerPoint.y + 20 },
|
|
{ x: polygonMaxX + 50, y: centerPoint.y + 20 },
|
|
{ x: polygonMaxX + 50, y: centerPoint.y + 50 },
|
|
{ x: polygonMaxX + 80, y: centerPoint.y },
|
|
{ x: polygonMaxX + 50, y: centerPoint.y - 50 },
|
|
{ x: polygonMaxX + 50, y: centerPoint.y - 20 },
|
|
{ x: polygonMaxX + 20, y: centerPoint.y - 20 },
|
|
]
|
|
|
|
stickeyPoint = { x: polygonMaxX + 80, y: centerPoint.y }
|
|
break
|
|
}
|
|
|
|
arrow = new fabric.Polygon(points, {
|
|
selectable: false,
|
|
name: 'arrow',
|
|
fill: 'transparent',
|
|
stroke: 'black',
|
|
direction: direction,
|
|
parent: polygon,
|
|
stickeyPoint: stickeyPoint,
|
|
})
|
|
|
|
polygon.arrow = arrow
|
|
polygon.canvas.add(arrow)
|
|
polygon.canvas.renderAll()
|
|
drawDirectionStringToArrow(polygon.canvas, 0)
|
|
}
|
|
|
|
/**
|
|
* 방향을 나타낸 화살표에 각도에 따라 글씨 추가
|
|
* @param canvas
|
|
* @param compass
|
|
*/
|
|
export const drawDirectionStringToArrow = (canvas, compass = 0) => {
|
|
const arrows = canvas?.getObjects().filter((obj) => obj.name === 'arrow')
|
|
|
|
if (arrows.length === 0) {
|
|
return
|
|
}
|
|
|
|
const eastArrows = arrows.filter((arrow) => arrow.direction === 'east')
|
|
const westArrows = arrows.filter((arrow) => arrow.direction === 'west')
|
|
const northArrows = arrows.filter((arrow) => arrow.direction === 'north')
|
|
const southArrows = arrows.filter((arrow) => arrow.direction === 'south')
|
|
|
|
let southText = '南'
|
|
let eastText = '東'
|
|
let westText = '西'
|
|
let northText = '北'
|
|
|
|
if (compass === 0 || compass === 360) {
|
|
// 남,동,서 가능
|
|
// 그대로
|
|
} else if (compass < 45) {
|
|
//남(남남동),동(동북동),서(서남서) 가능
|
|
//북(북북서)
|
|
southText = '南南東'
|
|
eastText = '東北東'
|
|
westText = '西南西'
|
|
northText = '北北西'
|
|
} else if (compass === 45) {
|
|
// 남, 서 가능
|
|
// 남(남동)
|
|
// 서(남서)
|
|
// 북(북서)
|
|
// 동(북동)
|
|
southText = '南東'
|
|
westText = '南西'
|
|
northText = '北西'
|
|
eastText = '北東'
|
|
} else if (compass < 90) {
|
|
// 북(서북서)
|
|
// 동 (북북동)
|
|
// 남(동남동)
|
|
// 서(남남서)
|
|
northText = '北西北'
|
|
eastText = '北北東'
|
|
southText = '東南東'
|
|
westText = '南南西'
|
|
} else if (compass === 90) {
|
|
// 동(북)
|
|
// 서(남)
|
|
// 남(동)
|
|
// 북(서)
|
|
eastText = '北'
|
|
westText = '南'
|
|
southText = '東'
|
|
northText = '西'
|
|
} else if (compass < 135) {
|
|
// 남,서,북 가능
|
|
// 동(북북서)
|
|
// 서(남남동)
|
|
// 남(동북동)
|
|
// 북(서남서)
|
|
eastText = '北北西'
|
|
westText = '南南東'
|
|
southText = '東北東'
|
|
northText = '西南西'
|
|
} else if (compass === 135) {
|
|
// 서,북 가능
|
|
|
|
// 서(남동)
|
|
// 북(남서)
|
|
// 남(북동)
|
|
// 동(북서)
|
|
|
|
westText = '南東'
|
|
northText = '南西'
|
|
southText = '北東'
|
|
eastText = '北西'
|
|
} else if (compass < 180) {
|
|
// 북,동,서 가능
|
|
// 북(남남서)
|
|
// 동(서북서)
|
|
// 남(북북동)
|
|
// 서(동남동)
|
|
|
|
northText = '南南西'
|
|
eastText = '西北西'
|
|
southText = '北北東'
|
|
westText = '東南東'
|
|
} else if (compass === 180) {
|
|
// 북,동,서 가능
|
|
// 북(남)
|
|
// 동(서)
|
|
// 남(북)
|
|
// 서(동)
|
|
northText = '南'
|
|
eastText = '西'
|
|
southText = '北'
|
|
westText = '東'
|
|
} else if (compass < 225) {
|
|
// 서,북,동 가능
|
|
// 북(남남동)
|
|
// 동(서남서)
|
|
// 남(북북서)
|
|
// 서(동남동)
|
|
northText = '南南東'
|
|
eastText = '西南西'
|
|
southText = '北北西'
|
|
westText = '東南東'
|
|
} else if (compass === 225) {
|
|
// 북,동 가능
|
|
// 북(남동)
|
|
// 동(남서)
|
|
// 남(북서)
|
|
// 서(북동)
|
|
northText = '南東'
|
|
eastText = '南西'
|
|
southText = '北西'
|
|
westText = '北東'
|
|
} else if (compass < 270) {
|
|
// 북동남 가능
|
|
// 북(동남동)
|
|
// 동(남남서)
|
|
// 남(서북서)
|
|
// 서(북북동)
|
|
northText = '東南東'
|
|
eastText = '南南西'
|
|
southText = '西北西'
|
|
westText = '北北東'
|
|
} else if (compass === 270) {
|
|
// 북동남 가능
|
|
// 북(동)
|
|
// 동(남)
|
|
// 남(서)
|
|
// 서(북)
|
|
northText = '東'
|
|
eastText = '南'
|
|
southText = '西'
|
|
westText = '北'
|
|
} else if (compass < 315) {
|
|
// 북,동,남 가능
|
|
// 북(동북동)
|
|
// 동(남남동)
|
|
// 남(서남서)
|
|
// 서(북북서)
|
|
northText = '東北東'
|
|
eastText = '南南東'
|
|
southText = '西南西'
|
|
westText = '北北西'
|
|
} else if (compass === 315) {
|
|
// 동,남 가능
|
|
// 북(북동)
|
|
// 동(남동)
|
|
// 남(남서)
|
|
// 서(북서)
|
|
northText = '北東'
|
|
eastText = '南東'
|
|
southText = '南西'
|
|
westText = '北西'
|
|
} else if (compass < 360) {
|
|
// 남,동,서 가능
|
|
// 북(북북동)
|
|
// 동(동남동)
|
|
// 남(남남서)
|
|
// 서(서북서)
|
|
northText = '北北東'
|
|
eastText = '東南東'
|
|
southText = '南南西'
|
|
westText = '西北西'
|
|
}
|
|
|
|
clearDirectionText(canvas)
|
|
|
|
addTextByArrows(eastArrows, eastText, canvas)
|
|
addTextByArrows(westArrows, westText, canvas)
|
|
addTextByArrows(northArrows, northText, canvas)
|
|
addTextByArrows(southArrows, southText, canvas)
|
|
}
|
|
|
|
const clearDirectionText = (canvas) => {
|
|
const texts = canvas.getObjects().filter((obj) => obj.name === 'directionText')
|
|
texts.forEach((text) => {
|
|
canvas.remove(text)
|
|
})
|
|
}
|
|
|
|
const addTextByArrows = (arrows, txt, canvas) => {
|
|
arrows.forEach((arrow, index) => {
|
|
const text = new fabric.Text(`${txt}${index + 1}`, {
|
|
fontSize: arrow.parent.fontSize,
|
|
fill: 'black',
|
|
originX: 'center',
|
|
originY: 'center',
|
|
name: 'directionText',
|
|
selectable: false,
|
|
left: arrow.stickeyPoint.x,
|
|
top: arrow.stickeyPoint.y,
|
|
parent: arrow,
|
|
})
|
|
canvas.add(text)
|
|
})
|
|
}
|