1597 lines
51 KiB
JavaScript
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'
|
|
}
|
|
}
|
|
}
|
|
}
|