Jaeyoung Lee 1704efc9d5 Merge branch 'feature/test' into feature/test-jy
# Conflicts:
#	src/components/Roof2.jsx
#	src/components/fabric/QPolygon.js
2024-07-24 14:48:53 +09:00

1597 lines
51 KiB
JavaScript

import { fabric } from 'fabric'
import {
calculateIntersection,
calculateIntersection2,
distanceBetweenPoints,
findClosestLineToPoint,
findTopTwoIndexesByDistance,
getDegreeByChon,
getDirectionByPoint,
getRoofHeight,
getRoofHypotenuse,
sortedPoints,
} from '@/util/canvas-util'
import { QLine } from '@/components/fabric/QLine'
import { drawHelpLineInHexagon2 } from '@/util/qpolygon-utils'
export default class QPolygon extends fabric.Group {
type = 'QPolygon'
polygon
points
texts = []
lines = []
canvas
fontSize
qCells = []
name
shape = 0 // 점 6개일때의 shape 모양
helpPoints = []
helpLines = []
wall
initPoints
initOption
constructor(points, options, canvas) {
/*if (points.length !== 4 && points.length !== 6) {
throw new Error('Points must be 4 or 6.')
}*/
if (!options.fontSize) {
throw new Error('Font size is required.')
}
// 소수점 전부 제거
points.forEach((point) => {
point.x = Math.round(point.x)
point.y = Math.round(point.y)
})
const sortPoints = sortedPoints(points)
const polygon = new fabric.Polygon(sortPoints, options)
super([polygon], { selectable: false })
this.fontSize = options.fontSize
this.points = sortPoints
this.polygon = polygon
this.name = options.name
this.initPoints = points
this.initOption = options
this.init()
this.addEvent()
this.initLines()
this.setShape()
}
initLines() {
this.points.forEach((point, i) => {
const nextPoint = this.points[(i + 1) % this.points.length]
const line = new QLine([point.x, point.y, nextPoint.x, nextPoint.y], {
stroke: this.stroke,
strokeWidth: this.strokeWidth,
fontSize: this.fontSize,
direction: getDirectionByPoint(point, nextPoint),
})
this.lines.push(line)
})
}
init() {
this.addLengthText()
}
addEvent() {
this.on('scaling', (e) => {
this.updateLengthText()
})
this.on('selected', function() {
// 모든 컨트롤 떼기
Object.keys(this.controls).forEach((controlKey) => {
if (controlKey !== 'mtr') {
this.setControlVisible(controlKey, false)
}
})
})
}
setWall(wall) {
this.wall = wall
}
setFontSize(fontSize) {
this.fontSize = fontSize
this.texts.forEach((text) => {
text.set({ fontSize })
})
this.getObjects().forEach((obj) => {
if (obj.type[0] === 'Q') {
obj.setFontSize(fontSize)
}
})
this.canvas.add()
}
addLengthText() {
if (this.texts.length > 0) {
this.texts.forEach((text) => {
this.canvas.remove(text)
})
this.texts = []
}
const points = this.points
points.forEach((start, i) => {
const end = points[(i + 1) % points.length]
const dx = end.x - start.x
const dy = end.y - start.y
const length = Math.sqrt(dx * dx + dy * dy)
const midPoint = new fabric.Point((start.x + end.x) / 2, (start.y + end.y) / 2)
// Create new text object if it doesn't exist
const text = new fabric.Text(length.toFixed(0), {
left: midPoint.x,
top: midPoint.y,
fontSize: this.fontSize,
selectable: false,
})
this.texts.push(text)
this.canvas.add(text)
})
this.canvas.renderAll()
}
updateLengthText() {
const points = this.getCurrentPoints()
points.forEach((start, i) => {
const end = points[(i + 1) % points.length]
const dx = end.x - start.x
const dy = end.y - start.y
const length = Math.sqrt(dx * dx + dy * dy)
// Update the text object with the new length
this.texts[i].set({ text: length.toFixed(0) })
})
this.canvas.renderAll()
}
fillCell(cell = { width: 50, height: 100, padding: 10 }) {
const points = this.getCurrentPoints()
let bounds
try {
bounds = fabric.util.makeBoundingBoxFromPoints(points)
} catch (error) {
alert('다각형의 꼭지점이 4개 이상이어야 합니다.')
return
}
for (let x = bounds.left; x < bounds.left + bounds.width; x += cell.width + cell.padding) {
for (let y = bounds.top; y < bounds.top + bounds.height; y += cell.height + cell.padding) {
const rect = new fabric.Rect({
left: x,
top: y,
width: cell.width,
height: cell.height,
fill: 'transparent',
stroke: 'black',
selectable: false,
})
const rectPoints = [
new fabric.Point(rect.left, rect.top),
new fabric.Point(rect.left + rect.width, rect.top),
new fabric.Point(rect.left, rect.top + rect.height),
new fabric.Point(rect.left + rect.width, rect.top + rect.height),
]
const isInside = rectPoints.every((rectPoint) => this.inPolygon(rectPoint) && this.distanceFromEdge(rectPoint) >= cell.padding)
if (isInside) {
this.canvas.add(rect)
}
}
}
this.canvas.renderAll()
}
/**
* this.lines의 direction이 top 인 line의 모든 합이 bottom 인 line의 모든 합과 같은지 확인
* this.lines의 direction이 left 인 line의 모든 합이 right 인 line의 모든 합과 같은지 확인
* return {boolean}
*/
isValid() {
const leftLinesLengthSum = this.lines.filter((line) => line.direction === 'left').reduce((sum, line) => sum + line.length, 0)
const rightLinesLengthSum = this.lines.filter((line) => line.direction === 'right').reduce((sum, line) => sum + line.length, 0)
const topLinesLengthSum = this.lines.filter((line) => line.direction === 'top').reduce((sum, line) => sum + line.length, 0)
const bottomLinesLengthSum = this.lines.filter((line) => line.direction === 'bottom').reduce((sum, line) => sum + line.length, 0)
return leftLinesLengthSum === rightLinesLengthSum && topLinesLengthSum === bottomLinesLengthSum
}
inPolygon(point) {
const vertices = this.getCurrentPoints()
let intersects = 0
for (let i = 0; i < vertices.length; i++) {
let vertex1 = vertices[i]
let vertex2 = vertices[(i + 1) % vertices.length]
if (vertex1.y > vertex2.y) {
let tmp = vertex1
vertex1 = vertex2
vertex2 = tmp
}
if (point.y === vertex1.y || point.y === vertex2.y) {
point.y += 0.01
}
if (point.y <= vertex1.y || point.y > vertex2.y) {
continue
}
let xInt = ((point.y - vertex1.y) * (vertex2.x - vertex1.x)) / (vertex2.y - vertex1.y) + vertex1.x
if (xInt < point.x) {
intersects++
}
}
return intersects % 2 === 1
}
distanceFromEdge(point) {
const vertices = this.getCurrentPoints()
let minDistance = Infinity
for (let i = 0; i < vertices.length; i++) {
let vertex1 = vertices[i]
let vertex2 = vertices[(i + 1) % vertices.length]
const dx = vertex2.x - vertex1.x
const dy = vertex2.y - vertex1.y
const t = ((point.x - vertex1.x) * dx + (point.y - vertex1.y) * dy) / (dx * dx + dy * dy)
let closestPoint
if (t < 0) {
closestPoint = vertex1
} else if (t > 1) {
closestPoint = vertex2
} else {
closestPoint = new fabric.Point(vertex1.x + t * dx, vertex1.y + t * dy)
}
const distance = distanceBetweenPoints(point, closestPoint)
if (distance < minDistance) {
minDistance = distance
}
}
return minDistance
}
setViewLengthText(boolean) {
this.texts.forEach((text) => {
text.visible = boolean
})
this.canvas.renderAll()
}
getCurrentPoints() {
const scaleX = this.scaleX
const scaleY = this.scaleY
const left = this.left
const top = this.top
// 시작점
const point = this.points[0]
const movingX = left - point.x * scaleX
const movingY = top - point.y * scaleY
return this.points.map((point) => {
return {
x: point.x * scaleX + movingX,
y: point.y * scaleY + movingY,
}
})
}
fillBackground(pattern) {
this.polygon.set({ fill: pattern })
this.canvas.requestRenderAll()
}
// 보조선 그리기
drawHelpLine(chon = 4) {
console.log(chon)
if (!this.isValid()) {
return
}
/* if (this.lines.length === 4) {
this.#drawHelpLineInRect(chon)
} else if (this.lines.length === 6) {
// TODO : 6각형
this.#drawHelpLineInHexagon(chon)
} else if (this.lines.length === 8) {
// TODO : 8각형
this.#drawHelpLineInOctagon(chon)
}*/
this.#drawHelpLineInOctagon(chon)
}
/**
* 현재 점 6개만 가능
*/
setShape() {
let shape = 0
if (this.lines.length !== 6) {
return
}
//외각선 기준
const topIndex = findTopTwoIndexesByDistance(this.lines).sort((a, b) => a - b) //배열중에 큰 2값을 가져옴 TODO: 나중에는 인자로 받아서 다각으로 수정 해야됨
//일단 배열 6개 짜리 기준의 선 번호
if (topIndex[0] === 4) {
if (topIndex[1] === 5) {
//1번
shape = 1
}
} else if (topIndex[0] === 1) {
//4번
if (topIndex[1] === 2) {
shape = 4
}
} else if (topIndex[0] === 0) {
if (topIndex[1] === 1) {
//2번
shape = 2
} else if (topIndex[1] === 5) {
//3번
shape = 3
}
}
this.shape = shape
}
/**
* 현재 점 6개만 가능
* @returns {number}
*/
getShape() {
return this.shape
}
drawHelpLineInRect(chon) {
let type = 1
let smallestLength = Infinity
let maxLength = 0
this.lines.forEach((line) => {
if (line.length < smallestLength) {
smallestLength = line.length
}
if (line.length > maxLength) {
maxLength = line.length
}
})
// QPolygon 객체의 모든 선들을 가져옵니다.
const lines = [...this.lines]
// 이 선들을 길이에 따라 정렬합니다.
lines.sort((a, b) => a.length - b.length)
// 정렬된 배열에서 가장 작은 두 선을 선택합니다.
let smallestLines
if (smallestLength === maxLength) {
// 정사각형인 경우 0, 2번째 라인이 가장 짧은 라인
smallestLines = [lines[0], lines[2]]
} else {
smallestLines = lines.slice(0, 2)
}
let needPlusLine
let needMinusLine
const direction = smallestLines[0].direction
if (direction === 'top' || direction === 'bottom') {
needPlusLine = smallestLines[0].x1 < smallestLines[1].x1 ? smallestLines[0] : smallestLines[1]
needMinusLine = needPlusLine === smallestLines[0] ? smallestLines[1] : smallestLines[0]
type = 1 // 가로가 긴 사각형
}
if (direction === 'left' || direction === 'right') {
needPlusLine = smallestLines[0].y1 < smallestLines[1].y1 ? smallestLines[0] : smallestLines[1]
needMinusLine = needPlusLine === smallestLines[0] ? smallestLines[1] : smallestLines[0]
type = 2 // 세로가 긴 사각형
}
let point1
let point2
if (type === 1) {
point1 = {
x: needPlusLine.x1 + smallestLength / 2,
y: needPlusLine.y1 > needPlusLine.y2 ? needPlusLine.y1 - smallestLength / 2 : needPlusLine.y2 - smallestLength / 2,
}
point2 = {
x: needMinusLine.x1 - smallestLength / 2,
y: needMinusLine.y1 > needMinusLine.y2 ? needMinusLine.y1 - smallestLength / 2 : needMinusLine.y2 - smallestLength / 2,
}
} else if (type === 2) {
point1 = {
x: needPlusLine.x1 > needPlusLine.x2 ? needPlusLine.x1 - smallestLength / 2 : needPlusLine.x2 - smallestLength / 2,
y: needPlusLine.y1 + smallestLength / 2,
}
point2 = {
x: needMinusLine.x1 > needMinusLine.x2 ? needMinusLine.x1 - smallestLength / 2 : needMinusLine.x2 - smallestLength / 2,
y: needMinusLine.y1 - smallestLength / 2,
}
}
// 빗변1
const realLine1 = new QLine(
[needPlusLine.x1, needPlusLine.y1, point1.x, point1.y],
{ fontSize: this.fontSize, stroke: 'black', strokeWidth: 1 },
getRoofHypotenuse(smallestLength / 2),
)
// 빗변2
const realLine2 = new QLine(
[needPlusLine.x2, needPlusLine.y2, point1.x, point1.y],
{ fontSize: this.fontSize, stroke: 'black', strokeWidth: 1 },
getRoofHypotenuse(smallestLength / 2),
)
// 빗변3
const realLine3 = new QLine(
[needMinusLine.x1, needMinusLine.y1, point2.x, point2.y],
{ fontSize: this.fontSize, stroke: 'black', strokeWidth: 1 },
getRoofHypotenuse(smallestLength / 2),
)
// 빗변4
const realLine4 = new QLine(
[needMinusLine.x2, needMinusLine.y2, point2.x, point2.y],
{ fontSize: this.fontSize, stroke: 'black', strokeWidth: 1 },
getRoofHypotenuse(smallestLength / 2),
)
let centerPoint1
let centerPoint2
if (type === 1) {
centerPoint1 = { x: point1.x - smallestLength / 2, y: point1.y }
centerPoint2 = { x: point2.x + smallestLength / 2, y: point2.y }
} else if (type === 2) {
centerPoint1 = { x: point1.x, y: point1.y - smallestLength / 2 }
centerPoint2 = { x: point2.x, y: point2.y + smallestLength / 2 }
}
// 옆으로 누워있는 지붕의 높이
const realLine5 = new QLine(
[point1.x, point1.y, centerPoint1.x, centerPoint1.y],
{
fontSize: this.fontSize,
stroke: 'black',
strokeWidth: 1,
strokeDashArray: [5, 5],
},
getRoofHeight(smallestLength / 2, getDegreeByChon(chon)),
)
// 옆으로 누워있는 지붕의 높이
const realLine6 = new QLine(
[point2.x, point2.y, centerPoint2.x, centerPoint2.y],
{
fontSize: this.fontSize,
stroke: 'black',
strokeWidth: 1,
strokeDashArray: [5, 5],
},
getRoofHeight(smallestLength / 2, getDegreeByChon(chon)),
)
// 용마루
const ridge = new QLine([point1.x, point1.y, point2.x, point2.y], {
fontSize: this.fontSize,
stroke: 'black',
strokeWidth: 1,
})
this.canvas.add(realLine1)
this.canvas.add(realLine2)
this.canvas.add(realLine3)
this.canvas.add(realLine4)
this.canvas.add(realLine5)
this.canvas.add(realLine6)
if (smallestLength !== maxLength) {
// 정사각형이 아닌경우에만 용마루를 추가한다.
this.canvas.add(ridge)
}
}
drawHelpLineInHexagon2(chon) {
const oneSideLines = [...this.lines].map((line) => {
let newX1, newY1, newX2, newY2
if (line.direction === 'top') {
newX1 = line.x2
newY1 = line.y2
newX2 = line.x1
newY2 = line.y1
line.x1 = newX1
line.y1 = newY1
line.x2 = newX2
line.y2 = newY2
line.direction = 'bottom'
} else if (line.direction === 'left') {
newX1 = line.x2
newY1 = line.y2
newX2 = line.x1
newY2 = line.y1
line.x1 = newX1
line.y1 = newY1
line.x2 = newX2
line.y2 = newY2
line.direction = 'right'
}
return line
})
const centerLines = []
const helpLines = []
const ridgeStartPoints = []
const horizontalLines = oneSideLines.filter((line) => line.direction === 'right')
const verticalLines = oneSideLines.filter((line) => line.direction === 'bottom')
const horizontalMaxLength = horizontalLines.reduce((max, obj) => Math.max(max, obj.length), 0)
const verticalMaxLength = verticalLines.reduce((max, obj) => Math.max(max, obj.length), 0)
// 모든 가로선의 중심선을 긋는다.
horizontalLines.forEach((line, index) => {
const nextLine = horizontalLines[(index + 1) % horizontalLines.length]
const startCenterX = Math.max(line.x1, nextLine.x1)
const startCenterY = (line.y1 + nextLine.y1) / 2
let endCenterX = line.length >= nextLine.length ? startCenterX + line.length : startCenterX + nextLine.length
const endCenterY = startCenterY
if (endCenterX > Math.max(line.x2, nextLine.x2)) {
endCenterX = Math.max(line.x2, nextLine.x2)
}
const centerLine = new QLine([startCenterX, startCenterY, endCenterX, endCenterY], {
fontSize: this.fontSize,
stroke: 'red',
strokeWidth: 1,
direction: 'horizontal',
})
// this.addWithUpdate(centerLine)
centerLines.push(centerLine)
})
// 모든 세로선의 중심선을 긋는다.
verticalLines.forEach((line, index) => {
const nextLine = verticalLines[(index + 1) % verticalLines.length]
const startCenterX = (line.x1 + nextLine.x1) / 2
const startCenterY = Math.min(line.y1, nextLine.y1)
const endCenterX = startCenterX
let endCenterY = line.length >= nextLine.length ? startCenterY + line.length : startCenterY + nextLine.length
if (endCenterY > Math.max(line.y2, nextLine.y2)) {
endCenterY = Math.max(line.y2, nextLine.y2)
}
const centerLine = new QLine([startCenterX, startCenterY, endCenterX, endCenterY], {
fontSize: this.fontSize,
stroke: 'blue',
strokeWidth: 1,
direction: 'vertical',
})
// this.addWithUpdate(centerLine)
centerLines.push(centerLine)
})
const maxLength = horizontalMaxLength < verticalMaxLength ? horizontalMaxLength : verticalMaxLength
this.points.forEach((point, index) => {
const wallPoint = this.wall.points[index]
// 두 점의 좌표
const x1 = point.x
const y1 = point.y
const x2 = wallPoint.x
const y2 = wallPoint.y
let newX2, newY2
// x1, y1을 기준으로 x2, y2와의 거리를 유지한 새로운 직선 생성
const angle = Math.atan2(y2 - y1, x2 - x1)
newX2 = Math.floor(x1 + (maxLength / 2 + 50) * Math.cos(angle))
newY2 = Math.floor(y1 + (maxLength / 2 + 50) * Math.sin(angle))
const line = new QLine([x1, y1, newX2, newY2], {
fontSize: this.fontSize,
stroke: 'green',
idx: index,
})
this.addWithUpdate(line)
helpLines.push(line)
this.canvas.renderAll()
})
helpLines.forEach((line, index) => {
if (line.isAlreadyInterSection) {
return
}
const nextLine = helpLines[(index + 1 + helpLines.length) % helpLines.length]
this.canvas.renderAll()
let intersectionPoint = calculateIntersection(line, nextLine)
if (!intersectionPoint) {
return
}
line.set({ isAlreadyInterSection: true })
nextLine.set({ isAlreadyInterSection: true })
const helpLine1 = new QLine([nextLine.x1, nextLine.y1, intersectionPoint.x, intersectionPoint.y], {
fontSize: this.fontSize,
stroke: 'skyblue',
})
const helpLine2 = new QLine([line.x1, line.y1, intersectionPoint.x, intersectionPoint.y], {
fontSize: this.fontSize,
stroke: 'skyblue',
})
ridgeStartPoints.push(intersectionPoint)
this.addWithUpdate(helpLine1)
this.addWithUpdate(helpLine2)
this.removeWithUpdate(nextLine)
this.removeWithUpdate(line)
this.canvas.renderAll()
})
// 안만나는 선들
const notInterSectionLines = helpLines.filter((line) => !line.isAlreadyInterSection)
const ridgeEndPoints = []
const interSectionPoints = []
notInterSectionLines.forEach((line, index) => {
line.line.set({ strokeWidth: (index + 1) * 5 })
centerLines.forEach((centerLine) => {
const interSectionPoint = calculateIntersection2(line, centerLine)
if (!this.inPolygon(interSectionPoint) || !interSectionPoint) {
return
}
line.interSectionPoints.push(interSectionPoint)
interSectionPoints.push(interSectionPoint)
})
})
ridgeStartPoints.forEach((point, index) => {
let arrivalPoint
let distance = Infinity
let startPoint
interSectionPoints.forEach((interSectionPoint) => {
if (Math.abs(point.x - interSectionPoint.x) < 3 || Math.abs(point.y - interSectionPoint.y) < 3) {
if (distanceBetweenPoints(point, interSectionPoint) < distance) {
startPoint = point
distance = distanceBetweenPoints(point, interSectionPoint)
arrivalPoint = interSectionPoint
}
}
})
if (arrivalPoint) {
const line = notInterSectionLines.filter((line) => line.interSectionPoints.includes(arrivalPoint))[0]
const ridge = new QLine([startPoint.x, startPoint.y, arrivalPoint.x, arrivalPoint.y], {
stroke: 'black',
fontSize: this.fontSize,
})
const helpLine = new QLine([line.x1, line.y1, arrivalPoint.x, arrivalPoint.y], {
stroke: 'red',
fontSize: this.fontSize,
})
ridgeEndPoints.push(arrivalPoint)
this.addWithUpdate(ridge)
this.addWithUpdate(helpLine)
this.removeWithUpdate(line)
this.canvas.renderAll()
}
})
ridgeEndPoints.forEach((point, index) => {
const currentRidgeEndPoint = ridgeEndPoints[index]
const nextRidgeEndPoint = ridgeEndPoints[(index + 1) % ridgeEndPoints.length]
const ridgeConnectLine = new QLine([currentRidgeEndPoint.x, currentRidgeEndPoint.y, nextRidgeEndPoint.x, nextRidgeEndPoint.y], {
fontSize: this.fontSize,
stroke: 'green',
})
this.addWithUpdate(ridgeConnectLine)
this.canvas.renderAll()
})
}
drawHelpLineInHexagon(chon) {
const historyLines = []
const helpPoints = []
const notInterSectionLines = []
const ridge = []
const maxLength = this.lines.reduce((max, obj) => Math.max(max, obj.length), 0)
this.points.forEach((point, index) => {
const wallPoint = this.wall.points[index]
// 두 점의 좌표
const x1 = point.x
const y1 = point.y
const x2 = wallPoint.x
const y2 = wallPoint.y
const historyLines = []
const helpPoints = []
const notInterSectionLines = []
const ridge = []
const maxLength = this.lines.reduce((max, obj) => Math.max(max, obj.length), 0)
this.points.forEach((point, index) => {
const wallPoint = this.wall.points[index]
// 두 점의 좌표
const x1 = point.x
const y1 = point.y
const x2 = wallPoint.x
const y2 = wallPoint.y
let newX2, newY2
// x1, y1을 기준으로 x2, y2와의 거리를 유지한 새로운 직선 생성
const angle = Math.atan2(y2 - y1, x2 - x1)
newX2 = x1 + (maxLength / 2) * Math.cos(angle)
newY2 = y1 + (maxLength / 2) * Math.sin(angle)
const line = new QLine([x1, y1, newX2, newY2], {
fontSize: this.fontSize,
stroke: 'blue',
idx: index,
})
historyLines.push(line)
this.canvas.add(line)
this.canvas.renderAll()
})
/**
* 삼각 지붕
*/
historyLines.forEach((line, index) => {
const prevLine = historyLines[(index - 1 + historyLines.length) % historyLines.length]
let intersectionPoint = calculateIntersection(line, prevLine)
if (!intersectionPoint) {
notInterSectionLines.push(line)
return
}
const helpLine1 = new QLine([prevLine.x1, prevLine.y1, intersectionPoint.x, intersectionPoint.y], {
fontSize: this.fontSize,
stroke: 'red',
})
const helpLine2 = new QLine([line.x1, line.y1, intersectionPoint.x, intersectionPoint.y], {
fontSize: this.fontSize,
stroke: 'red',
})
notInterSectionLines.pop()
helpPoints.push(intersectionPoint)
this.canvas.add(helpLine1)
this.canvas.add(helpLine2)
this.canvas.remove(prevLine)
this.canvas.remove(line)
this.canvas.renderAll()
})
// 용마루
const ridgePoint = []
helpPoints.forEach((helpPoint, index) => {
const closestLine = findClosestLineToPoint(helpPoint, notInterSectionLines)
// 가까운 선의 중심점
const centerClosestLinePoint = {
x: (closestLine.x1 + closestLine.x2) / 2,
y: (closestLine.y1 + closestLine.y2) / 2,
}
const direction = getDirectionByPoint(helpPoint, centerClosestLinePoint)
let newX, newY
switch (direction) {
case 'left': {
newX = ((closestLine.x2 - closestLine.x1) * (helpPoint.y - closestLine.y1)) / (closestLine.y2 - closestLine.y1) + closestLine.x1
newY = helpPoint.y
break
}
case 'right': {
newX = ((closestLine.x2 - closestLine.x1) * (helpPoint.y - closestLine.y1)) / (closestLine.y2 - closestLine.y1) + closestLine.x1
newY = helpPoint.y
break
}
case 'top': {
newX = helpPoint.x
newY = ((closestLine.y2 - closestLine.y1) * (helpPoint.x - closestLine.x1)) / (closestLine.x2 - closestLine.x1) + closestLine.y1
break
}
case 'bottom': {
newX = helpPoint.x
newY = ((closestLine.y2 - closestLine.y1) * (helpPoint.x - closestLine.x1)) / (closestLine.x2 - closestLine.x1) + closestLine.y1
break
}
}
const ridgeHelpLine = new QLine([closestLine.x1, closestLine.y1, newX, newY], {
fontSize: this.fontSize,
stroke: 'purple',
strokeWidth: 5,
})
ridgePoint.push({ x: newX, y: newY })
const ridge = new QLine([helpPoint.x, helpPoint.y, newX, newY], {
fontSize: this.fontSize,
stroke: 'skyblue',
strokeWidth: 5,
})
this.canvas.add(ridge)
this.canvas.renderAll()
this.canvas.add(ridgeHelpLine)
this.canvas.remove(closestLine)
this.canvas.renderAll()
})
// 용마루 끼리 연결
for (let i = 0; i < ridgePoint.length; i = i + 2) {
const currentRidgeEndPoint = ridgePoint[i]
const nextRidgeEndPoint = ridgePoint[(i + 1) % ridgePoint.length]
const ridgeConnectLine = new QLine([currentRidgeEndPoint.x, currentRidgeEndPoint.y, nextRidgeEndPoint.x, nextRidgeEndPoint.y], {
fontSize: this.fontSize,
stroke: 'green',
strokeWidth: 5,
})
this.canvas.add(ridgeConnectLine)
this.canvas.renderAll()
}
this.canvas.renderAll()
})
}
#drawHelpLineInOctagon(chon) {
this.drawRoofRidge()
// this.drawHips()
// this.connectLinePoint()
}
/*마루 그리기
외벽의 모양이 돌출된 ㄷ의 형태일때
현재 라인의 외벽길이가 붙어있는 두외벽의 길이보다 짧거나 같다면
지붕의 두 꼭지점에서 외벽의 두 꼭지점으로 45도 방향으로 선을 그려 만나는 지점이 마루의 시작점이 되고
시작점에서 부터 (가장 긴선 - ( 삼각형의 빗변 에서 직각까지의 수직길이 x 2))의 길이가 마루의 끝점이 된다.
다만 마루의 길이는 나머지 나머지 두 지붕의 길이중 짧은선의 길이를 넘어갈수 없다.
*/
drawRoofRidge() {
let prevLine, currentLine, nextLine, prevWall, currentWall, nextWall
let startXPoint, startYPoint, endXPoint, endYPoint
let dVector, ridgeMaxLength, ridgeMinLength, ridgeRun
let maxSquare = this.getLargestSquareCoordinate()
// console.log(maxSquare)
this.lines.forEach(
(value, index) => {
if (index === 0) {
prevLine = this.lines[this.lines.length - 1]
prevWall = this.wall.lines[this.wall.lines.length - 1]
} else {
prevLine = this.lines[index - 1]
prevWall = this.wall.lines[index - 1]
}
currentLine = this.lines[index]
currentWall = this.wall.lines[index]
if (index === this.lines.length - 1) {
nextLine = this.lines[0]
nextWall = this.wall.lines[0]
} else if (index === this.lines.length) {
nextLine = this.lines[1]
nextWall = this.wall.lines[1]
} else {
nextLine = this.lines[index + 1]
nextWall = this.wall.lines[index + 1]
}
if (this.getLineDirection(prevLine) !== this.getLineDirection(nextLine) && currentWall.length < currentLine.length) {
dVector = this.getDirectionForDegree(prevLine, currentLine)
let prevLineLength, currentLineLength, nextLineLength
this.getRoofBaseLine(prevLine, currentLine, nextLine, dVector)
// let acrossLine = this.getAcrossLine(currentLine, dVector)
// if (acrossLine !== null) {
// console.log(currentWall.length - acrossLine.length)
// console.log('acrossLine : ' + acrossLine.length)
// console.log((currentWall.length - acrossLine.length) + '<=' + prevWall.length + '&&' + (currentWall.length - acrossLine.length) + '<=' + nextWall.length)
// }
// 마루는 세개의 벽중에서 가장 길 수 없다.
/* if ((currentWall.length <= prevWall.length && currentWall.length <= nextWall.length)
// || (acrossLine !== null && (currentWall.length - acrossLine.length <= prevWall.length || currentWall.length - acrossLine.length <= nextWall.length))
) {
console.log('currentLine.length : ' + currentLine.length)
ridgeMaxLength = Math.min(prevLine.length, nextLine.length)
ridgeMinLength = Math.max(prevLine.length, nextLine.length) - currentLine.length
ridgeRun = ridgeMinLength < ridgeMaxLength ? ridgeMinLength : ridgeMaxLength
console.log(ridgeMinLength, ridgeMaxLength, ridgeRun)
switch (dVector) {
case 45:
startXPoint = currentLine.x1 + (currentLine.length / 2)
startYPoint = currentLine.y1 - (currentLine.length / 2)
endXPoint = startXPoint
endYPoint = startYPoint
break
case 135:
startXPoint = currentLine.x1 + (currentLine.length / 2)
startYPoint = currentLine.y1 + (currentLine.length / 2)
endXPoint = startXPoint
endYPoint = startYPoint
break
case 225:
startXPoint = currentLine.x1 - (currentLine.length / 2)
startYPoint = currentLine.y1 + (currentLine.length / 2)
endXPoint = startXPoint
endYPoint = startYPoint
break
case 315:
startXPoint = currentLine.x1 - (currentLine.length / 2)
startYPoint = currentLine.y1 - (currentLine.length / 2)
endXPoint = startXPoint
endYPoint = startYPoint
break
}
// console.log('startXPoint, startYPoint, endXPoint, endYPoint')
// console.log(startXPoint, startYPoint, endXPoint, endYPoint)
if (this.ridges.length < this.getMaxRidge(this.lines.length)) {
const ridge = new QLine([startXPoint, startYPoint, endXPoint, endYPoint], {
fontSize: this.fontSize,
stroke: 'blue',
strokeWidth: 1,
})
this.addWithUpdate(ridge)
this.ridges.push(ridge)
// 마루와 연결될 추녀마루을 그려준다.
// const leftHip = new QLine([currentLine.x1, currentLine.y1, startXPoint, startYPoint], {
// fontSize: this.fontSize,
// stroke: 'red',
// strokeWidth: 1,
// })
// const rightHip = new QLine([currentLine.x2, currentLine.y2, startXPoint, startYPoint], {
// fontSize: this.fontSize,
// stroke: 'red',
// strokeWidth: 1,
// })
// this.addWithUpdate(leftHip)
// this.addWithUpdate(rightHip)
// this.hips.push(leftHip)
// this.hips.push(rightHip)
}
}*/
}
},
)
this.canvas.renderAll()
}
drawHips() {
let prevLine, currentLine, nextLine
this.lines.forEach(
(value, index) => {
if (index === 0) {
prevLine = this.lines[this.lines.length - 1]
} else {
prevLine = this.lines[index - 1]
}
currentLine = this.lines[index]
if (index === this.lines.length - 1) {
nextLine = this.lines[0]
} else if (index === this.lines.length) {
nextLine = this.lines[1]
} else {
nextLine = this.lines[index + 1]
}
if (!this.isAlreadyHip(currentLine)) {
let endXPoint, endYPoint
let dVector = this.getDirectionForDegree(prevLine, currentLine)
let linesXCoordinate = []
console.log('hip dVector : ' + dVector)
switch (dVector) {
case 45:
this.lines.forEach((line) => {
if ((currentLine.x1 < line.x1 || currentLine.x1 < line.x2)
&& (line.y1 < currentLine.y1 || line.y2 < currentLine.y1)) {
if (currentLine.x1 !== line.x1) {
linesXCoordinate.push(line.x1)
}
if (currentLine.x1 !== line.x2) {
linesXCoordinate.push(line.x2)
}
}
})
break
case 135:
this.lines.forEach((line) => {
if ((currentLine.x1 < line.x1 || currentLine.x1 < line.x2)
&& (currentLine.y1 < line.y1 || currentLine.y1 < line.y2)) {
if (currentLine.x1 !== line.x1) {
linesXCoordinate.push(line.x1)
}
if (currentLine.x1 !== line.x2) {
linesXCoordinate.push(line.x2)
}
}
})
break
case 225:
this.lines.forEach((line) => {
if ((line.x1 < currentLine.x1 || line.x2 < currentLine.x1)
&& (currentLine.y1 < line.y1 || currentLine.y1 < line.y2)) {
if (currentLine.x1 !== line.x1) {
linesXCoordinate.push(line.x1)
}
if (currentLine.x1 !== line.x2) {
linesXCoordinate.push(line.x2)
}
}
})
break
case 315:
this.lines.forEach((line) => {
if ((line.x1 < currentLine.x1 || line.x2 < currentLine.x1)
&& (line.y1 < currentLine.y1 || line.y2 < currentLine.y1)) {
if (currentLine.x1 !== line.x1) {
linesXCoordinate.push(line.x1)
}
if (currentLine.x1 !== line.x2) {
linesXCoordinate.push(line.x2)
}
}
})
break
}
console.log('dVector : ', dVector)
console.log('currentLine.x1 : ' + currentLine.x1 + ' , linesXCoordinate :', Math.min.apply(null, linesXCoordinate))
let lineMaxHypotenuse
if (linesXCoordinate.length > 0) {
lineMaxHypotenuse = Math.floor(getRoofHypotenuse(Math.abs(currentLine.x1 - Math.min.apply(null, linesXCoordinate))))
console.log(currentLine.x1, lineMaxHypotenuse)
}
console.log('lineMaxHypotenuse : ', lineMaxHypotenuse)
this.ridges.forEach(ridge => {
console.log('getRoofHypotenuse(Math.abs(currentLine.x1 - ridge.x1)) : ' + Math.floor(getRoofHypotenuse(Math.abs(currentLine.x1 - ridge.x1))) + ' <= lineMaxHypotenuse : ' + lineMaxHypotenuse)
if (Math.abs(currentLine.x1 - ridge.x1) === Math.abs(currentLine.y1 - ridge.y1)
&& Math.floor(getRoofHypotenuse(Math.abs(currentLine.x1 - ridge.x1))) <= lineMaxHypotenuse) {
endXPoint = ridge.x1
endYPoint = ridge.y1
console.log('X1')
}
if (Math.abs(currentLine.x1 - ridge.x2) === Math.abs(currentLine.y1 - ridge.y2)
&& getRoofHypotenuse(Math.abs(currentLine.x1 - ridge.x2)) <= lineMaxHypotenuse) {
endXPoint = ridge.x2
endYPoint = ridge.y2
console.log('X2')
}
})
// TODO [ljyoung] : 마루와 만나지 않는 hip 계산
console.log('endXPoint : ' + endXPoint + ' , endYPoint : ' + endYPoint)
if (endXPoint === undefined || endYPoint === undefined) {
console.log('todo')
// endXPoint = currentLine.x1 + lineMaxHypotenuse / 2
// endYPoint = currentLine.y1 + lineMaxHypotenuse / 2
console.log('currentLine.x1 : ' + currentLine.x1 + ' , ' +
'this.getReverseHypothenuse(lineMaxHypotenuse): ' + this.getReverseHypotenuse(lineMaxHypotenuse) / 2)
switch (dVector) {
case 45:
endXPoint = Math.abs(currentLine.x1 + this.getReverseHypotenuse(lineMaxHypotenuse) / 2)
endYPoint = Math.abs(currentLine.y1 - this.getReverseHypotenuse(lineMaxHypotenuse) / 2)
break
case 135:
endXPoint = Math.abs(currentLine.x1 + this.getReverseHypotenuse(lineMaxHypotenuse) / 2)
endYPoint = Math.abs(currentLine.y1 + this.getReverseHypotenuse(lineMaxHypotenuse) / 2)
break
case 225:
endXPoint = Math.abs(currentLine.x1 - this.getReverseHypotenuse(lineMaxHypotenuse) / 2)
endYPoint = Math.abs(currentLine.y1 + this.getReverseHypotenuse(lineMaxHypotenuse) / 2)
break
case 315:
endXPoint = Math.abs(currentLine.x1 - this.getReverseHypotenuse(lineMaxHypotenuse) / 2)
endYPoint = Math.abs(currentLine.y1 - this.getReverseHypotenuse(lineMaxHypotenuse) / 2)
break
}
}
console.log(currentLine)
console.log(currentLine.x1, currentLine.y1, endXPoint, endYPoint)
const hip = new QLine([currentLine.x1, currentLine.y1, endXPoint, endYPoint], {
fontSize: this.fontSize,
stroke: 'red',
strokeWidth: 1,
})
this.addWithUpdate(hip)
this.hips.push(hip)
}
},
)
this.canvas.renderAll()
}
/*
안쪽으로 들어간 모양의 기준을 잡기 위해 가장긴 x좌표와 y좌표를 구한다.
*/
getLargestSquareCoordinate() {
let arrX = []
let arrY = []
this.points.forEach(point => {
arrX.push(point.x)
arrY.push(point.y)
})
let minX = Math.min.apply(null, arrX)
let minY = Math.min.apply(null, arrY)
let maxX = Math.max.apply(null, arrX)
let maxY = Math.max.apply(null, arrY)
return { minX: minX, minY: minY, maxX: maxX, maxY: maxY }
}
/*
피타고라스 정리를 이용하여 빗변의 길이를 알때 나머지 변의 길이를 구한다.
*/
getReverseHypotenuse(line) {
return Math.sqrt(Math.pow(line, 2) / 2)
}
getRoofBaseLine(prevLine, currentLine, nextLine, dVector) {
let prevLineLength, currentLineLength, nextLineLength
let objectX = {}
let objectY = {}
let arrLine = []
let currentPolygon = []
const testXArr = [currentLine.x1, currentLine.x2, prevLine.x1, nextLine.x2]
const testYArr = [currentLine.y1, currentLine.y2, prevLine.y1, nextLine.y2]
let minX = Math.min.apply(null, testXArr)
let maxX = Math.max.apply(null, testXArr)
let minY = Math.min.apply(null, testYArr)
let maxY = Math.max.apply(null, testYArr)
let inside = false
console.log('testXArr : ' + testXArr, 'min : ', Math.min.apply(null, testXArr), 'max : ', Math.max.apply(null, testXArr))
console.log('testYArr : ' + testYArr, 'min : ', Math.min.apply(null, testYArr), 'max : ', Math.max.apply(null, testYArr))
console.log('currentLine.length : ' + currentLine.length, 'dVector : ' + dVector)
console.log('x1,y1 :', currentLine.x1, currentLine.y1, ', x2,y2 : ', currentLine.x2
, currentLine.y2, ', x3,y3 : ', nextLine.x2, nextLine.y2, ', x4,y4 : ', prevLine.x1, prevLine.y1)
//X좌표 기준점을 구한다.
objectX = { x1: currentLine.x1, x2: currentLine.x2, x3: nextLine.x2, x4: prevLine.x1 }
//Y좌표 기준점을 구한다.
objectY = { y1: currentLine.y1, y2: currentLine.y2, y3: nextLine.y2, y4: prevLine.y1 }
console.log('objectX : ', objectX)
console.log('objectY : ', objectY)
let lineDirection = this.getLineDirection(currentLine)
arrLine = this.lines.filter((line) => {
console.log('line.length : ' + line.length)
console.log(minX < line.x1 && line.x2 < maxX && minY < line.y1 && line.y2 < maxY)
return minX < line.x1 && line.x2 < maxX && minY < line.y1 && line.y2 < maxY
/* if (dVector === 45 || dVector === 135) {
console.log('objectX.x1 < line.x1 && objectX.x1 < line.x2', objectX.x1 < line.x1, objectX.x1 < line.x2)
console.log('objectY.y1, objectY.y3, line.y1 =', objectY.y1, objectY.y3, line.y1)
console.log('objectY.y1, objectY.y4, line.y2 =', objectY.y1, objectY.y4, line.y2)
console.log('this.isBetween(objectY.y1, objectY.y3, line.y1) :', this.isBetween(objectY.y1, objectY.y3, line.y1))
console.log('this.isBetween(objectY.y1, objectY.y4, line.y2) : ', this.isBetween(objectY.y1, objectY.y4, line.y2))
console.log((objectX.x1 < line.x1 && objectX.x1 < line.x2)
&& (this.isBetween(objectY.y1, objectY.y2, line.y1) || this.isBetween(objectY.y1, objectY.y2, line.y1)))
return (objectX.x1 < line.x1 && objectX.x1 < line.x2)
&& (this.isBetween(objectY.y1, objectY.y3, line.y1) || this.isBetween(objectY.y1, objectY.y4, line.y2))
}
if (dVector === 225 || dVector === 315) {
console.log('objectY.y1 < line.y1 && objectY.y1 < line.y2', objectY.y1 < line.y1, objectY.y1 < line.y2)
console.log('objectX.x1, objectX.x3, line.x1 =', objectX.x1, objectX.x3, line.x1)
console.log('objectX.x1, objectX.x4, line.x2 =', objectX.x1, objectX.x4, line.x2)
console.log((objectY.y1 < line.y1 && objectY.y1 < line.y2)
&& (this.isBetween(objectX.x1, objectX.x3, line.x1) || this.isBetween(objectX.x1, objectX.x4, line.x2)))
return (objectY.y1 < line.y1 && objectY.y1 < line.y2)
&& (this.isBetween(objectX.x1, objectX.x3, line.x1) || this.isBetween(objectX.x1, objectX.x4, line.x2))
}*/
})
console.log('arrLine : ' + arrLine.length)
function isPointInPolygon(polygon, point) {
const [x, y] = point
let inside = false
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
const [xi, yi] = polygon[i]
const [xj, yj] = polygon[j]
const intersect = ((yi > y) !== (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi)
if (intersect) inside = !inside
}
return inside
}
}
/*
a와 b 사이에 c가 있는지 확인한다.
*/
isBetween(a, b, c) {
return (a < c && c < b) || (b < c && c < a)
}
/*
라인과 마주하는 다른 라인과의 가장 가까운 거리를 구한다.
*/
getAcrossLine(currentLine, dVector) {
let lineDirection = this.getLineDirection(currentLine)
let acrossLines = []
if (lineDirection === 't' || lineDirection === 'b') {
if (dVector === 45 || dVector === 135) {
console.log('45, 135 : currentLine을 기준으로 오른쪽에서 찾는다.')
acrossLines = this.lines.filter(line => {
if ((this.getLineDirection(line) === 'l' || this.getLineDirection(line) === 'r')
&& (currentLine.x1 < line.x1 || currentLine.x1 < line.x2)) {
return line
}
})
if (acrossLines.length > 0) {
acrossLines = acrossLines.reduce((a, b) => {
return a.x1 < b.x1 ? a : b
})
}
}
if (dVector === 225 || dVector === 315) {
console.log('225, 315 : currentLine을 기준으로 왼쪽에서 찾는다.')
acrossLines = this.lines.filter(line => {
if ((this.getLineDirection(line) === 'l' || this.getLineDirection(line) === 'r')
&& (line.x1 < currentLine.x1 || line.x2 < currentLine.x1)) {
return line
}
})
if (acrossLines.length > 0) {
acrossLines = acrossLines.reduce((a, b) => {
return b.x1 < a.x1 ? a : b
})
}
}
}
if (lineDirection === 'l' || lineDirection === 'r') {
if (dVector === 45 || dVector === 315) {
console.log('45, 315 : currentLine을 기준으로 위에서 찾는다.')
acrossLines = this.lines.filter(line => {
if ((this.getLineDirection(line) === 't' || this.getLineDirection(line) === 'b')
&& (line.y1 < currentLine.y1 || line.y2 < currentLine.y1)) {
console.log(line)
return line
}
})
if (acrossLines.length > 0) {
acrossLines = acrossLines.reduce((a, b) => {
return b.y1 < a.y1 ? a : b
})
}
}
if (dVector === 135 || dVector === 225) {
console.log('135, 225 : currentLine을 기준으로 아래에서 찾는다.')
acrossLines = this.lines.filter(line => {
if ((this.getLineDirection(line) === 't' || this.getLineDirection(line) === 'b')
&& (currentLine.y1 < line.y1 || currentLine.y1 < line.y2)) {
return line
}
})
if (acrossLines.length > 0) {
acrossLines = acrossLines.reduce((a, b) => {
return a.y1 < b.y1 ? a : b
})
}
}
}
console.log('acrossLines : ', acrossLines)
if (acrossLines.length > 0) {
if (lineDirection === 't' || lineDirection === 'b') {
// 현재 라인의 y1, y2의 최소값과 최대값으로 정렬한다.
const [minY, maxY] = currentLine.y1 < currentLine.y2 ? [currentLine.y1, currentLine.y2] : [currentLine.y2, currentLine.y1]
const y1InRange = acrossLines.y1 < maxY && acrossLines.y1 > minY
const y2InRange = acrossLines.y2 < maxY && acrossLines.y2 > minY
// console.log('y1InRange : ' + y1InRange, 'y1InRange : ' + y1InRange)
if (y1InRange && y2InRange) {
return null
} else {
return acrossLines
}
}
if (lineDirection === 'r' || lineDirection === 'l') {
const [minX, maxX] = currentLine.x1 < currentLine.x2 ? [currentLine.x1, currentLine.x2] : [currentLine.x2, currentLine.x1]
const x1InRange = acrossLines.x1 < maxX && acrossLines.x1 > minX
const x2InRange = acrossLines.x2 < maxX && acrossLines.x2 > minX
console.log('x1InRange :' + x1InRange, 'x1InRange : ' + x2InRange)
if (x1InRange && x2InRange) {
console.log('null')
return null
} else {
console.log('acrossLines')
return acrossLines
}
}
}
return null
}
/*
두 선의 교차점이 존재하는지 확인한다.
*/
findIntersection(line1, line2) {
const [x1, y1, x2, y2] = line1 // 첫 번째 선의 두 점
const [x3, y3, x4, y4] = line2 // 두 번째 선의 두 점
// 선의 방정식의 계수 계산
const A1 = y2 - y1
const B1 = x1 - x2
const C1 = A1 * x1 + B1 * y1
const A2 = y4 - y3
const B2 = x3 - x4
const C2 = A2 * x3 + B2 * y3
const determinant = A1 * B2 - A2 * B1
if (determinant === 0) {
// 두 선이 평행하거나 일직선일 경우
console.log('두 직선은 평행하거나 일직선입니다.')
return null
}
const x = (B1 * C2 - B2 * C1) / determinant
const y = (A1 * C2 - A2 * C1) / determinant
return { x, y }
}
/*
추녀마루(hip) 중복방지를 위해 마루와 함께 그려진 추녀마루를 확인한다
*/
isAlreadyHip(line) {
let isAlreadyHip = false
this.hips.forEach(hip => {
if (line.x1 === hip.x1 && line.y1 === hip.y1) {
isAlreadyHip = true
}
})
return isAlreadyHip
}
/*
3개 이상 이어지지 않은 라인 포인트 계산
모임지붕에서 point는 3개 이상의 라인과 접해야 함.
*/
connectLinePoint() {
let missedPoints = []
let missedLine = []
this.ridges.forEach(ridge => {
if (this.findConnectionLineCount(ridge.x1, ridge.y1) < 2) {
missedPoints.push({ x: ridge.x1, y: ridge.y1 })
}
if (this.findConnectionLineCount(ridge.x2, ridge.y2) < 2) {
missedPoints.push({ x: ridge.x2, y: ridge.y2 })
}
})
console.log(missedPoints)
missedPoints.forEach(p1 => {
let nearX, nearY, diffX, diffY, diffMinX, diffMinY
//가장 가까운 포인트를 확인
missedPoints.forEach(p2 => {
diffX = Math.abs(p1.x - p2.x)
diffY = Math.abs(p1.y - p2.y)
if (diffMinX === undefined && diffMinY === undefined && diffX > 0 && diffY > 0) {
diffMinX = diffX
diffMinY = diffY
}
console.log(nearX, nearY, diffX, diffY, diffMinX, diffMinY)
if (diffX !== 0 && diffY !== 0 && (diffX === diffY)
&& diffX <= diffMinX && diffY <= diffMinY) {
nearX = p2.x
nearY = p2.y
diffMinX = Math.abs(p1.x - p2.x)
diffMinY = Math.abs(p1.y - p2.y)
}
})
console.log(nearX, nearY)
if (nearX !== undefined && nearY !== undefined) {
if (p1.x < nearX && p1.y < nearY) {
missedLine.push({ x1: p1.x, y1: p1.y, x2: nearX, y2: nearY })
} else if (p1.x > nearX && p1.y < nearY) {
missedLine.push({ x1: nearX, y1: p1.y, x2: p1.x, y2: nearY })
} else if (p1.x > nearX && p1.y > nearY) {
missedLine.push({ x1: nearX, y1: nearY, x2: p1.x, y2: p1.y })
} else if (p1.x < nearX && p1.y > nearY) {
missedLine.push({ x1: nearX, y1: nearY, x2: p1.x, y2: p1.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: this.fontSize,
stroke: 'green',
strokeWidth: 1,
})
this.addWithUpdate(line)
})
this.canvas.renderAll()
}
/*
hip은 항상 마루에 연결되고 마루에는 2개 이상의 hip이 연결된다.
hip의 시작은 처마꼭지점이며 끝은 마루이기 때문에 힙의 끝 좌표와 비교한다.
*/
findConnectionLineCount(x, y) {
console.log(this.hips)
let count = 0
this.hips.forEach((hip, index) => {
if (x === hip.x2 && y === hip.y2) {
count++
}
})
console.log(count)
return count
}
/*
최대 생성 마루 갯수
*/
getMaxRidge(length) {
let a1 = 4
let d = 2
return ((length - a1) / d) + 1
}
/*
두 라인의 사잇각 계산
*/
getDirectionForDegree(line1, line2) {
let degree = this.getLineDirection(line1) + this.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
}
/*
현재 라인의 방향을 계산
*/
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'
}
}
}
}