8126 lines
328 KiB
JavaScript
8126 lines
328 KiB
JavaScript
import { fabric } from 'fabric'
|
|
import { QLine } from '@/components/fabric/QLine'
|
|
import { getDegreeByChon, isPointOnLine } from '@/util/canvas-util'
|
|
|
|
import { QPolygon } from '@/components/fabric/QPolygon'
|
|
import * as turf from '@turf/turf'
|
|
import { LINE_TYPE, POLYGON_TYPE } from '@/common/common'
|
|
import { OUTER_LINE_TYPE } from '@/store/outerLineAtom'
|
|
import Big from 'big.js'
|
|
|
|
const TWO_PI = Math.PI * 2
|
|
|
|
export const defineQPloygon = () => {
|
|
fabric.QPolygon.fromObject = function (object, callback) {
|
|
fabric.Object._fromObject('QPolygon', object, callback, 'points')
|
|
}
|
|
}
|
|
|
|
/**
|
|
* point1에서 point2를 잇는 방향의 각도를 구한다.
|
|
* @param point1
|
|
* @param point2
|
|
* @returns {number}
|
|
*/
|
|
export const calculateAngle = (point1, point2) => {
|
|
const deltaX = Big(point2.x ?? 0)
|
|
.minus(point1.x ?? 0)
|
|
.toNumber()
|
|
const deltaY = Big(point2.y ?? 0)
|
|
.minus(point1.y ?? 0)
|
|
.toNumber()
|
|
const angleInRadians = Math.atan2(deltaY, deltaX)
|
|
return Big(angleInRadians * (180 / Math.PI))
|
|
.round()
|
|
.toNumber()
|
|
}
|
|
|
|
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) {
|
|
const 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,
|
|
}
|
|
}
|
|
|
|
/**
|
|
* edgeA와 edgeB가 마주치는 포인트의 좌표를 반환한다.
|
|
* @param edgeA
|
|
* @param edgeB
|
|
* @returns {{x: *, y: *, isIntersectionOutside: boolean}|null}
|
|
*/
|
|
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 // 선들이 평행하거나 일치합니다
|
|
}
|
|
|
|
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
|
|
|
|
// 교차점이 두 선분의 범위 내에 있는지 확인
|
|
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) {
|
|
let startAngle = Math.atan2(startVertex.y - center.y, startVertex.x - center.x)
|
|
let 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 })
|
|
originPolygon.setViewLengthText(false)
|
|
|
|
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
|
|
}
|
|
}
|
|
}
|
|
|
|
function normalizePoint(point) {
|
|
return {
|
|
x: Math.round(point.x),
|
|
y: Math.round(point.y),
|
|
}
|
|
}
|
|
|
|
function arePolygonsEqual(polygon1, polygon2) {
|
|
if (polygon1.length !== polygon2.length) return false
|
|
|
|
const normalizedPolygon1 = polygon1.map(normalizePoint).sort((a, b) => a.x - b.x || a.y - b.y)
|
|
const normalizedPolygon2 = polygon2.map(normalizePoint).sort((a, b) => a.x - b.x || a.y - b.y)
|
|
|
|
return normalizedPolygon1.every((point, index) => arePointsEqual(point, normalizedPolygon2[index]))
|
|
}
|
|
|
|
export function removeDuplicatePolygons(polygons) {
|
|
let uniquePolygons = []
|
|
|
|
// x가 전부 같거나, y가 전부 같은 경우 제거
|
|
polygons.forEach((polygon) => {
|
|
const isDuplicate = uniquePolygons.some((uniquePolygon) => arePolygonsEqual(polygon, uniquePolygon))
|
|
if (!isDuplicate) {
|
|
uniquePolygons.push(polygon)
|
|
}
|
|
})
|
|
|
|
uniquePolygons = uniquePolygons.filter((polygon) => {
|
|
return isValidPoints(polygon)
|
|
})
|
|
|
|
return uniquePolygons
|
|
}
|
|
|
|
// 같은 직선상에 있는지 확인 같은 직선이라면 polygon을 생성할 수 없으므로 false
|
|
const isValidPoints = (points) => {
|
|
function isColinear(p1, p2, p3) {
|
|
return (p2.x - p1.x) * (p3.y - p1.y) === (p3.x - p1.x) * (p2.y - p1.y)
|
|
}
|
|
|
|
function segmentsOverlap(a1, a2, b1, b2) {
|
|
// 같은 직선 상에 있는가?
|
|
if (!isColinear(a1, a2, b1) || !isColinear(a1, a2, b2)) {
|
|
return false
|
|
}
|
|
|
|
const isHorizontal = a1.y === a2.y
|
|
if (isHorizontal) {
|
|
const aMin = Math.min(a1.x, a2.x),
|
|
aMax = Math.max(a1.x, a2.x)
|
|
const bMin = Math.min(b1.x, b2.x),
|
|
bMax = Math.max(b1.x, b2.x)
|
|
return Math.max(aMin, bMin) < Math.min(aMax, bMax)
|
|
} else {
|
|
const aMin = Math.min(a1.y, a2.y),
|
|
aMax = Math.max(a1.y, a2.y)
|
|
const bMin = Math.min(b1.y, b2.y),
|
|
bMax = Math.max(b1.y, b2.y)
|
|
return Math.max(aMin, bMin) < Math.min(aMax, bMax)
|
|
}
|
|
}
|
|
|
|
for (let i = 0; i < points.length - 2; i++) {
|
|
const a1 = points[i]
|
|
const a2 = points[i + 1]
|
|
const b1 = points[i + 1] // 연속되는 점
|
|
const b2 = points[i + 2]
|
|
|
|
if (segmentsOverlap(a1, a2, b1, b2)) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
export const isSamePoint = (a, b) => {
|
|
if (!a || !b) {
|
|
return false
|
|
}
|
|
return Math.abs(Math.round(a.x) - Math.round(b.x)) <= 2 && Math.abs(Math.round(a.y) - Math.round(b.y)) <= 2
|
|
}
|
|
|
|
/**
|
|
* 박공지붕(templateA, templateB)을 그린다.
|
|
* @param roofId
|
|
* @param canvas
|
|
* @param textMode
|
|
*/
|
|
export const drawGabledRoof = (roofId, canvas, textMode) => {
|
|
const roof = canvas?.getObjects().find((object) => object.id === roofId)
|
|
const roofLines = roof.lines
|
|
const wallLines = canvas?.getObjects().find((object) => object.name === POLYGON_TYPE.WALL && object.attributes.roofId === roof.id).lines
|
|
const hasNonParallelLines = roofLines.filter((line) => line.x1 !== line.x2 && line.y1 !== line.y2)
|
|
if (hasNonParallelLines.length > 0) {
|
|
// alert('대각선이 존재합니다.')
|
|
return
|
|
}
|
|
|
|
// 처마라인의 기본속성 입력
|
|
const eaves = []
|
|
roofLines.forEach((currentRoof, index) => {
|
|
if (currentRoof.attributes?.type === LINE_TYPE.WALLLINE.EAVES) {
|
|
eaves.push({ index: index, roof: currentRoof, length: currentRoof.attributes.planeSize })
|
|
}
|
|
})
|
|
const ridges = []
|
|
eaves.sort((a, b) => a.length - b.length)
|
|
|
|
eaves.forEach((eave) => {
|
|
const index = eave.index,
|
|
currentRoof = eave.roof
|
|
const currentWall = wallLines[index]
|
|
|
|
const oppositeLine = roofLines
|
|
.filter((line) => line !== currentRoof) // 현재 벽라인을 제외한 나머지 벽라인
|
|
.filter((line) => {
|
|
if (currentRoof.x1 === currentRoof.x2) {
|
|
const vector = Math.sign(currentRoof.y1 - currentRoof.y2)
|
|
const vector2 = Math.sign(currentRoof.x1 - currentWall.x1)
|
|
return line.x1 === line.x2 && Math.sign(line.y1 - line.y2) === -vector && Math.sign(currentRoof.x1 - line.x1) === vector2
|
|
}
|
|
if (currentRoof.y1 === currentRoof.y2) {
|
|
const vector = Math.sign(currentRoof.x1 - currentRoof.x2)
|
|
const vector2 = Math.sign(currentRoof.y1 - currentWall.y1)
|
|
return line.y1 === line.y2 && Math.sign(line.x1 - line.x2) === -vector && Math.sign(currentRoof.y1 - line.y1) === vector2
|
|
}
|
|
}) // 현재 벽라인과 직교하는 벽라인
|
|
|
|
// 현재 벽라인과 직교하는 벽라인 사이에 마루를 그린다.
|
|
oppositeLine.forEach((line) => {
|
|
let points // 마루의 시작점과 끝점
|
|
if (currentRoof.x1 === currentRoof.x2) {
|
|
const currentRoofYRange = [Math.min(currentRoof.y1, currentRoof.y2), Math.max(currentRoof.y1, currentRoof.y2)]
|
|
const lineYRange = [Math.min(line.y1, line.y2), Math.max(line.y1, line.y2)]
|
|
const overlapYRange = [Math.max(currentRoofYRange[0], lineYRange[0]), Math.min(currentRoofYRange[1], lineYRange[1])]
|
|
if (overlapYRange[1] - overlapYRange[0] > 0) {
|
|
const centerX = Math.round(((currentRoof.x1 + line.x1) / 2) * 10) / 10
|
|
points = [centerX, overlapYRange[0], centerX, overlapYRange[1]]
|
|
}
|
|
}
|
|
if (currentRoof.y1 === currentRoof.y2) {
|
|
const currentRoofXRange = [Math.min(currentRoof.x1, currentRoof.x2), Math.max(currentRoof.x1, currentRoof.x2)]
|
|
const lineXRange = [Math.min(line.x1, line.x2), Math.max(line.x1, line.x2)]
|
|
const overlapXRange = [Math.max(currentRoofXRange[0], lineXRange[0]), Math.min(currentRoofXRange[1], lineXRange[1])]
|
|
if (overlapXRange[1] - overlapXRange[0] > 0) {
|
|
const centerY = Math.round(((currentRoof.y1 + line.y1) / 2) * 10) / 10
|
|
points = [overlapXRange[0], centerY, overlapXRange[1], centerY]
|
|
}
|
|
}
|
|
// 마루를 그린다.
|
|
if (points) {
|
|
const ridge = new QLine(points, {
|
|
parentId: roof.id,
|
|
fontSize: roof.fontSize,
|
|
stroke: '#1083E3',
|
|
strokeWidth: 1,
|
|
name: LINE_TYPE.SUBLINE.RIDGE,
|
|
attributes: { roofId: roof.id, currentRoofId: [currentRoof.id] },
|
|
visible: false,
|
|
})
|
|
const duplicateRidge = ridges.find(
|
|
(ridge) => ridge.x1 === points[0] && ridge.y1 === points[1] && ridge.x2 === points[2] && ridge.y2 === points[3],
|
|
)
|
|
// 중복된 마루는 제외한다.
|
|
if (duplicateRidge) {
|
|
duplicateRidge.attributes.currentRoofId.push(currentRoof.id)
|
|
} else {
|
|
canvas.add(ridge)
|
|
canvas.renderAll()
|
|
ridges.push(ridge)
|
|
}
|
|
}
|
|
})
|
|
})
|
|
// 처마마다 지붕 polygon 을 그린다.
|
|
eaves.forEach((eave) => {
|
|
const index = eave.index,
|
|
currentRoof = eave.roof
|
|
const currentWall = wallLines[index]
|
|
const currentRidges = ridges.filter((ridge) => ridge.attributes.currentRoofId.includes(eave.roof.id))
|
|
let points = []
|
|
const signX = Math.sign(currentRoof.x1 - currentRoof.x2)
|
|
let currentX1 = currentRoof.x1,
|
|
currentY1 = currentRoof.y1,
|
|
currentX2 = currentRoof.x2,
|
|
currentY2 = currentRoof.y2
|
|
let changeX1 = [Math.min(currentRoof.x1, currentRoof.x2), Math.min(currentRoof.x1, currentRoof.x2)],
|
|
changeY1 = [Math.min(currentRoof.y1, currentRoof.y2), Math.min(currentRoof.y1, currentRoof.y2)],
|
|
changeX2 = [Math.max(currentRoof.x2, currentRoof.x1), Math.max(currentRoof.x2, currentRoof.x1)],
|
|
changeY2 = [Math.max(currentRoof.y2, currentRoof.y1), Math.max(currentRoof.y2, currentRoof.y1)]
|
|
|
|
if (signX === 0) {
|
|
currentY1 = Math.min(currentRoof.y1, currentRoof.y2, currentWall.y1, currentWall.y2)
|
|
changeY1[1] = currentY1
|
|
currentY2 = Math.max(currentRoof.y1, currentRoof.y2, currentWall.y1, currentWall.y2)
|
|
changeY2[1] = currentY2
|
|
} else {
|
|
currentX1 = Math.min(currentRoof.x1, currentRoof.x2, currentWall.x1, currentWall.x2)
|
|
changeX1[1] = currentX1
|
|
currentX2 = Math.max(currentRoof.x1, currentRoof.x2, currentWall.x1, currentWall.x2)
|
|
changeX2[1] = currentX2
|
|
}
|
|
points.push({ x: currentX1, y: currentY1 }, { x: currentX2, y: currentY2 })
|
|
|
|
currentRidges.forEach((ridge) => {
|
|
let ridgeX1 = ridge.x1,
|
|
ridgeY1 = ridge.y1,
|
|
ridgeX2 = ridge.x2,
|
|
ridgeY2 = ridge.y2
|
|
if (signX === 0) {
|
|
ridgeY1 = Math.min(ridge.y1, ridge.y2)
|
|
ridgeY2 = Math.max(ridge.y1, ridge.y2)
|
|
} else {
|
|
ridgeX1 = Math.min(ridge.x1, ridge.x2)
|
|
ridgeX2 = Math.max(ridge.x1, ridge.x2)
|
|
}
|
|
points.push({ x: ridgeX1, y: ridgeY1 }, { x: ridgeX2, y: ridgeY2 })
|
|
})
|
|
|
|
points.forEach((point) => {
|
|
if (point.x === changeX1[0] && changeX1[0] !== changeX1[1]) {
|
|
point.x = changeX1[1]
|
|
}
|
|
if (point.x === changeX2[0] && changeX2[0] !== changeX2[1]) {
|
|
point.x = changeX2[1]
|
|
}
|
|
if (point.y === changeY1[0] && changeY1[0] !== changeY1[1]) {
|
|
point.y = changeY1[1]
|
|
}
|
|
if (point.y === changeY2[0] && changeY2[0] !== changeY2[1]) {
|
|
point.y = changeY2[1]
|
|
}
|
|
})
|
|
//중복된 point 제거
|
|
points = points.filter((point, index, self) => index === self.findIndex((p) => p.x === point.x && p.y === point.y))
|
|
//point 정렬 (가장 좌측, 최상단의 점을 기준으로 삼는다.)
|
|
const startPoint = points
|
|
.filter((point) => point.x === Math.min(...points.map((point) => point.x)))
|
|
.reduce((prev, current) => {
|
|
return prev.y < current.y ? prev : current
|
|
})
|
|
|
|
const sortedPoints = []
|
|
sortedPoints.push(startPoint)
|
|
|
|
points.forEach((p, index) => {
|
|
if (index === 0) {
|
|
//시작점 다음 점 찾기, y좌표가 startPoint.y 보다 큰 점 중 x좌표가 가까운 점
|
|
const underStartPoint = points.filter((point) => point.y > startPoint.y)
|
|
const nextPoint = underStartPoint
|
|
.filter((point) => point.x === startPoint.x)
|
|
.reduce((prev, current) => {
|
|
if (prev === undefined) {
|
|
return current
|
|
}
|
|
return Math.abs(prev.y - startPoint.y) < Math.abs(current.y - startPoint.y) ? prev : current
|
|
}, undefined)
|
|
if (nextPoint) {
|
|
sortedPoints.push(nextPoint)
|
|
} else {
|
|
const nextPoint = underStartPoint.reduce((prev, current) => {
|
|
const prevHypos = Math.sqrt(Math.abs(Math.pow(prev.x - startPoint.x, 2)) + Math.abs(Math.pow(prev.y - startPoint.y, 2)))
|
|
const currentHypos = Math.sqrt(Math.abs(Math.pow(current.x - startPoint.x, 2)) + Math.abs(Math.pow(current.y - startPoint.y, 2)))
|
|
return prevHypos < currentHypos ? prev : current
|
|
}, undefined)
|
|
sortedPoints.push(nextPoint)
|
|
}
|
|
} else {
|
|
const lastPoint = sortedPoints[sortedPoints.length - 1]
|
|
const prevPoint = sortedPoints[sortedPoints.length - 2]
|
|
const otherPoints = points.filter((point) => sortedPoints.includes(point) === false)
|
|
const nextPoint = otherPoints.reduce((prev, current) => {
|
|
if (prev === undefined) {
|
|
const height = Math.abs(Math.sqrt(Math.abs(Math.pow(prevPoint.x - lastPoint.x, 2)) + Math.abs(Math.pow(prevPoint.y - lastPoint.y, 2))))
|
|
const adjacent = Math.abs(Math.sqrt(Math.abs(Math.pow(current.x - lastPoint.x, 2)) + Math.abs(Math.pow(current.y - lastPoint.y, 2))))
|
|
const hypotenuse = Math.abs(Math.sqrt(Math.abs(Math.pow(current.x - prevPoint.x, 2)) + Math.abs(Math.pow(current.y - prevPoint.y, 2))))
|
|
|
|
const angle = Math.round(
|
|
Math.acos((Math.pow(adjacent, 2) + Math.pow(height, 2) - Math.pow(hypotenuse, 2)) / (2 * adjacent * height)) * (180 / Math.PI),
|
|
)
|
|
if (angle === 90) {
|
|
return current
|
|
}
|
|
} else {
|
|
return prev
|
|
}
|
|
}, undefined)
|
|
if (nextPoint) {
|
|
sortedPoints.push(nextPoint)
|
|
} else {
|
|
const nextPoint = otherPoints.reduce((prev, current) => {
|
|
if (prev !== undefined) {
|
|
const height = Math.abs(Math.sqrt(Math.abs(Math.pow(prevPoint.x - lastPoint.x, 2)) + Math.abs(Math.pow(prevPoint.y - lastPoint.y, 2))))
|
|
const adjacentC = Math.abs(Math.sqrt(Math.abs(Math.pow(current.x - lastPoint.x, 2)) + Math.abs(Math.pow(current.y - lastPoint.y, 2))))
|
|
const hypotenuseC = Math.abs(Math.sqrt(Math.abs(Math.pow(current.x - prevPoint.x, 2)) + Math.abs(Math.pow(current.y - prevPoint.y, 2))))
|
|
const angleC = Math.round(
|
|
Math.acos((Math.pow(adjacentC, 2) + Math.pow(height, 2) - Math.pow(hypotenuseC, 2)) / (2 * adjacentC * height)) * (180 / Math.PI),
|
|
)
|
|
const adjacentP = Math.abs(Math.sqrt(Math.abs(Math.pow(prev.x - lastPoint.x, 2)) + Math.abs(Math.pow(prev.y - lastPoint.y, 2))))
|
|
const hypotenuseP = Math.abs(Math.sqrt(Math.abs(Math.pow(prev.x - prevPoint.x, 2)) + Math.abs(Math.pow(prev.y - prevPoint.y, 2))))
|
|
const angleP = Math.round(
|
|
Math.acos((Math.pow(adjacentP, 2) + Math.pow(height, 2) - Math.pow(hypotenuseP, 2)) / (2 * adjacentP * height)) * (180 / Math.PI),
|
|
)
|
|
if (Math.abs(90 - angleC) < Math.abs(90 - angleP)) {
|
|
return current
|
|
} else {
|
|
return prev
|
|
}
|
|
} else {
|
|
return current
|
|
}
|
|
}, undefined)
|
|
if (nextPoint) {
|
|
sortedPoints.push(nextPoint)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|
|
if (sortedPoints.length > 0) {
|
|
const roofPolygon = new QPolygon(sortedPoints, {
|
|
parentId: roof.id,
|
|
fill: 'transparent',
|
|
stroke: '#1083E3',
|
|
strokeWidth: 2,
|
|
selectable: false,
|
|
fontSize: roof.fontSize,
|
|
name: 'roofPolygon',
|
|
attributes: {
|
|
roofId: roof.id,
|
|
currentRoofId: currentRoof.id,
|
|
pitch: currentRoof.attributes.pitch,
|
|
degree: currentRoof.attributes.degree,
|
|
direction: currentRoof.direction,
|
|
},
|
|
originX: 'center',
|
|
originY: 'center',
|
|
})
|
|
const currentDegree = currentRoof.attributes.pitch > 0 ? getDegreeByChon(currentRoof.attributes.pitch) : currentRoof.attributes.degree
|
|
//지붕 각도에 따른 실측치 조정
|
|
roofPolygon.lines.forEach((line) => {
|
|
line.attributes.planeSize = Math.round(Math.sqrt(Math.pow(line.x2 - line.x1, 2) + Math.pow(line.y2 - line.y1, 2)) * 10)
|
|
const slope = (line) => (line.x2 - line.x1 === 0 ? Infinity : (line.y2 - line.y1) / (line.x2 - line.x1))
|
|
|
|
if (currentDegree > 0 && slope(line) !== slope(currentRoof)) {
|
|
const height = Math.tan(currentDegree * (Math.PI / 180)) * line.attributes.planeSize
|
|
line.attributes.actualSize = Math.round(Math.sqrt(Math.pow(line.attributes.planeSize, 2) + Math.pow(height, 2)))
|
|
} else {
|
|
line.attributes.actualSize = line.attributes.planeSize
|
|
}
|
|
})
|
|
roof.separatePolygon.push(roofPolygon)
|
|
canvas.add(roofPolygon)
|
|
canvas.renderAll()
|
|
}
|
|
})
|
|
|
|
if (ridges.length > 0) {
|
|
ridges.forEach((ridge) => roof.innerLines.push(ridge))
|
|
}
|
|
//기준선 제거
|
|
// ridges.forEach((ridge) => canvas.remove(ridge))
|
|
|
|
eaves.forEach((eave) => {
|
|
const currentRoof = eave.roof
|
|
let currentRidges = ridges.filter((ridge) => ridge.attributes.currentRoofId.includes(currentRoof.id))
|
|
if (Math.sign(currentRoof.x1 - currentRoof.x2) === 0) {
|
|
currentRidges = currentRidges.reduce((farthest, ridge) => {
|
|
const currentDistance = Math.abs(ridge.x1 - currentRoof.x1)
|
|
const farthestDistance = farthest ? Math.abs(farthest.x1 - currentRoof.x1) : 0
|
|
return currentDistance > farthestDistance ? ridge : farthest
|
|
}, null)
|
|
} else {
|
|
currentRidges = currentRidges.reduce((farthest, ridge) => {
|
|
const currentDistance = Math.abs(ridge.y1 - currentRoof.y1)
|
|
const farthestDistance = farthest ? Math.abs(farthest.y1 - currentRoof.y1) : 0
|
|
return currentDistance > farthestDistance ? ridge : farthest
|
|
}, null)
|
|
}
|
|
if (currentRidges) {
|
|
const ridgeMidX = (currentRidges.x1 + currentRidges.x2) / 2
|
|
const ridgeMidY = (currentRidges.y1 + currentRidges.y2) / 2
|
|
const x2 = Math.sign(currentRidges.x1 - currentRidges.x2) === 0 ? currentRoof.x1 : ridgeMidX
|
|
const y2 = Math.sign(currentRidges.x1 - currentRidges.x2) === 0 ? ridgeMidY : currentRoof.y1
|
|
|
|
const pitchSizeLine = new QLine([ridgeMidX, ridgeMidY, x2, y2], {
|
|
parentId: roof.id,
|
|
stroke: '#000000',
|
|
strokeWidth: 2,
|
|
strokeDashArray: [5, 5],
|
|
selectable: false,
|
|
fontSize: roof.fontSize,
|
|
attributes: {
|
|
roofId: roof.id,
|
|
type: 'pitchSizeLine',
|
|
},
|
|
})
|
|
canvas.add(pitchSizeLine)
|
|
canvas.renderAll()
|
|
|
|
const adjust = Math.sqrt(
|
|
Math.pow(Math.round(Math.abs(pitchSizeLine.x1 - pitchSizeLine.x2) * 10), 2) +
|
|
Math.pow(Math.round(Math.abs(pitchSizeLine.y1 - pitchSizeLine.y2) * 10), 2),
|
|
)
|
|
const currentDegree = currentRoof.attributes.pitch > 0 ? getDegreeByChon(currentRoof.attributes.pitch) : currentRoof.attributes.degree
|
|
const height = Math.tan(currentDegree * (Math.PI / 180)) * adjust
|
|
const lengthText = Math.round(Math.sqrt(Math.pow(adjust, 2) + Math.pow(height, 2)))
|
|
pitchSizeLine.setLengthText(lengthText)
|
|
}
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 한쪽흐름 지붕
|
|
* @param roofId
|
|
* @param canvas
|
|
* @param textMode
|
|
*/
|
|
export const drawShedRoof = (roofId, canvas, textMode) => {
|
|
const roof = canvas?.getObjects().find((object) => object.id === roofId)
|
|
const hasNonParallelLines = roof.lines.filter((line) => Math.abs(line.x1 - line.x2) > 1 && Math.abs(line.y1 - line.y2) > 1)
|
|
if (hasNonParallelLines.length > 0) {
|
|
// alert('대각선이 존재합니다.')
|
|
return
|
|
}
|
|
|
|
const sheds = roof.lines.filter((line) => line.attributes?.type === LINE_TYPE.WALLLINE.SHED)
|
|
const gables = roof.lines.filter((line) => line.attributes?.type === LINE_TYPE.WALLLINE.GABLE)
|
|
const eaves = roof.lines.filter((line) => line.attributes?.type === LINE_TYPE.WALLLINE.EAVES)
|
|
|
|
let shedDegree = sheds[0].attributes.degree || 0
|
|
const shedChon = sheds[0].attributes.pitch || 0
|
|
|
|
if (shedDegree === 0) {
|
|
shedDegree = getDegreeByChon(shedChon)
|
|
}
|
|
const getHeight = function (adjust, degree) {
|
|
return Math.tan(degree * (Math.PI / 180)) * adjust
|
|
}
|
|
|
|
gables.forEach(
|
|
(gable) =>
|
|
(gable.attributes.actualSize = calcLineActualSize(
|
|
{
|
|
x1: gable.x1,
|
|
y1: gable.y1,
|
|
x2: gable.x2,
|
|
y2: gable.y2,
|
|
},
|
|
shedDegree,
|
|
)),
|
|
)
|
|
|
|
const pitchSizeLines = []
|
|
|
|
sheds.forEach((shed) => {
|
|
let points = []
|
|
let x1, y1, x2, y2
|
|
const signX = Math.sign(shed.x1 - shed.x2)
|
|
if (signX !== 0) {
|
|
points.push(shed.x1, shed.x2)
|
|
y1 = shed.y1
|
|
} else {
|
|
points.push(shed.y1, shed.y2)
|
|
x1 = shed.x1
|
|
}
|
|
eaves.forEach((eave) => {
|
|
if (signX !== 0) {
|
|
points.push(eave.x1, eave.x2)
|
|
points.sort((a, b) => a - b)
|
|
x1 = (points[1] + points[2]) / 2
|
|
x2 = (points[1] + points[2]) / 2
|
|
y2 = eave.y1
|
|
} else {
|
|
points.push(eave.y1, eave.y2)
|
|
points.sort((a, b) => a - b)
|
|
y1 = (points[1] + points[2]) / 2
|
|
y2 = (points[1] + points[2]) / 2
|
|
x2 = eave.x1
|
|
}
|
|
points.sort((a, b) => a - b)
|
|
// const planeSize = Math.round(Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)) * 10)
|
|
const planeSize = calcLinePlaneSize({ x1, y1, x2, y2 })
|
|
// const actualSize = Math.round(Math.sqrt (Math.pow(planeSize, 2) + Math.pow(getHeight(planeSize, shedDegree), 2)) * 10)
|
|
const actualSize = calcLineActualSize({ x1, y1, x2, y2 }, shedDegree)
|
|
|
|
const line = new QLine([x1, y1, x2, y2], {
|
|
parentId: roof.id,
|
|
stroke: '#000000',
|
|
strokeWidth: 2,
|
|
strokeDashArray: [5, 5],
|
|
selectable: false,
|
|
fontSize: roof.fontSize,
|
|
textMode: textMode,
|
|
attributes: {
|
|
roofId: roof.id,
|
|
type: 'pitchSizeLine',
|
|
planeSize: actualSize,
|
|
actualSize: actualSize,
|
|
},
|
|
})
|
|
pitchSizeLines.push(line)
|
|
})
|
|
})
|
|
|
|
const maxLine = pitchSizeLines.reduce((prev, current) => (prev.length > current.length ? prev : current), pitchSizeLines[0])
|
|
canvas.add(maxLine)
|
|
canvas.renderAll()
|
|
}
|
|
|
|
/**
|
|
* 마루가 있는 지붕을 그린다.
|
|
* @param roofId
|
|
* @param canvas
|
|
* @param textMode
|
|
*/
|
|
export const drawRidgeRoof = (roofId, canvas, textMode) => {
|
|
const roof = canvas?.getObjects().find((object) => object.id === roofId)
|
|
const wall = canvas.getObjects().find((object) => object.name === POLYGON_TYPE.WALL && object.attributes.roofId === roofId)
|
|
|
|
const hasNonParallelLines = roof.lines.filter((line) => Big(line.x1).minus(Big(line.x2)).gt(1) && Big(line.y1).minus(Big(line.y2)).gt(1))
|
|
if (hasNonParallelLines.length > 0) {
|
|
return
|
|
}
|
|
|
|
const eavesType = [LINE_TYPE.WALLLINE.EAVES, LINE_TYPE.WALLLINE.HIPANDGABLE]
|
|
const gableType = [LINE_TYPE.WALLLINE.GABLE, LINE_TYPE.WALLLINE.JERKINHEAD]
|
|
|
|
/** 외벽선 */
|
|
const baseLines = wall.baseLines.filter((line) => line.attributes.planeSize > 0)
|
|
const baseLinePoints = baseLines.map((line) => ({ x: line.x1, y: line.y1 }))
|
|
|
|
/** 모양 판단을 위한 라인 처리.
|
|
* 평행한 라인이 나누어져 있는 경우 하나의 선으로 판단 한다.
|
|
*/
|
|
const drawBaseLines = []
|
|
baseLines.forEach((currentLine, index) => {
|
|
let nextLine = baseLines[(index + 1) % baseLines.length]
|
|
let prevLine = baseLines[(index - 1 + baseLines.length) % baseLines.length]
|
|
const currentAngle = calculateAngle(currentLine.startPoint, currentLine.endPoint)
|
|
const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint)
|
|
const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint)
|
|
|
|
let { x1, y1, x2, y2 } = currentLine
|
|
|
|
if (currentAngle !== prevAngle || (currentAngle === prevAngle && currentLine.attributes.type !== prevLine.attributes.type)) {
|
|
if (currentAngle === nextAngle && currentLine.attributes.type === nextLine.attributes.type) {
|
|
let nextIndex = baseLines.findIndex((line) => line === nextLine)
|
|
while (nextIndex !== index) {
|
|
const checkNextLine = baseLines[(nextIndex + 1 + baseLines.length) % baseLines.length]
|
|
const checkAngle = calculateAngle(checkNextLine.startPoint, checkNextLine.endPoint)
|
|
if (currentAngle !== checkAngle) {
|
|
x2 = checkNextLine.x1
|
|
y2 = checkNextLine.y1
|
|
break
|
|
} else {
|
|
nextIndex = nextIndex + 1
|
|
}
|
|
}
|
|
}
|
|
drawBaseLines.push({ x1, y1, x2, y2, line: currentLine, size: calcLinePlaneSize({ x1, y1, x2, y2 }) })
|
|
}
|
|
})
|
|
|
|
/** baseLine을 기준으로 확인용 polygon 작성 */
|
|
const checkWallPolygon = new QPolygon(baseLinePoints, {})
|
|
|
|
/**
|
|
* 외벽선이 시계방향인지 시계반대 방향인지 확인
|
|
* @type {boolean}
|
|
*/
|
|
let counterClockwise = true
|
|
let signedArea = 0
|
|
|
|
baseLinePoints.forEach((point, index) => {
|
|
const nextPoint = baseLinePoints[(index + 1) % baseLinePoints.length]
|
|
signedArea += point.x * nextPoint.y - point.y * nextPoint.x
|
|
})
|
|
|
|
if (signedArea > 0) {
|
|
counterClockwise = false
|
|
}
|
|
|
|
const drawEavesFirstLines = []
|
|
const drawEavesSecondLines = []
|
|
const drawGableRidgeFirst = []
|
|
const drawGableRidgeSecond = []
|
|
const drawGablePolygonFirst = []
|
|
const drawGablePolygonSecond = []
|
|
/** 모양을 판단한다. */
|
|
drawBaseLines.forEach((currentBaseLine, index) => {
|
|
let prevBaseLine = drawBaseLines[(index - 1 + drawBaseLines.length) % drawBaseLines.length]
|
|
let nextBaseLine = drawBaseLines[(index + 1) % drawBaseLines.length]
|
|
const currentLine = currentBaseLine.line
|
|
const prevLine = prevBaseLine.line
|
|
const nextLine = nextBaseLine.line
|
|
const currentAngle = calculateAngle(currentLine.startPoint, currentLine.endPoint)
|
|
const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint)
|
|
const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint)
|
|
|
|
const checkScale = Big(10)
|
|
const xVector = Big(nextLine.x2).minus(Big(nextLine.x1))
|
|
const yVector = Big(nextLine.y2).minus(Big(nextLine.y1))
|
|
const currentMidX = Big(currentLine.x1).plus(Big(currentLine.x2)).div(2)
|
|
const currentMidY = Big(currentLine.y1).plus(Big(currentLine.y2)).div(2)
|
|
|
|
const checkPoints = {
|
|
x: currentMidX.plus(checkScale.times(Math.sign(xVector.toNumber()))).toNumber(),
|
|
y: currentMidY.plus(checkScale.times(Math.sign(yVector.toNumber()))).toNumber(),
|
|
}
|
|
|
|
/** 현재 라인이 처마유형일 경우 */
|
|
if (eavesType.includes(currentLine.attributes?.type)) {
|
|
if (eavesType.includes(nextLine.attributes?.type)) {
|
|
/**
|
|
* 이전, 다음 라인이 처마일때 라인의 방향이 반대면 ㄷ 모양으로 판단한다.
|
|
*/
|
|
if (
|
|
eavesType.includes(prevLine.attributes?.type) &&
|
|
eavesType.includes(nextLine.attributes?.type) &&
|
|
Big(prevAngle).minus(Big(nextAngle)).abs().eq(180)
|
|
) {
|
|
/**
|
|
* 다음라인 방향에 포인트를 확인해서 역방향 ㄷ 모양인지 판단한다.
|
|
* @type {{x: *, y: *}}
|
|
*/
|
|
if (checkWallPolygon.inPolygon(checkPoints)) {
|
|
drawEavesFirstLines.push({ currentBaseLine, prevBaseLine, nextBaseLine })
|
|
} else {
|
|
drawEavesSecondLines.push({ currentBaseLine, prevBaseLine, nextBaseLine })
|
|
}
|
|
} else {
|
|
drawEavesSecondLines.push({ currentBaseLine, prevBaseLine, nextBaseLine })
|
|
}
|
|
} else if (gableType.includes(nextLine.attributes?.type) && gableType.includes(prevLine.attributes?.type)) {
|
|
if (Big(prevAngle).minus(Big(nextAngle)).abs().eq(180)) {
|
|
const checkPoints = {
|
|
x: currentMidX.plus(checkScale.times(Math.sign(xVector.toNumber()))).toNumber(),
|
|
y: currentMidY.plus(checkScale.times(Math.sign(yVector.toNumber()))).toNumber(),
|
|
}
|
|
|
|
if (checkWallPolygon.inPolygon(checkPoints)) {
|
|
drawGablePolygonFirst.push({ currentBaseLine, prevBaseLine, nextBaseLine })
|
|
} else {
|
|
drawGablePolygonSecond.push({ currentBaseLine, prevBaseLine, nextBaseLine })
|
|
}
|
|
// if (!checkWallPolygon.inPolygon(checkPoints)) {
|
|
drawGableRidgeSecond.push({ currentBaseLine, prevBaseLine, nextBaseLine })
|
|
// }
|
|
} else {
|
|
if (currentAngle !== prevAngle && currentAngle !== nextAngle) {
|
|
drawGablePolygonSecond.push({ currentBaseLine, prevBaseLine, nextBaseLine })
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (gableType.includes(currentLine.attributes?.type)) {
|
|
if (eavesType.includes(nextLine.attributes?.type)) {
|
|
if (
|
|
eavesType.includes(prevLine.attributes?.type) &&
|
|
eavesType.includes(nextLine.attributes?.type) &&
|
|
Big(prevAngle).minus(Big(nextAngle)).abs().eq(180)
|
|
) {
|
|
if (checkWallPolygon.inPolygon(checkPoints)) {
|
|
drawGableRidgeFirst.push({ currentBaseLine, prevBaseLine, nextBaseLine })
|
|
} else {
|
|
drawGableRidgeSecond.push({ currentBaseLine, prevBaseLine, nextBaseLine })
|
|
}
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|
|
drawEavesFirstLines.sort((a, b) => a.currentBaseLine.size - b.currentBaseLine.size)
|
|
drawGableRidgeFirst.sort((a, b) => a.currentBaseLine.size - b.currentBaseLine.size)
|
|
drawGableRidgeSecond.sort((a, b) => a.currentBaseLine.size - b.currentBaseLine.size)
|
|
|
|
/** 추녀마루 */
|
|
let baseHipLines = []
|
|
/** 용마루 */
|
|
let baseRidgeLines = []
|
|
/** 박공지붕 마루*/
|
|
let baseGableRidgeLines = []
|
|
|
|
/** 박공지붕 라인*/
|
|
let baseGableLines = []
|
|
/** 용마루의 갯수*/
|
|
let baseRidgeCount = 0
|
|
|
|
// console.log('drawGableFirstLines :', drawGableFirstLines)
|
|
// console.log('drawGableSecondLines :', drawGableSecondLines)
|
|
// console.log('drawEavesFirstLines :', drawEavesFirstLines)
|
|
// console.log('drawEavesSecondLines :', drawEavesSecondLines)
|
|
// console.log('drawGableRidgeFirst : ', drawGableRidgeFirst)
|
|
// console.log('drawGableRidgeSecond : ', drawGableRidgeSecond)
|
|
// console.log('drawGablePolygonFirst :', drawGablePolygonFirst)
|
|
// console.log('drawGablePolygonSecond :', drawGablePolygonSecond)
|
|
|
|
/** 박공지붕에서 파생되는 마루를 그린다. ㄷ 형태*/
|
|
drawGableRidgeFirst.forEach((current) => {
|
|
const { currentBaseLine, prevBaseLine, nextBaseLine } = current
|
|
const currentLine = currentBaseLine.line
|
|
const prevLine = prevBaseLine.line
|
|
const nextLine = nextBaseLine.line
|
|
|
|
let { x1, x2, y1, y2, size } = currentBaseLine
|
|
let beforePrevBaseLine, afterNextBaseLine
|
|
|
|
/** 이전 라인의 경사 */
|
|
const prevDegree = prevLine.attributes.pitch > 0 ? getDegreeByChon(prevLine.attributes.pitch) : prevLine.attributes.degree
|
|
/** 다음 라인의 경사 */
|
|
const nextDegree = nextLine.attributes.pitch > 0 ? getDegreeByChon(nextLine.attributes.pitch) : nextLine.attributes.degree
|
|
/** 현재 라인의 경사 */
|
|
const currentDegree = currentLine.attributes.pitch > 0 ? getDegreeByChon(currentLine.attributes.pitch) : currentLine.attributes.degree
|
|
|
|
/** 이전 라인의 전라인, 다음 라인의 다음라인을 찾는다 */
|
|
drawBaseLines.forEach((line, index) => {
|
|
if (line === prevBaseLine) {
|
|
beforePrevBaseLine = drawBaseLines[(index - 1 + drawBaseLines.length) % drawBaseLines.length]
|
|
}
|
|
if (line === nextBaseLine) {
|
|
afterNextBaseLine = drawBaseLines[(index + 1) % drawBaseLines.length]
|
|
}
|
|
})
|
|
|
|
const beforePrevLine = beforePrevBaseLine?.line
|
|
const afterNextLine = afterNextBaseLine?.line
|
|
|
|
/** 각 라인의 흐름 방향을 확인한다. */
|
|
const currentAngle = calculateAngle(currentLine.startPoint, currentLine.endPoint)
|
|
const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint)
|
|
const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint)
|
|
const beforePrevAngle = calculateAngle(beforePrevLine.startPoint, beforePrevLine.endPoint)
|
|
const afterNextAngle = calculateAngle(afterNextLine.startPoint, afterNextLine.endPoint)
|
|
|
|
/** 현재라인의 vector*/
|
|
const currentVectorX = Math.sign(Big(x2).minus(Big(x1)).toNumber())
|
|
const currentVectorY = Math.sign(Big(y2).minus(Big(y1)).toNumber())
|
|
|
|
/** 이전라인의 vector*/
|
|
const prevVectorX = Math.sign(Big(prevLine.x2).minus(Big(prevLine.x1)))
|
|
const prevVectorY = Math.sign(Big(prevLine.y2).minus(Big(prevLine.y1)))
|
|
|
|
/** 다음라인의 vector*/
|
|
const nextVectorX = Math.sign(Big(nextLine.x2).minus(Big(nextLine.x1)))
|
|
const nextVectorY = Math.sign(Big(nextLine.y2).minus(Big(nextLine.y1)))
|
|
|
|
/** 현재라인의 기준점*/
|
|
let currentMidX = Big(x1).plus(Big(x2)).div(2).plus(Big(prevVectorX).times(currentLine.attributes.offset))
|
|
let currentMidY = Big(y1).plus(Big(y2)).div(2).plus(Big(prevVectorY).times(currentLine.attributes.offset))
|
|
|
|
/** 마루 반대 좌표*/
|
|
let oppositeMidX = currentMidX,
|
|
oppositeMidY = currentMidY
|
|
|
|
/** 한개의 지붕선을 둘로 나누어서 처리 하는 경우 */
|
|
if (prevAngle === beforePrevAngle || nextAngle === afterNextAngle) {
|
|
if (currentVectorX === 0) {
|
|
const addLength = Big(currentLine.y1).minus(Big(currentLine.y2)).abs().div(2)
|
|
const ridgeVector = Math.sign(prevLine.x1 - currentLine.x1)
|
|
oppositeMidX = Big(prevLine.x1).plus(Big(addLength).times(ridgeVector))
|
|
|
|
const ridgeEdge = {
|
|
vertex1: { x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() },
|
|
vertex2: { x: currentMidX.toNumber(), y: currentMidY.toNumber() },
|
|
}
|
|
|
|
const ridgeVectorX = Math.sign(ridgeEdge.vertex1.x - ridgeEdge.vertex2.x)
|
|
roof.lines
|
|
.filter((line) => line.x1 === line.x2)
|
|
.forEach((line) => {
|
|
const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
|
|
const is = edgesIntersection(ridgeEdge, lineEdge)
|
|
if (is) {
|
|
const isVectorX = Math.sign(ridgeEdge.vertex1.x - is.x)
|
|
if (
|
|
isVectorX === ridgeVectorX &&
|
|
((line.x1 <= currentMidX && line.x2 >= currentMidX) || (line.x2 <= currentMidX && line.x1 >= currentMidX))
|
|
) {
|
|
currentMidX = Big(is.x)
|
|
currentMidY = Big(is.y)
|
|
}
|
|
}
|
|
})
|
|
} else {
|
|
const addLength = Big(currentLine.x1).minus(Big(currentLine.x2)).abs().div(2)
|
|
const ridgeVector = Math.sign(prevLine.y1 - currentLine.y1)
|
|
oppositeMidY = Big(prevLine.y1).plus(addLength.times(ridgeVector))
|
|
|
|
const ridgeEdge = {
|
|
vertex1: { x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() },
|
|
vertex2: { x: currentMidX.toNumber(), y: currentMidY.toNumber() },
|
|
}
|
|
const ridgeVectorY = Math.sign(ridgeEdge.vertex1.y - ridgeEdge.vertex2.y)
|
|
roof.lines
|
|
.filter((line) => line.y1 === line.y2)
|
|
.forEach((line) => {
|
|
const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
|
|
const is = edgesIntersection(ridgeEdge, lineEdge)
|
|
if (is) {
|
|
const isVectorY = Math.sign(ridgeEdge.vertex1.y - is.y)
|
|
if (
|
|
isVectorY === ridgeVectorY &&
|
|
((line.x1 <= currentMidX && line.x2 >= currentMidX) || (line.x2 <= currentMidX && line.x1 >= currentMidX))
|
|
) {
|
|
currentMidX = Big(is.x)
|
|
currentMidY = Big(is.y)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
const prevHipEdge = {
|
|
vertex1: { x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() },
|
|
vertex2: { x: prevLine.x1, y: prevLine.y1 },
|
|
}
|
|
const prevHipVectorX = Math.sign(prevHipEdge.vertex1.x - prevHipEdge.vertex2.x)
|
|
const prevHipVectorY = Math.sign(prevHipEdge.vertex1.y - prevHipEdge.vertex2.y)
|
|
const prevIsPoints = []
|
|
roof.lines
|
|
.filter((line) => (Math.sign(prevLine.x1 - prevLine.x2) === 0 ? line.x1 === line.x2 : line.y1 === line.y2))
|
|
.forEach((line) => {
|
|
const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
|
|
const is = edgesIntersection(prevHipEdge, lineEdge)
|
|
if (is) {
|
|
const isVectorX = Math.sign(prevHipEdge.vertex1.x - is.x)
|
|
const isVectorY = Math.sign(prevHipEdge.vertex1.y - is.y)
|
|
if (isVectorX === prevHipVectorX && isVectorY === prevHipVectorY) {
|
|
const size = Big(prevHipEdge.vertex1.x)
|
|
.minus(Big(is.x))
|
|
.abs()
|
|
.pow(2)
|
|
.plus(Big(prevHipEdge.vertex1.y).minus(Big(is.y)).abs().pow(2))
|
|
.sqrt()
|
|
.toNumber()
|
|
prevIsPoints.push({ is, size })
|
|
}
|
|
}
|
|
})
|
|
|
|
if (prevIsPoints.length > 0) {
|
|
const prevIs = prevIsPoints.sort((a, b) => a.size - b.size)[0].is
|
|
const prevHipLine = drawHipLine(
|
|
[prevIs.x, prevIs.y, oppositeMidX.toNumber(), oppositeMidY.toNumber()],
|
|
canvas,
|
|
roof,
|
|
textMode,
|
|
null,
|
|
prevDegree,
|
|
prevDegree,
|
|
)
|
|
baseHipLines.push({
|
|
x1: prevLine.x1,
|
|
y1: prevLine.y1,
|
|
x2: oppositeMidX.toNumber(),
|
|
y2: oppositeMidY.toNumber(),
|
|
line: prevHipLine,
|
|
})
|
|
}
|
|
|
|
const nextHipEdge = {
|
|
vertex1: { x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() },
|
|
vertex2: { x: nextLine.x2, y: nextLine.y2 },
|
|
}
|
|
const nextHipVectorX = Math.sign(nextHipEdge.vertex1.x - nextHipEdge.vertex2.x)
|
|
const nextHipVectorY = Math.sign(nextHipEdge.vertex1.y - nextHipEdge.vertex2.y)
|
|
const nextIsPoints = []
|
|
|
|
roof.lines
|
|
.filter((line) => (Math.sign(nextLine.x1 - nextLine.x2) === 0 ? line.x1 === line.x2 : line.y1 === line.y2))
|
|
.forEach((line) => {
|
|
const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
|
|
const is = edgesIntersection(nextHipEdge, lineEdge)
|
|
if (is) {
|
|
const isVectorX = Math.sign(nextHipEdge.vertex1.x - is.x)
|
|
const isVectorY = Math.sign(nextHipEdge.vertex1.y - is.y)
|
|
if (isVectorX === nextHipVectorX && isVectorY === nextHipVectorY) {
|
|
const size = Big(nextHipEdge.vertex1.x)
|
|
.minus(Big(is.x))
|
|
.abs()
|
|
.pow(2)
|
|
.plus(Big(nextHipEdge.vertex1.y).minus(Big(is.y)).abs().pow(2))
|
|
.sqrt()
|
|
.toNumber()
|
|
nextIsPoints.push({ is, size })
|
|
}
|
|
}
|
|
})
|
|
if (nextIsPoints.length > 0) {
|
|
const nextIs = nextIsPoints.sort((a, b) => a.size - b.size)[0].is
|
|
const nextHipLine = drawHipLine(
|
|
[nextIs.x, nextIs.y, oppositeMidX.toNumber(), oppositeMidY.toNumber()],
|
|
canvas,
|
|
roof,
|
|
textMode,
|
|
null,
|
|
nextDegree,
|
|
nextDegree,
|
|
)
|
|
baseHipLines.push({
|
|
x1: nextLine.x2,
|
|
y1: nextLine.y2,
|
|
x2: oppositeMidX.toNumber(),
|
|
y2: oppositeMidY.toNumber(),
|
|
line: nextHipLine,
|
|
})
|
|
}
|
|
|
|
if (baseRidgeCount < getMaxRidge(baseLines.length)) {
|
|
const ridgeLine = drawRidgeLine(
|
|
[currentMidX.toNumber(), currentMidY.toNumber(), oppositeMidX.toNumber(), oppositeMidY.toNumber()],
|
|
canvas,
|
|
roof,
|
|
textMode,
|
|
)
|
|
baseGableRidgeLines.push(ridgeLine)
|
|
baseRidgeCount++
|
|
}
|
|
} else {
|
|
if (beforePrevBaseLine === afterNextBaseLine) {
|
|
const afterNextMidX = Big(afterNextLine.x1).plus(Big(afterNextLine.x2)).div(2)
|
|
const afterNextMidY = Big(afterNextLine.y1).plus(Big(afterNextLine.y2)).div(2)
|
|
const vectorMidX = Math.sign(currentMidX.minus(afterNextMidX))
|
|
const vectorMidY = Math.sign(currentMidY.minus(afterNextMidY))
|
|
|
|
let oppositeMidX, oppositeMidY
|
|
if (eavesType.includes(afterNextLine.attributes?.type)) {
|
|
const checkSize = currentMidX
|
|
.minus(afterNextMidX)
|
|
.pow(2)
|
|
.plus(currentMidY.minus(afterNextMidY).pow(2))
|
|
.sqrt()
|
|
.minus(Big(afterNextLine.attributes.planeSize).div(20))
|
|
.round(1)
|
|
oppositeMidX = currentMidX.plus(checkSize.times(vectorMidX).neg())
|
|
oppositeMidY = currentMidY.plus(checkSize.times(vectorMidY).neg())
|
|
|
|
const xVector1 = Math.sign(Big(oppositeMidX).minus(Big(afterNextLine.x1)).neg().toNumber())
|
|
const yVector1 = Math.sign(Big(oppositeMidY).minus(Big(afterNextLine.y1)).neg().toNumber())
|
|
const xVector2 = Math.sign(Big(oppositeMidX).minus(Big(afterNextLine.x2)).neg().toNumber())
|
|
const yVector2 = Math.sign(Big(oppositeMidY).minus(Big(afterNextLine.y2)).neg().toNumber())
|
|
|
|
let addOppositeX1 = 0,
|
|
addOppositeY1 = 0,
|
|
addOppositeX2 = 0,
|
|
addOppositeY2 = 0
|
|
|
|
if (!checkWallPolygon.inPolygon({ x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() })) {
|
|
const checkScale = currentMidX.minus(oppositeMidX).pow(2).plus(currentMidY.minus(oppositeMidY).pow(2)).sqrt()
|
|
addOppositeX1 = checkScale.times(xVector1).toNumber()
|
|
addOppositeY1 = checkScale.times(yVector1).toNumber()
|
|
addOppositeX2 = checkScale.times(xVector2).toNumber()
|
|
addOppositeY2 = checkScale.times(yVector2).toNumber()
|
|
}
|
|
|
|
let scale1 = Big(afterNextLine.attributes.offset).pow(2).plus(Big(nextLine.attributes.offset).pow(2)).sqrt()
|
|
scale1 = scale1.eq(0) ? Big(1) : scale1
|
|
let scale2 = Big(afterNextLine.attributes.offset).pow(2).plus(Big(prevLine.attributes.offset).pow(2)).sqrt()
|
|
scale2 = scale2.eq(0) ? Big(1) : scale2
|
|
|
|
const checkHip1 = {
|
|
x1: Big(afterNextLine.x1).plus(scale1.times(xVector1)).toNumber(),
|
|
y1: Big(afterNextLine.y1).plus(scale1.times(yVector1)).toNumber(),
|
|
x2: oppositeMidX.plus(addOppositeX1).toNumber(),
|
|
y2: oppositeMidY.plus(addOppositeY1).toNumber(),
|
|
}
|
|
|
|
const checkHip2 = {
|
|
x1: Big(afterNextLine.x2).plus(scale2.times(xVector2)).toNumber(),
|
|
y1: Big(afterNextLine.y2).plus(scale2.times(yVector2)).toNumber(),
|
|
x2: oppositeMidX.plus(addOppositeX2).toNumber(),
|
|
y2: oppositeMidY.plus(addOppositeY2).toNumber(),
|
|
}
|
|
|
|
const intersection1 = findRoofIntersection(roof, checkHip1, {
|
|
x: oppositeMidX.plus(addOppositeX1),
|
|
y: oppositeMidY.plus(addOppositeY1),
|
|
})
|
|
const intersection2 = findRoofIntersection(roof, checkHip2, {
|
|
x: oppositeMidX.plus(addOppositeX2),
|
|
y: oppositeMidY.plus(addOppositeY2),
|
|
})
|
|
|
|
const afterNextDegree =
|
|
afterNextLine.attributes.pitch > 0 ? getDegreeByChon(afterNextLine.attributes.pitch) : afterNextLine.attributes.degree
|
|
|
|
if (intersection1) {
|
|
const hipLine = drawHipLine(
|
|
[intersection1.intersection.x, intersection1.intersection.y, oppositeMidX.plus(addOppositeX1), oppositeMidY.plus(addOppositeY1)],
|
|
canvas,
|
|
roof,
|
|
textMode,
|
|
null,
|
|
nextDegree,
|
|
afterNextDegree,
|
|
)
|
|
baseHipLines.push({
|
|
x1: afterNextLine.x1,
|
|
y1: afterNextLine.y1,
|
|
x2: oppositeMidX.plus(addOppositeX1).toNumber(),
|
|
y2: oppositeMidY.plus(addOppositeY1).toNumber(),
|
|
line: hipLine,
|
|
})
|
|
}
|
|
if (intersection2) {
|
|
const hipLine = drawHipLine(
|
|
[intersection2.intersection.x, intersection2.intersection.y, oppositeMidX.plus(addOppositeX2), oppositeMidY.plus(addOppositeY2)],
|
|
canvas,
|
|
roof,
|
|
textMode,
|
|
null,
|
|
prevDegree,
|
|
afterNextDegree,
|
|
)
|
|
baseHipLines.push({
|
|
x1: afterNextLine.x2,
|
|
y1: afterNextLine.y2,
|
|
x2: oppositeMidX.plus(addOppositeX2).toNumber(),
|
|
y2: oppositeMidY.plus(addOppositeY2).toNumber(),
|
|
line: hipLine,
|
|
})
|
|
}
|
|
} else {
|
|
oppositeMidX = Big(afterNextLine.x1).plus(Big(afterNextLine.x2)).div(2).plus(Big(prevVectorX).neg().times(afterNextLine.attributes.offset))
|
|
oppositeMidY = Big(afterNextLine.y1).plus(Big(afterNextLine.y2)).div(2).plus(Big(prevVectorY).neg().times(afterNextLine.attributes.offset))
|
|
}
|
|
|
|
const vectorOppositeX = Math.sign(currentMidX.minus(oppositeMidX))
|
|
const vectorOppositeY = Math.sign(currentMidY.minus(oppositeMidY))
|
|
|
|
if (vectorMidX === vectorOppositeX && vectorMidY === vectorOppositeY && baseRidgeCount < getMaxRidge(baseLines.length)) {
|
|
if (!roof.inPolygon({ x: currentMidX.toNumber(), y: currentMidY.toNumber() })) {
|
|
const checkEdge = {
|
|
vertex1: { x: currentMidX.toNumber(), y: currentMidY.toNumber() },
|
|
vertex2: { x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() },
|
|
}
|
|
const intersectionPoints = []
|
|
|
|
roof.lines
|
|
.filter((line) => (currentVectorX === 0 ? line.x1 === line.x2 : line.y1 === line.y2))
|
|
.forEach((line) => {
|
|
const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
|
|
const intersection = edgesIntersection(checkEdge, lineEdge)
|
|
if (
|
|
intersection &&
|
|
((line.x1 <= intersection.x && line.x2 >= intersection.x && line.y1 <= intersection.y && line.y2 >= intersection.y) ||
|
|
(line.x2 <= intersection.x && line.x1 >= intersection.x && line.y2 <= intersection.y && line.y1 >= intersection.y))
|
|
) {
|
|
const size = Big(intersection.x)
|
|
.minus(currentMidX)
|
|
.abs()
|
|
.pow(2)
|
|
.plus(Big(intersection.y).minus(currentMidY).abs().pow(2))
|
|
.sqrt()
|
|
.toNumber()
|
|
intersectionPoints.push({ intersection, size })
|
|
}
|
|
})
|
|
|
|
if (intersectionPoints.length > 0) {
|
|
const intersection = intersectionPoints.sort((a, b) => a.size - b.size)[0].intersection
|
|
currentMidX = Big(intersection.x)
|
|
currentMidY = Big(intersection.y)
|
|
}
|
|
}
|
|
|
|
if (!roof.inPolygon({ x: oppositeMidX.toNumber(), y: oppositeMidX.toNumber() })) {
|
|
const checkEdge = {
|
|
vertex1: { x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() },
|
|
vertex2: { x: currentMidX.toNumber(), y: currentMidY.toNumber() },
|
|
}
|
|
const intersectionPoints = []
|
|
|
|
roof.lines
|
|
.filter((line) => (currentVectorX === 0 ? line.x1 === line.x2 : line.y1 === line.y2))
|
|
.forEach((line) => {
|
|
const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
|
|
const intersection = edgesIntersection(checkEdge, lineEdge)
|
|
if (
|
|
intersection &&
|
|
((line.x1 <= intersection.x && line.x2 >= intersection.x && line.y1 <= intersection.y && line.y2 >= intersection.y) ||
|
|
(line.x2 <= intersection.x && line.x1 >= intersection.x && line.y2 <= intersection.y && line.y1 >= intersection.y))
|
|
) {
|
|
const size = Big(intersection.x)
|
|
.minus(oppositeMidX)
|
|
.abs()
|
|
.pow(2)
|
|
.plus(Big(intersection.y).minus(oppositeMidY).abs().pow(2))
|
|
.sqrt()
|
|
.toNumber()
|
|
intersectionPoints.push({ intersection, size })
|
|
}
|
|
})
|
|
|
|
if (intersectionPoints.length > 0) {
|
|
const intersection = intersectionPoints.sort((a, b) => a.size - b.size)[0].intersection
|
|
oppositeMidX = Big(intersection.x)
|
|
oppositeMidY = Big(intersection.y)
|
|
}
|
|
}
|
|
|
|
const ridge = drawRidgeLine(
|
|
[currentMidX.toNumber(), currentMidY.toNumber(), oppositeMidX.toNumber(), oppositeMidY.toNumber()],
|
|
canvas,
|
|
roof,
|
|
textMode,
|
|
)
|
|
baseGableRidgeLines.push(ridge)
|
|
baseRidgeCount++
|
|
}
|
|
} else {
|
|
const vectorMidX = Math.sign(Big(nextLine.x2).minus(nextLine.x1))
|
|
const vectorMidY = Math.sign(Big(nextLine.y2).minus(nextLine.y1))
|
|
|
|
let prevOppositeMidX, prevOppositeMidY, nextOppositeMidX, nextOppositeMidY
|
|
const beforePrevOffset =
|
|
currentAngle === beforePrevAngle
|
|
? Big(beforePrevLine.attributes.offset)
|
|
: Big(beforePrevLine.attributes.offset).plus(currentLine.attributes.offset)
|
|
const afterNextOffset =
|
|
currentAngle === afterNextAngle
|
|
? Big(afterNextLine.attributes.offset)
|
|
: Big(afterNextLine.attributes.offset).plus(currentLine.attributes.offset)
|
|
const prevSize = Big(prevLine.attributes.planeSize).div(10)
|
|
const nextSize = Big(nextLine.attributes.planeSize).div(10)
|
|
|
|
let prevHipCoords, nextHipCoords
|
|
|
|
/** 다음 라인이 그 다음 라인과의 사이에 추녀마루가 존재 하는지 확인. 처마-처마 인 경우 추녀마루*/
|
|
if (eavesType.includes(afterNextLine.attributes?.type)) {
|
|
/** 현재 라인의 길이를 기준으로 추녀 마루의 길이를 삼각함수를 사용하여 판단한다.*/
|
|
let hipLength = Big(size).div(10).div(2).pow(2).plus(Big(size).div(10).div(2).pow(2)).sqrt()
|
|
|
|
const nextHalfVector = getHalfAngleVector(nextLine, afterNextLine)
|
|
let nextHipVector = { x: nextHalfVector.x, y: nextHalfVector.y }
|
|
|
|
/** 이전 라인과의 사이 추녀마루의 각도를 확인한다, 각도가 지붕안쪽으로 향하지 않을때 반대로 처리한다.*/
|
|
const nextCheckPoint = {
|
|
x: Big(nextLine.x2).plus(Big(nextHalfVector.x).times(10)),
|
|
y: Big(nextLine.y2).plus(Big(nextHalfVector.y).times(10)),
|
|
}
|
|
if (!checkWallPolygon.inPolygon(nextCheckPoint)) {
|
|
nextHipVector = { x: Big(nextHipVector.x).neg().toNumber(), y: Big(nextHipVector.y).neg().toNumber() }
|
|
}
|
|
|
|
const nextEndPoint = {
|
|
x: Big(nextLine.x2).plus(Big(nextHipVector.x).times(hipLength)),
|
|
y: Big(nextLine.y2).plus(Big(nextHipVector.y).times(hipLength)),
|
|
}
|
|
|
|
let ridgeEdge = {
|
|
vertex1: { x: currentMidX.toNumber(), y: currentMidY.toNumber() },
|
|
vertex2: {
|
|
x: currentMidX.plus(Big(nextVectorX).times(nextBaseLine.size)).toNumber(),
|
|
y: currentMidY.plus(Big(nextVectorY).times(nextBaseLine.size)).toNumber(),
|
|
},
|
|
}
|
|
let hipEdge = {
|
|
vertex1: { x: nextLine.x2, y: nextLine.y2 },
|
|
vertex2: { x: nextEndPoint.x, y: nextEndPoint.y },
|
|
}
|
|
let intersection = edgesIntersection(ridgeEdge, hipEdge)
|
|
if (intersection) {
|
|
nextHipCoords = { x1: nextLine.x2, y1: nextLine.y2, x2: intersection.x, y2: intersection.y }
|
|
nextOppositeMidY = Big(intersection.y)
|
|
nextOppositeMidX = Big(intersection.x)
|
|
}
|
|
} else {
|
|
if (vectorMidX === 0) {
|
|
nextOppositeMidY = currentMidY.plus(nextSize.plus(afterNextOffset).times(vectorMidY))
|
|
nextOppositeMidX = currentMidX
|
|
} else {
|
|
nextOppositeMidX = currentMidX.plus(nextSize.plus(afterNextOffset).times(vectorMidX))
|
|
nextOppositeMidY = currentMidY
|
|
}
|
|
}
|
|
|
|
/** 이전 라인이 그 이전 라인과의 사이에 추녀마루가 존재 하는지 확인. 처마-처마 인 경우 추녀마루*/
|
|
if (eavesType.includes(beforePrevLine.attributes?.type)) {
|
|
/** 현재 라인의 길이를 기준으로 추녀 마루의 길이를 삼각함수를 사용하여 판단한다.*/
|
|
let hipLength = Big(size).div(10).div(2).pow(2).plus(Big(size).div(10).div(2).pow(2)).sqrt()
|
|
|
|
const prevHalfVector = getHalfAngleVector(prevLine, beforePrevLine)
|
|
let prevHipVector = { x: prevHalfVector.x, y: prevHalfVector.y }
|
|
|
|
/** 이전 라인과의 사이 추녀마루의 각도를 확인한다, 각도가 지붕안쪽으로 향하지 않을때 반대로 처리한다.*/
|
|
const prevCheckPoint = {
|
|
x: Big(prevLine.x1).plus(Big(prevHalfVector.x).times(10)),
|
|
y: Big(prevLine.y1).plus(Big(prevHalfVector.y).times(10)),
|
|
}
|
|
|
|
if (!checkWallPolygon.inPolygon(prevCheckPoint)) {
|
|
prevHipVector = { x: Big(prevHipVector.x).neg().toNumber(), y: Big(prevHipVector.y).neg().toNumber() }
|
|
}
|
|
|
|
const prevEndPoint = {
|
|
x: Big(prevLine.x1).plus(Big(prevHipVector.x).times(hipLength)),
|
|
y: Big(prevLine.y1).plus(Big(prevHipVector.y).times(hipLength)),
|
|
}
|
|
|
|
let ridgeEdge = {
|
|
vertex1: { x: currentMidX.toNumber(), y: currentMidY.toNumber() },
|
|
vertex2: {
|
|
x: currentMidX.plus(Big(prevVectorX).times(prevBaseLine.size)).toNumber(),
|
|
y: currentMidY.plus(Big(prevVectorY).times(prevBaseLine.size)).toNumber(),
|
|
},
|
|
}
|
|
let hipEdge = {
|
|
vertex1: { x: prevLine.x1, y: prevLine.y1 },
|
|
vertex2: { x: prevEndPoint.x, y: prevEndPoint.y },
|
|
}
|
|
let intersection = edgesIntersection(ridgeEdge, hipEdge)
|
|
if (intersection) {
|
|
prevHipCoords = { x1: prevLine.x1, y1: prevLine.y1, x2: intersection.x, y2: intersection.y }
|
|
prevOppositeMidY = Big(intersection.y)
|
|
prevOppositeMidX = Big(intersection.x)
|
|
}
|
|
} else {
|
|
if (vectorMidX === 0) {
|
|
prevOppositeMidY = currentMidY.plus(prevSize.plus(beforePrevOffset).times(vectorMidY))
|
|
prevOppositeMidX = currentMidX
|
|
} else {
|
|
prevOppositeMidX = currentMidX.plus(prevSize.plus(beforePrevOffset).times(vectorMidX))
|
|
prevOppositeMidY = currentMidY
|
|
}
|
|
}
|
|
|
|
const currentMidEdge = {
|
|
vertex1: { x: currentMidX.toNumber(), y: currentMidY.toNumber() },
|
|
vertex2: {
|
|
x: currentVectorX === 0 ? nextLine.x2 : currentMidX.toNumber(),
|
|
y: currentVectorX === 0 ? currentMidY.toNumber() : nextLine.y2,
|
|
},
|
|
}
|
|
|
|
let oppositeLines = []
|
|
drawBaseLines
|
|
.filter((line, index) => {
|
|
const currentLine = line.line
|
|
const nextLine = drawBaseLines[(index + 1) % drawBaseLines.length].line
|
|
const prevLine = drawBaseLines[(index - 1 + drawBaseLines.length) % drawBaseLines.length].line
|
|
const angle = calculateAngle(currentLine.startPoint, currentLine.endPoint)
|
|
const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint)
|
|
const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint)
|
|
if (angle === prevAngle || angle === nextAngle) {
|
|
const sameAngleLine = angle === prevAngle ? prevLine : nextLine
|
|
if (gableType.includes(currentLine.attributes.type) && !gableType.includes(sameAngleLine.attributes.type)) {
|
|
switch (currentAngle) {
|
|
case 90:
|
|
return angle === -90
|
|
case -90:
|
|
return angle === 90
|
|
case 0:
|
|
return angle === 180
|
|
case 180:
|
|
return angle === 0
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
})
|
|
.forEach((line) => {
|
|
const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
|
|
const intersection = edgesIntersection(currentMidEdge, lineEdge)
|
|
if (intersection) {
|
|
if (line.x1 <= intersection.x && line.x2 >= intersection.x && line.y1 <= intersection.y && line.y2 >= intersection.y) {
|
|
oppositeLines.push({
|
|
line,
|
|
intersection,
|
|
size: Big(intersection.x)
|
|
.minus(currentMidX)
|
|
.abs()
|
|
.pow(2)
|
|
.plus(Big(intersection.y).minus(currentMidY).abs().pow(2))
|
|
.sqrt()
|
|
.toNumber(),
|
|
})
|
|
}
|
|
}
|
|
})
|
|
|
|
if (oppositeLines.length > 0) {
|
|
const oppositePoint = oppositeLines.sort((a, b) => a.size - b.size)[0].intersection
|
|
const checkEdge = {
|
|
vertex1: { x: currentMidX.toNumber(), y: currentMidY.toNumber() },
|
|
vertex2: { x: oppositePoint.x, y: oppositePoint.y },
|
|
}
|
|
const oppositeRoofPoints = []
|
|
roof.lines
|
|
.filter((line) => {
|
|
const angle = calculateAngle(line.startPoint, line.endPoint)
|
|
switch (currentAngle) {
|
|
case 90:
|
|
return angle === -90
|
|
case -90:
|
|
return angle === 90
|
|
case 0:
|
|
return angle === 180
|
|
case 180:
|
|
return angle === 0
|
|
}
|
|
})
|
|
.forEach((line) => {
|
|
const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
|
|
const intersection = edgesIntersection(checkEdge, lineEdge)
|
|
if (
|
|
intersection &&
|
|
((line.x1 <= intersection.x && line.x2 >= intersection.x && line.y1 <= intersection.y && line.y2 >= intersection.y) ||
|
|
(line.x1 >= intersection.x && line.x2 <= intersection.x && line.y1 >= intersection.y && line.y2 <= intersection.y))
|
|
) {
|
|
oppositeRoofPoints.push({
|
|
line,
|
|
intersection,
|
|
size: Big(intersection.x)
|
|
.minus(currentMidX.toNumber())
|
|
.abs()
|
|
.pow(2)
|
|
.plus(Big(intersection.y).minus(currentMidY.toNumber()).abs().pow(2))
|
|
.sqrt()
|
|
.toNumber(),
|
|
})
|
|
}
|
|
})
|
|
const oppositeRoofPoint = oppositeRoofPoints.sort((a, b) => a.size - b.size)[0].intersection
|
|
oppositeMidX = Big(oppositeRoofPoint.x)
|
|
oppositeMidY = Big(oppositeRoofPoint.y)
|
|
|
|
const currentRoofPoints = []
|
|
roof.lines
|
|
.filter((line) => {
|
|
const angle = calculateAngle(line.startPoint, line.endPoint)
|
|
return currentAngle === angle
|
|
})
|
|
.forEach((line) => {
|
|
const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
|
|
const intersection = edgesIntersection(checkEdge, lineEdge)
|
|
if (
|
|
intersection &&
|
|
((line.x1 <= intersection.x && line.x2 >= intersection.x && line.y1 <= intersection.y && line.y2 >= intersection.y) ||
|
|
(line.x1 >= intersection.x && line.x2 <= intersection.x && line.y1 >= intersection.y && line.y2 <= intersection.y))
|
|
) {
|
|
currentRoofPoints.push({
|
|
line,
|
|
intersection,
|
|
size: Big(intersection.x)
|
|
.minus(currentMidX.toNumber())
|
|
.abs()
|
|
.pow(2)
|
|
.plus(Big(intersection.y).minus(currentMidY.toNumber()).abs().pow(2))
|
|
.sqrt()
|
|
.toNumber(),
|
|
})
|
|
}
|
|
})
|
|
const currentRoofPoint = currentRoofPoints.sort((a, b) => a.size - b.size)[0].intersection
|
|
currentMidX = Big(currentRoofPoint.x)
|
|
currentMidY = Big(currentRoofPoint.y)
|
|
} else {
|
|
const checkPrevSize = currentMidX.minus(prevOppositeMidX).pow(2).plus(currentMidY.minus(prevOppositeMidY).pow(2)).sqrt()
|
|
const checkNextSize = currentMidX.minus(nextOppositeMidX).pow(2).plus(currentMidY.minus(nextOppositeMidY).pow(2)).sqrt()
|
|
|
|
/** 두 포인트 중에 current와 가까운 포인트를 사용*/
|
|
if (checkPrevSize.gt(checkNextSize)) {
|
|
if (nextHipCoords) {
|
|
let intersectPoints = []
|
|
const hipEdge = {
|
|
vertex1: { x: nextHipCoords.x2, y: nextHipCoords.y2 },
|
|
vertex2: { x: nextHipCoords.x1, y: nextHipCoords.y1 },
|
|
}
|
|
|
|
/** 외벽선에서 라인 겹치는 경우에 대한 확인*/
|
|
roof.lines.forEach((line) => {
|
|
const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
|
|
const intersection = edgesIntersection(hipEdge, lineEdge)
|
|
if (
|
|
intersection &&
|
|
Math.sign(nextHipCoords.x2 - nextHipCoords.x1) === Math.sign(nextHipCoords.x2 - intersection.x) &&
|
|
Math.sign(nextHipCoords.y2 - nextHipCoords.y1) === Math.sign(nextHipCoords.y2 - intersection.y)
|
|
) {
|
|
const intersectEdge = {
|
|
vertex1: { x: nextHipCoords.x2, y: nextHipCoords.y2 },
|
|
vertex2: { x: intersection.x, y: intersection.y },
|
|
}
|
|
const is = edgesIntersection(intersectEdge, lineEdge)
|
|
if (!is.isIntersectionOutside) {
|
|
const intersectSize = Big(nextHipCoords.x2)
|
|
.minus(Big(intersection.x))
|
|
.pow(2)
|
|
.plus(Big(nextHipCoords.y2).minus(Big(intersection.y)).pow(2))
|
|
.abs()
|
|
.sqrt()
|
|
.toNumber()
|
|
|
|
intersectPoints.push({
|
|
intersection,
|
|
size: intersectSize,
|
|
line,
|
|
})
|
|
}
|
|
}
|
|
})
|
|
const intersect = intersectPoints.sort((a, b) => a.size - b.size)[0]
|
|
if (intersect) {
|
|
const degree =
|
|
intersect.line.attributes.pitch > 0 ? getDegreeByChon(intersect.line.attributes.pitch) : intersect.line.attributes.degree
|
|
const hipLine = drawHipLine(
|
|
[intersect.intersection.x, intersect.intersection.y, nextOppositeMidX.toNumber(), nextOppositeMidY.toNumber()],
|
|
canvas,
|
|
roof,
|
|
textMode,
|
|
null,
|
|
degree,
|
|
degree,
|
|
)
|
|
baseHipLines.push({
|
|
x1: nextHipCoords.x1,
|
|
y1: nextHipCoords.y1,
|
|
x2: nextHipCoords.x2,
|
|
y2: nextHipCoords.y2,
|
|
line: hipLine,
|
|
})
|
|
}
|
|
}
|
|
oppositeMidY = nextOppositeMidY
|
|
oppositeMidX = nextOppositeMidX
|
|
} else {
|
|
if (prevHipCoords) {
|
|
let intersectPoints = []
|
|
const hipEdge = {
|
|
vertex1: { x: prevHipCoords.x2, y: prevHipCoords.y2 },
|
|
vertex2: { x: prevHipCoords.x1, y: prevHipCoords.y1 },
|
|
}
|
|
|
|
/** 외벽선에서 라인 겹치는 경우에 대한 확인*/
|
|
roof.lines.forEach((line) => {
|
|
const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
|
|
const intersection = edgesIntersection(hipEdge, lineEdge)
|
|
if (
|
|
intersection &&
|
|
Math.sign(prevHipCoords.x2 - prevHipCoords.x1) === Math.sign(prevHipCoords.x2 - intersection.x) &&
|
|
Math.sign(prevHipCoords.y2 - prevHipCoords.y1) === Math.sign(prevHipCoords.y2 - intersection.y)
|
|
) {
|
|
const intersectEdge = {
|
|
vertex1: { x: prevHipCoords.x2, y: prevHipCoords.y2 },
|
|
vertex2: { x: intersection.x, y: intersection.y },
|
|
}
|
|
const is = edgesIntersection(intersectEdge, lineEdge)
|
|
if (!is.isIntersectionOutside) {
|
|
const intersectSize = Big(prevHipCoords.x2)
|
|
.minus(Big(intersection.x))
|
|
.pow(2)
|
|
.plus(Big(prevHipCoords.y2).minus(Big(intersection.y)).pow(2))
|
|
.abs()
|
|
.sqrt()
|
|
.toNumber()
|
|
|
|
intersectPoints.push({
|
|
intersection,
|
|
size: intersectSize,
|
|
line,
|
|
})
|
|
}
|
|
}
|
|
})
|
|
const intersect = intersectPoints.sort((a, b) => a.size - b.size)[0]
|
|
|
|
if (intersect) {
|
|
const degree =
|
|
intersect.line.attributes.pitch > 0 ? getDegreeByChon(intersect.line.attributes.pitch) : intersect.line.attributes.degree
|
|
const hipLine = drawHipLine(
|
|
[intersect.intersection.x, intersect.intersection.y, prevOppositeMidX.toNumber(), prevOppositeMidY.toNumber()],
|
|
canvas,
|
|
roof,
|
|
textMode,
|
|
null,
|
|
degree,
|
|
degree,
|
|
)
|
|
baseHipLines.push({
|
|
x1: prevHipCoords.x1,
|
|
y1: prevHipCoords.y1,
|
|
x2: prevHipCoords.x2,
|
|
y2: prevHipCoords.y2,
|
|
line: hipLine,
|
|
})
|
|
}
|
|
}
|
|
oppositeMidY = prevOppositeMidY
|
|
oppositeMidX = prevOppositeMidX
|
|
}
|
|
}
|
|
}
|
|
if (baseRidgeCount < getMaxRidge(baseLines.length)) {
|
|
/** 포인트가 지붕 밖에 있는 경우 조정 */
|
|
if (!roof.inPolygon({ x: currentMidX.toNumber(), y: currentMidY.toNumber() })) {
|
|
const checkEdge = {
|
|
vertex1: { x: currentMidX.toNumber(), y: currentMidY.toNumber() },
|
|
vertex2: { x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() },
|
|
}
|
|
const intersectionPoints = []
|
|
|
|
roof.lines
|
|
.filter((line) => (currentVectorX === 0 ? line.x1 === line.x2 : line.y1 === line.y2))
|
|
.forEach((line) => {
|
|
const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
|
|
const intersection = edgesIntersection(checkEdge, lineEdge)
|
|
if (
|
|
intersection &&
|
|
((line.x1 <= intersection.x && line.x2 >= intersection.x && line.y1 <= intersection.y && line.y2 >= intersection.y) ||
|
|
(line.x2 <= intersection.x && line.x1 >= intersection.x && line.y2 <= intersection.y && line.y1 >= intersection.y))
|
|
) {
|
|
const size = Big(intersection.x)
|
|
.minus(currentMidX)
|
|
.abs()
|
|
.pow(2)
|
|
.plus(Big(intersection.y).minus(currentMidY).abs().pow(2))
|
|
.sqrt()
|
|
.toNumber()
|
|
intersectionPoints.push({ intersection, size })
|
|
}
|
|
})
|
|
|
|
if (intersectionPoints.length > 0) {
|
|
const intersection = intersectionPoints.sort((a, b) => a.size - b.size)[0].intersection
|
|
currentMidX = Big(intersection.x)
|
|
currentMidY = Big(intersection.y)
|
|
}
|
|
}
|
|
|
|
if (!roof.inPolygon({ x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() })) {
|
|
const checkEdge = {
|
|
vertex1: { x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() },
|
|
vertex2: { x: currentMidX.toNumber(), y: currentMidY.toNumber() },
|
|
}
|
|
const intersectionPoints = []
|
|
|
|
roof.lines
|
|
.filter((line) => (currentVectorX === 0 ? line.x1 === line.x2 : line.y1 === line.y2))
|
|
.forEach((line) => {
|
|
const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
|
|
const intersection = edgesIntersection(checkEdge, lineEdge)
|
|
if (
|
|
intersection &&
|
|
((line.x1 <= intersection.x && line.x2 >= intersection.x && line.y1 <= intersection.y && line.y2 >= intersection.y) ||
|
|
(line.x2 <= intersection.x && line.x1 >= intersection.x && line.y2 <= intersection.y && line.y1 >= intersection.y))
|
|
) {
|
|
const size = Big(intersection.x)
|
|
.minus(oppositeMidX)
|
|
.abs()
|
|
.pow(2)
|
|
.plus(Big(intersection.y).minus(oppositeMidY).abs().pow(2))
|
|
.sqrt()
|
|
.toNumber()
|
|
intersectionPoints.push({ intersection, size })
|
|
}
|
|
})
|
|
|
|
if (intersectionPoints.length > 0) {
|
|
const intersection = intersectionPoints.sort((a, b) => a.size - b.size)[0].intersection
|
|
oppositeMidX = Big(intersection.x)
|
|
oppositeMidY = Big(intersection.y)
|
|
}
|
|
}
|
|
|
|
/** 마루가 맞은편 외벽선에 닿는 경우 해당 부분까지로 한정한다. */
|
|
const ridgeEdge = {
|
|
vertex1: { x: currentMidX.toNumber(), y: currentMidY.toNumber() },
|
|
vertex2: { x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() },
|
|
}
|
|
const ridgeVectorX = Math.sign(currentMidX.minus(oppositeMidX).toNumber())
|
|
const ridgeVectorY = Math.sign(currentMidY.minus(oppositeMidY).toNumber())
|
|
|
|
roof.lines
|
|
.filter((line) => {
|
|
const lineVectorX = Math.sign(Big(line.x2).minus(Big(line.x1)).toNumber())
|
|
const lineVectorY = Math.sign(Big(line.y2).minus(Big(line.y1)).toNumber())
|
|
return (
|
|
(lineVectorX === currentVectorX && lineVectorY !== currentVectorY) || (lineVectorX !== currentVectorX && lineVectorY === currentVectorY)
|
|
)
|
|
})
|
|
.forEach((line) => {
|
|
const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
|
|
const intersection = edgesIntersection(ridgeEdge, lineEdge)
|
|
if (intersection && !intersection.isIntersectionOutside) {
|
|
const isVectorX = Math.sign(Big(currentMidX).minus(intersection.x).toNumber())
|
|
const isVectorY = Math.sign(Big(currentMidY).minus(intersection.y).toNumber())
|
|
if (isVectorX === ridgeVectorX && isVectorY === ridgeVectorY) {
|
|
oppositeMidX = Big(intersection.x)
|
|
oppositeMidY = Big(intersection.y)
|
|
}
|
|
}
|
|
})
|
|
const ridgeLine = drawRidgeLine(
|
|
[currentMidX.toNumber(), currentMidY.toNumber(), oppositeMidX.toNumber(), oppositeMidY.toNumber()],
|
|
canvas,
|
|
roof,
|
|
textMode,
|
|
)
|
|
baseGableRidgeLines.push(ridgeLine)
|
|
baseRidgeCount++
|
|
}
|
|
}
|
|
})
|
|
|
|
/** 박공지붕에서 파생되는 마루를 그린다. 첫번째에서 처리 하지 못한 라인이 있는 경우 */
|
|
drawGableRidgeSecond.forEach((current) => {
|
|
const { currentBaseLine, prevBaseLine, nextBaseLine } = current
|
|
const currentLine = currentBaseLine.line
|
|
const prevLine = prevBaseLine.line
|
|
const nextLine = nextBaseLine.line
|
|
|
|
/** 이전 라인의 경사 */
|
|
const prevDegree = prevLine.attributes.pitch > 0 ? getDegreeByChon(prevLine.attributes.pitch) : prevLine.attributes.degree
|
|
/** 다음 라인의 경사 */
|
|
const nextDegree = nextLine.attributes.pitch > 0 ? getDegreeByChon(nextLine.attributes.pitch) : nextLine.attributes.degree
|
|
/** 현재 라인의 경사 */
|
|
const currentDegree = currentLine.attributes.pitch > 0 ? getDegreeByChon(currentLine.attributes.pitch) : currentLine.attributes.degree
|
|
|
|
const currentAngle = calculateAngle(currentLine.startPoint, currentLine.endPoint)
|
|
const currentVectorX = Big(currentLine.x2).minus(currentLine.x1)
|
|
const currentVectorY = Big(currentLine.y2).minus(currentLine.y1)
|
|
const checkVectorX = Big(nextLine.x2).minus(Big(nextLine.x1))
|
|
const checkVectorY = Big(nextLine.y2).minus(Big(nextLine.y1))
|
|
const currentMidX = Big(currentLine.x1).plus(Big(currentLine.x2)).div(2)
|
|
const currentMidY = Big(currentLine.y1).plus(Big(currentLine.y2)).div(2)
|
|
const checkSize = Big(10)
|
|
|
|
const checkPoints = {
|
|
x: currentMidX.plus(checkSize.times(Math.sign(checkVectorX.toNumber()))).toNumber(),
|
|
y: currentMidY.plus(checkSize.times(Math.sign(checkVectorY.toNumber()))).toNumber(),
|
|
}
|
|
if (!checkWallPolygon.inPolygon(checkPoints)) {
|
|
const currentMidEdge = {
|
|
vertex1: { x: currentMidX.toNumber(), y: currentMidY.toNumber() },
|
|
vertex2: {
|
|
x: currentMidX.plus(checkSize.times(Math.sign(checkVectorX.neg().toNumber()))).toNumber(),
|
|
y: currentMidY.plus(checkSize.times(Math.sign(checkVectorY.neg().toNumber()))).toNumber(),
|
|
},
|
|
}
|
|
|
|
let oppositeLines = []
|
|
baseLines
|
|
.filter((line, index) => {
|
|
let nextLine = baseLines[(index + 1) % baseLines.length]
|
|
let prevLine = baseLines[(index - 1 + baseLines.length) % baseLines.length]
|
|
if (
|
|
(gableType.includes(nextLine.attributes.type) && gableType.includes(prevLine.attributes.type)) ||
|
|
(eavesType.includes(nextLine.attributes.type) && eavesType.includes(prevLine.attributes.type))
|
|
) {
|
|
const angle = calculateAngle(line.startPoint, line.endPoint)
|
|
switch (currentAngle) {
|
|
case 90:
|
|
return angle === -90
|
|
case -90:
|
|
return angle === 90
|
|
case 0:
|
|
return angle === 180
|
|
case 180:
|
|
return angle === 0
|
|
}
|
|
}
|
|
return false
|
|
})
|
|
.forEach((line) => {
|
|
const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
|
|
const intersection = edgesIntersection(currentMidEdge, lineEdge)
|
|
if (intersection) {
|
|
oppositeLines.push({
|
|
line,
|
|
intersection,
|
|
size: Big(intersection.x).minus(currentMidX).abs().pow(2).plus(Big(intersection.y).minus(currentMidY).abs().pow(2)).sqrt(),
|
|
})
|
|
}
|
|
})
|
|
|
|
if (oppositeLines.length === 0) {
|
|
return
|
|
}
|
|
const oppositeLine = oppositeLines.sort((a, b) => a.size - b.size)[0]
|
|
|
|
let points = []
|
|
if (eavesType.includes(oppositeLine.line.attributes.type)) {
|
|
const oppositeCurrentLine = oppositeLine.line
|
|
let oppositePrevLine, oppositeNextLine
|
|
baseLines.forEach((line, index) => {
|
|
if (line === oppositeCurrentLine) {
|
|
oppositePrevLine = baseLines[(index - 1 + baseLines.length) % baseLines.length]
|
|
oppositeNextLine = baseLines[(index + 1) % baseLines.length]
|
|
}
|
|
})
|
|
if (gableType.includes(oppositeNextLine.attributes.type) && gableType.includes(oppositePrevLine.attributes.type)) {
|
|
if (currentVectorX.eq(0)) {
|
|
const centerX = currentMidX.plus(oppositeLine.intersection.x).div(2).toNumber()
|
|
points = [centerX, currentLine.y1, centerX, currentLine.y2]
|
|
} else {
|
|
const centerY = currentMidY.plus(oppositeLine.intersection.y).div(2).toNumber()
|
|
points = [currentLine.x1, centerY, currentLine.x2, centerY]
|
|
}
|
|
}
|
|
if (eavesType.includes(oppositeNextLine.attributes.type) && eavesType.includes(oppositePrevLine.attributes.type)) {
|
|
/** 이전, 다음라인의 사잇각의 vector를 구한다. */
|
|
let prevVector = getHalfAngleVector(oppositePrevLine, oppositeCurrentLine)
|
|
let nextVector = getHalfAngleVector(oppositeCurrentLine, oppositeNextLine)
|
|
|
|
let prevHipVector = { x: Big(prevVector.x), y: Big(prevVector.y) }
|
|
let nextHipVector = { x: Big(nextVector.x), y: Big(nextVector.y) }
|
|
|
|
/** 이전 라인과의 사이 추녀마루의 각도를 확인한다, 각도가 지붕안쪽으로 향하지 않을때 반대로 처리한다.*/
|
|
const prevCheckPoint = {
|
|
x: Big(oppositeCurrentLine.x1).plus(Big(prevHipVector.x).times(10)),
|
|
y: Big(oppositeCurrentLine.y1).plus(Big(prevHipVector.y).times(10)),
|
|
}
|
|
if (!checkWallPolygon.inPolygon(prevCheckPoint)) {
|
|
prevHipVector = { x: Big(prevHipVector.x).neg(), y: Big(prevHipVector.y).neg() }
|
|
}
|
|
|
|
/** 다음 라인과의 사이 추녀마루의 각도를 확인한다, 각도가 지붕안쪽으로 향하지 않을때 반대로 처리한다.*/
|
|
const nextCheckPoint = {
|
|
x: Big(oppositeCurrentLine.x2).plus(Big(nextHipVector.x).times(10)),
|
|
y: Big(oppositeCurrentLine.y2).plus(Big(nextHipVector.y).times(10)),
|
|
}
|
|
if (!checkWallPolygon.inPolygon(nextCheckPoint)) {
|
|
nextHipVector = { x: Big(nextHipVector.x).neg(), y: Big(nextHipVector.y).neg() }
|
|
}
|
|
|
|
/** 현재 라인의 길이를 기준으로 추녀 마루의 길이를 삼각함수를 사용하여 판단한다.*/
|
|
let hipLength = Big(oppositeCurrentLine.attributes.planeSize)
|
|
.div(2)
|
|
.pow(2)
|
|
.plus(Big(oppositeCurrentLine.attributes.planeSize).div(2).pow(2))
|
|
.sqrt()
|
|
.div(10)
|
|
.round(2)
|
|
|
|
const ridgeEndPoint = {
|
|
x: Big(oppositeCurrentLine.x1).plus(hipLength.times(prevHipVector.x)).round(1),
|
|
y: Big(oppositeCurrentLine.y1).plus(hipLength.times(prevHipVector.y)).round(1),
|
|
}
|
|
|
|
const prevHypotenuse = Big(oppositePrevLine.attributes.offset).pow(2).plus(Big(oppositeCurrentLine.attributes.offset).pow(2)).sqrt()
|
|
const prevHipPoints = {
|
|
x1: Big(oppositeCurrentLine.x1).plus(prevHypotenuse.times(prevHipVector.x.neg())).round(1).toNumber(),
|
|
y1: Big(oppositeCurrentLine.y1).plus(prevHypotenuse.times(prevHipVector.y.neg())).round(1).toNumber(),
|
|
x2: ridgeEndPoint.x.toNumber(),
|
|
y2: ridgeEndPoint.y.toNumber(),
|
|
}
|
|
|
|
const nextHypotenuse = Big(oppositeNextLine.attributes.offset).pow(2).plus(Big(oppositeCurrentLine.attributes.offset).pow(2)).sqrt()
|
|
const nextHipPoints = {
|
|
x1: Big(oppositeCurrentLine.x2).plus(nextHypotenuse.times(nextHipVector.x.neg())).round(1).toNumber(),
|
|
y1: Big(oppositeCurrentLine.y2).plus(nextHypotenuse.times(nextHipVector.y.neg())).round(1).toNumber(),
|
|
x2: ridgeEndPoint.x.toNumber(),
|
|
y2: ridgeEndPoint.y.toNumber(),
|
|
}
|
|
|
|
const prevIntersection = findRoofIntersection(roof, prevHipPoints, ridgeEndPoint)
|
|
const nextIntersection = findRoofIntersection(roof, nextHipPoints, ridgeEndPoint)
|
|
|
|
if (prevIntersection) {
|
|
const prevHip = drawHipLine(
|
|
[prevIntersection.intersection.x, prevIntersection.intersection.y, ridgeEndPoint.x.toNumber(), ridgeEndPoint.y.toNumber()],
|
|
canvas,
|
|
roof,
|
|
textMode,
|
|
null,
|
|
prevDegree,
|
|
prevDegree,
|
|
)
|
|
baseHipLines.push({
|
|
x1: oppositeCurrentLine.x1,
|
|
y1: oppositeCurrentLine.y1,
|
|
x2: ridgeEndPoint.x,
|
|
y2: ridgeEndPoint.y,
|
|
line: prevHip,
|
|
})
|
|
}
|
|
if (nextIntersection) {
|
|
const nextHip = drawHipLine(
|
|
[nextIntersection.intersection.x, nextIntersection.intersection.y, ridgeEndPoint.x.toNumber(), ridgeEndPoint.y.toNumber()],
|
|
canvas,
|
|
roof,
|
|
textMode,
|
|
null,
|
|
nextDegree,
|
|
nextDegree,
|
|
)
|
|
baseHipLines.push({
|
|
x1: ridgeEndPoint.x,
|
|
y1: ridgeEndPoint.y,
|
|
x2: oppositeCurrentLine.x2,
|
|
y2: oppositeCurrentLine.y2,
|
|
line: nextHip,
|
|
})
|
|
}
|
|
|
|
const ridgeVectorX = Math.sign(currentMidX.minus(ridgeEndPoint.x).toNumber())
|
|
const ridgeVectorY = Math.sign(currentMidY.minus(ridgeEndPoint.y).toNumber())
|
|
const ridgePoints = {
|
|
x1: currentMidX.plus(Big(currentLine.attributes.offset).times(ridgeVectorX)).toNumber(),
|
|
y1: currentMidY.plus(Big(currentLine.attributes.offset).times(ridgeVectorY)).toNumber(),
|
|
x2: ridgeEndPoint.x.toNumber(),
|
|
y2: ridgeEndPoint.y.toNumber(),
|
|
}
|
|
const ridgeIntersection = findRoofIntersection(roof, ridgePoints, {
|
|
x: Big(ridgePoints.x2),
|
|
y: Big(ridgePoints.y2),
|
|
})
|
|
if (ridgeIntersection) {
|
|
points = [ridgeIntersection.intersection.x, ridgeIntersection.intersection.y, ridgeEndPoint.x, ridgeEndPoint.y]
|
|
}
|
|
}
|
|
} else {
|
|
if (currentVectorX.eq(0)) {
|
|
points = [oppositeLine.intersection.x, currentLine.y1, oppositeLine.intersection.x, currentLine.y2]
|
|
} else {
|
|
points = [currentLine.x1, oppositeLine.intersection.y, currentLine.x2, oppositeLine.intersection.y]
|
|
}
|
|
}
|
|
|
|
if (baseRidgeCount < getMaxRidge(baseLines.length)) {
|
|
const ridgeLine = drawRidgeLine(points, canvas, roof, textMode)
|
|
baseGableRidgeLines.push(ridgeLine)
|
|
baseRidgeCount++
|
|
}
|
|
} else {
|
|
const oppositeLines = baseLines.filter((line) => {
|
|
const lineAngle = calculateAngle(line.startPoint, line.endPoint)
|
|
switch (currentAngle) {
|
|
case 90:
|
|
return lineAngle === -90
|
|
case -90:
|
|
return lineAngle === 90
|
|
case 0:
|
|
return lineAngle === 180
|
|
case 180:
|
|
return lineAngle === 0
|
|
}
|
|
})
|
|
|
|
if (oppositeLines.length > 0 && baseRidgeCount < getMaxRidge(baseLines.length)) {
|
|
let ridgePoints = []
|
|
const oppositeLine = oppositeLines.sort((a, b) => {
|
|
let diffCurrentA, diffCurrentB
|
|
if (Math.sign(currentVectorX) === 0) {
|
|
diffCurrentA = currentMidY.minus(a.y1).abs()
|
|
diffCurrentB = currentMidY.minus(b.y1).abs()
|
|
} else {
|
|
diffCurrentA = currentMidX.minus(a.x1).abs()
|
|
diffCurrentB = currentMidX.minus(b.x1).abs()
|
|
}
|
|
return diffCurrentA.minus(diffCurrentB).toNumber()
|
|
})[0]
|
|
|
|
const prevOffset = prevLine.attributes.offset
|
|
const nextOffset = nextLine.attributes.offset
|
|
if (Math.sign(currentVectorX) === 0) {
|
|
const prevY = Big(currentLine.y1)
|
|
.plus(Big(Math.sign(currentVectorY)).neg().times(prevOffset))
|
|
.toNumber()
|
|
const nextY = Big(currentLine.y2)
|
|
.plus(Big(Math.sign(currentVectorY)).times(nextOffset))
|
|
.toNumber()
|
|
const midX = Big(currentLine.x1).plus(oppositeLine.x1).div(2).toNumber()
|
|
ridgePoints = [midX, prevY, midX, nextY]
|
|
} else {
|
|
const prevX = Big(currentLine.x1)
|
|
.plus(Big(Math.sign(currentVectorX)).neg().times(prevOffset))
|
|
.toNumber()
|
|
const nextX = Big(currentLine.x2)
|
|
.plus(Big(Math.sign(currentVectorX)).times(nextOffset))
|
|
.toNumber()
|
|
const midY = Big(currentLine.y1).plus(oppositeLine.y1).div(2).toNumber()
|
|
ridgePoints = [prevX, midY, nextX, midY]
|
|
}
|
|
const ridge = drawRidgeLine(ridgePoints, canvas, roof, textMode)
|
|
baseGableRidgeLines.push(ridge)
|
|
baseRidgeCount++
|
|
}
|
|
}
|
|
})
|
|
|
|
const uniqueRidgeLines = []
|
|
/** 중복제거 */
|
|
baseGableRidgeLines.forEach((currentLine, index) => {
|
|
if (index === 0) {
|
|
uniqueRidgeLines.push(currentLine)
|
|
} else {
|
|
const duplicateLines = uniqueRidgeLines.filter(
|
|
(line) =>
|
|
(currentLine.x1 === line.x1 && currentLine.y1 === line.y1 && currentLine.x2 === line.x2 && currentLine.y2 === line.y2) ||
|
|
(currentLine.x1 === line.x2 && currentLine.y1 === line.y2 && currentLine.x2 === line.x1 && currentLine.y2 === line.y1),
|
|
)
|
|
if (duplicateLines.length === 0) {
|
|
uniqueRidgeLines.push(currentLine)
|
|
}
|
|
}
|
|
})
|
|
|
|
baseGableRidgeLines = uniqueRidgeLines
|
|
|
|
/** 박공지붕 polygon 생성 */
|
|
drawGablePolygonFirst.forEach((current) => {
|
|
const { currentBaseLine, prevBaseLine, nextBaseLine } = current
|
|
const currentLine = currentBaseLine.line
|
|
const prevLine = prevBaseLine.line
|
|
const nextLine = nextBaseLine.line
|
|
let { x1, x2, y1, y2 } = currentBaseLine
|
|
let prevLineRidges = [],
|
|
nextLineRidges = []
|
|
|
|
const currentVectorX = Math.sign(currentLine.x2 - currentLine.x1)
|
|
const currentVectorY = Math.sign(currentLine.y2 - currentLine.y1)
|
|
const nextVectorX = Math.sign(nextLine.x1 - nextLine.x2)
|
|
const nextVectorY = Math.sign(nextLine.y1 - nextLine.y2)
|
|
const currentDegree = currentLine.attributes.pitch > 0 ? getDegreeByChon(currentLine.attributes.pitch) : currentLine.attributes.degree
|
|
|
|
let roofX1, roofY1, roofX2, roofY2
|
|
const currentMidX = Big(currentLine.x1).plus(Big(currentLine.x2)).div(2)
|
|
const currentMidY = Big(currentLine.y1).plus(Big(currentLine.y2)).div(2)
|
|
const intersectionRoofs = []
|
|
if (currentVectorX === 0) {
|
|
const checkEdge = {
|
|
vertex1: { x: prevLine.x1, y: currentMidY.toNumber() },
|
|
vertex2: { x: currentMidX.toNumber(), y: currentMidY.toNumber() },
|
|
}
|
|
roof.lines
|
|
.filter((line) => Math.sign(line.x2 - line.x1) === currentVectorX && Math.sign(line.y2 - line.y1) === currentVectorY)
|
|
.forEach((line) => {
|
|
const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
|
|
const intersection = edgesIntersection(checkEdge, lineEdge)
|
|
if (intersection) {
|
|
intersection.x = Math.round(intersection.x)
|
|
intersection.y = Math.round(intersection.y)
|
|
if (
|
|
(line.x1 <= intersection.x && intersection.x <= line.x2 && line.y1 <= intersection.y && intersection.y <= line.y2) ||
|
|
(line.x2 <= intersection.x && intersection.x <= line.x1 && line.y2 <= intersection.y && intersection.y <= line.y1)
|
|
) {
|
|
intersectionRoofs.push({
|
|
line,
|
|
intersection,
|
|
size: Big(intersection.x).minus(currentMidX).abs().pow(2).plus(Big(intersection.y).minus(currentMidY).abs().pow(2)).sqrt(),
|
|
})
|
|
}
|
|
}
|
|
})
|
|
} else {
|
|
const checkEdge = {
|
|
vertex1: { x: currentMidX.toNumber(), y: prevLine.y1 },
|
|
vertex2: { x: currentMidX.toNumber(), y: currentMidY.toNumber() },
|
|
}
|
|
roof.lines
|
|
.filter((line) => Math.sign(line.x2 - line.x1) === currentVectorX && Math.sign(line.y2 - line.y1) === currentVectorY)
|
|
.forEach((line) => {
|
|
const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
|
|
const intersection = edgesIntersection(checkEdge, lineEdge)
|
|
if (intersection) {
|
|
intersection.x = Math.round(intersection.x)
|
|
intersection.y = Math.round(intersection.y)
|
|
if (
|
|
(line.x1 <= intersection.x && intersection.x <= line.x2 && line.y1 <= intersection.y && intersection.y <= line.y2) ||
|
|
(line.x2 <= intersection.x && intersection.x <= line.x1 && line.y2 <= intersection.y && intersection.y <= line.y1)
|
|
) {
|
|
intersectionRoofs.push({
|
|
line,
|
|
intersection,
|
|
size: Big(intersection.x).minus(currentMidX).abs().pow(2).plus(Big(intersection.y).minus(currentMidY).abs().pow(2)).sqrt(),
|
|
})
|
|
}
|
|
}
|
|
})
|
|
}
|
|
if (intersectionRoofs.length > 0) {
|
|
const currentRoof = intersectionRoofs.sort((a, b) => a.size - b.size)[0].line
|
|
roofX1 = Big(currentRoof.x1)
|
|
roofY1 = Big(currentRoof.y1)
|
|
roofX2 = Big(currentRoof.x2)
|
|
roofY2 = Big(currentRoof.y2)
|
|
}
|
|
|
|
let prevRoofLine, nextRoofLine
|
|
const roofEdge = {
|
|
vertex1: { x: roofX1.toNumber(), y: roofY1.toNumber() },
|
|
vertex2: { x: roofX2.toNumber(), y: roofY2.toNumber() },
|
|
}
|
|
roof.lines
|
|
.filter((line) => (currentVectorX === 0 ? line.y1 === line.y2 : line.x1 === line.x2))
|
|
.forEach((line) => {
|
|
const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
|
|
const intersection = edgesIntersection(roofEdge, lineEdge)
|
|
if (intersection) {
|
|
intersection.x = Math.round(intersection.x)
|
|
intersection.y = Math.round(intersection.y)
|
|
if (
|
|
(line.x1 <= intersection.x && intersection.x <= line.x2 && line.y1 <= intersection.y && intersection.y <= line.y2) ||
|
|
(line.x2 <= intersection.x && intersection.x <= line.x1 && line.y2 <= intersection.y && intersection.y <= line.y1)
|
|
) {
|
|
if (roofX1.eq(intersection.x) && roofY1.eq(intersection.y)) {
|
|
prevRoofLine = line
|
|
}
|
|
if (roofX2.eq(intersection.x) && roofY2.eq(intersection.y)) {
|
|
nextRoofLine = line
|
|
}
|
|
}
|
|
}
|
|
})
|
|
const prevRoofEdge = {
|
|
vertex1: { x: prevRoofLine.x2, y: prevRoofLine.y2 },
|
|
vertex2: { x: prevRoofLine.x1, y: prevRoofLine.y1 },
|
|
}
|
|
const nextRoofEdge = {
|
|
vertex1: { x: nextRoofLine.x1, y: nextRoofLine.y1 },
|
|
vertex2: { x: nextRoofLine.x2, y: nextRoofLine.y2 },
|
|
}
|
|
|
|
baseGableRidgeLines.forEach((ridge) => {
|
|
const ridgeEdge = { vertex1: { x: ridge.x1, y: ridge.y1 }, vertex2: { x: ridge.x2, y: ridge.y2 } }
|
|
const prevIs = edgesIntersection(prevRoofEdge, ridgeEdge)
|
|
const nextIs = edgesIntersection(nextRoofEdge, ridgeEdge)
|
|
if (
|
|
prevIs &&
|
|
((ridgeEdge.vertex1.x === prevIs.x && ridgeEdge.vertex1.y === prevIs.y) ||
|
|
(ridgeEdge.vertex2.x === prevIs.x && ridgeEdge.vertex2.y === prevIs.y))
|
|
) {
|
|
prevLineRidges.push({
|
|
ridge,
|
|
size: calcLinePlaneSize({ x1: roofX1, y1: roofY1, x2: prevIs.x, y2: prevIs.y }),
|
|
})
|
|
}
|
|
if (
|
|
nextIs &&
|
|
((ridgeEdge.vertex1.x === nextIs.x && ridgeEdge.vertex1.y === nextIs.y) ||
|
|
(ridgeEdge.vertex2.x === nextIs.x && ridgeEdge.vertex2.y === nextIs.y))
|
|
) {
|
|
nextLineRidges.push({
|
|
ridge,
|
|
size: calcLinePlaneSize({ x1: roofX2, y1: roofY2, x2: nextIs.x, y2: nextIs.y }),
|
|
})
|
|
}
|
|
})
|
|
|
|
const polygonPoints = []
|
|
|
|
let prevLineRidge, nextLineRidge
|
|
if (prevLineRidges.length > 0) {
|
|
prevLineRidges = prevLineRidges
|
|
.filter((line) => {
|
|
const ridge = line.ridge
|
|
if (currentVectorX === 0) {
|
|
return ridge.y1 === roofY1.toNumber() || ridge.y2 === roofY1.toNumber()
|
|
} else {
|
|
return ridge.x1 === roofX1.toNumber() || ridge.x2 === roofX1.toNumber()
|
|
}
|
|
})
|
|
.sort((a, b) => a.size - b.size)
|
|
prevLineRidge = prevLineRidges[0].ridge
|
|
}
|
|
if (nextLineRidges.length > 0) {
|
|
nextLineRidges = nextLineRidges
|
|
.filter((line) => {
|
|
const ridge = line.ridge
|
|
if (currentVectorX === 0) {
|
|
return ridge.y1 === roofY2.toNumber() || ridge.y2 === roofY2.toNumber()
|
|
} else {
|
|
return ridge.x1 === roofX2.toNumber() || ridge.x2 === roofX2.toNumber()
|
|
}
|
|
})
|
|
.sort((a, b) => a.size - b.size)
|
|
nextLineRidge = nextLineRidges[0].ridge
|
|
}
|
|
const ridgeLine = prevLineRidge === undefined ? nextLineRidge : prevLineRidge
|
|
|
|
/** 마루선이 한쪽만 존재 연결될 경우 다각형을 그린다.*/
|
|
if (prevLineRidge === undefined || nextLineRidge === undefined) {
|
|
const points = [
|
|
{ x: roofX1.toNumber(), y: roofY1.toNumber() },
|
|
{ x: roofX2.toNumber(), y: roofY2.toNumber() },
|
|
{ x: ridgeLine.x1, y: ridgeLine.y1 },
|
|
{ x: ridgeLine.x2, y: ridgeLine.y2 },
|
|
]
|
|
const minX = Math.min(ridgeLine.x1, ridgeLine.x2, roofX1.toNumber(), roofX2.toNumber())
|
|
const minY = Math.min(ridgeLine.y1, ridgeLine.y2, roofY1.toNumber(), roofY2.toNumber())
|
|
|
|
let polygonPoints = []
|
|
|
|
polygonPoints.push(points.find((point) => point.x === minX && point.y === minY))
|
|
|
|
let i = 0
|
|
let length = points.length
|
|
while (i < length - 1) {
|
|
const currentPoint = polygonPoints[i]
|
|
if (i === 0) {
|
|
polygonPoints.push(points.find((point) => point.x === currentPoint.x && point.y > currentPoint.y))
|
|
i++
|
|
} else {
|
|
const prevPoint = polygonPoints[(i - 1 + polygonPoints.length) % polygonPoints.length]
|
|
const vectorX = Math.sign(prevPoint.x - currentPoint.x)
|
|
const vectorY = Math.sign(prevPoint.y - currentPoint.y)
|
|
if (i % 2 === 1) {
|
|
/** 가로선 찾기 */
|
|
const nextPoint = points.find((point) => point.y === currentPoint.y && point.x !== currentPoint.x)
|
|
if (nextPoint === undefined) {
|
|
const connectLine = roof.lines.find((line) => line.y1 === line.y2 && line.x1 === currentPoint.x && line.y1 === currentPoint.y)
|
|
if (connectLine) {
|
|
polygonPoints.push(
|
|
connectLine.x1 === currentPoint.x && connectLine.y1 === currentPoint.y
|
|
? { x: connectLine.x2, y: connectLine.y2 }
|
|
: { x: connectLine.x1, y: connectLine.y1 },
|
|
)
|
|
} else {
|
|
const intersectLine = roof.lines.find(
|
|
(line) =>
|
|
line.y1 === currentPoint.y &&
|
|
line.y2 === currentPoint.y &&
|
|
((line.x1 <= currentPoint.x && line.x2 >= currentPoint.x) || (line.x2 <= currentPoint.x && line.x1 >= currentPoint.x)),
|
|
)
|
|
/** 이전 라인이 위에서 아래로 항하면 오른쪽 포인트 찾기*/
|
|
if (vectorY === 1) {
|
|
polygonPoints.push({ x: intersectLine.x1, y: intersectLine.y1 })
|
|
}
|
|
/** 이전 라인이 아래에서 위로 항하면 왼쪽 포인트 찾기*/
|
|
if (vectorY === -1) {
|
|
polygonPoints.push({ x: intersectLine.x2, y: intersectLine.y2 })
|
|
}
|
|
}
|
|
length = length + 1
|
|
} else {
|
|
polygonPoints.push(nextPoint)
|
|
}
|
|
i++
|
|
} else {
|
|
/** 세로선 찾기*/
|
|
const nextPoint = points.find((point) => point.x === currentPoint.x && point.y !== currentPoint.y)
|
|
if (nextPoint === undefined) {
|
|
const connectLine = roof.lines.find((line) => line.x1 === line.x2 && line.x1 === currentPoint.x && line.y1 === currentPoint.y)
|
|
if (connectLine) {
|
|
polygonPoints.push(
|
|
connectLine.x1 === currentPoint.x && connectLine.y1 === currentPoint.y
|
|
? { x: connectLine.x2, y: connectLine.y2 }
|
|
: { x: connectLine.x1, y: connectLine.y1 },
|
|
)
|
|
} else {
|
|
const intersectLine = roof.lines.find(
|
|
(line) =>
|
|
line.x1 === currentPoint.x &&
|
|
line.x2 === currentPoint.x &&
|
|
((line.y1 <= currentPoint.y && line.y2 >= currentPoint.y) || (line.y2 <= currentPoint.y && line.y1 >= currentPoint.y)),
|
|
)
|
|
/** 이전 라인이 왼쪽에서 오른쪽로 항하면 위쪽 포인트 찾기*/
|
|
if (vectorX === -1) {
|
|
polygonPoints.push({ x: intersectLine.x1, y: intersectLine.y1 })
|
|
}
|
|
/** 이전 라인이 오른에서 왼쪽으로 항하면 아래쪽 포인트 찾기*/
|
|
if (vectorX === 1) {
|
|
polygonPoints.push({ x: intersectLine.x2, y: intersectLine.y2 })
|
|
}
|
|
}
|
|
length = length + 1
|
|
} else {
|
|
polygonPoints.push(nextPoint)
|
|
}
|
|
i++
|
|
}
|
|
}
|
|
}
|
|
polygonPoints = getSortedPoint(polygonPoints)
|
|
polygonPoints.forEach((currentPoint, index) => {
|
|
const nextPoint = polygonPoints[(index + 1) % polygonPoints.length]
|
|
const points = [currentPoint.x, currentPoint.y, nextPoint.x, nextPoint.y]
|
|
const isParallel = ridgeLine.x1 === ridgeLine.x2 ? currentPoint.x === nextPoint.x : currentPoint.y === nextPoint.y
|
|
|
|
let line
|
|
if (isParallel) {
|
|
line = drawRoofLine(points, canvas, roof, textMode)
|
|
baseGableLines.push(line)
|
|
} else {
|
|
line = drawHipLine(points, canvas, roof, textMode, null, currentDegree, currentDegree)
|
|
baseHipLines.push({ x1: line.x1, y1: line.y1, x2: line.x2, y2: line.y2, line })
|
|
}
|
|
})
|
|
} else {
|
|
/** 마루에서 현재라인으로 향하는 외벽선까지의 포인트를 확인 하여 처리*/
|
|
let checkEdge
|
|
if (currentVectorX === 0) {
|
|
checkEdge = {
|
|
vertex1: { x: ridgeLine.x1, y: roofY1.toNumber() },
|
|
vertex2: { x: roofX1.toNumber(), y: roofY1.toNumber() },
|
|
}
|
|
} else {
|
|
checkEdge = {
|
|
vertex1: { x: roofX1.toNumber(), y: ridgeLine.y1 },
|
|
vertex2: { x: roofX1.toNumber(), y: roofY1.toNumber() },
|
|
}
|
|
}
|
|
const checkVectorX = Math.sign(checkEdge.vertex1.x - checkEdge.vertex2.x)
|
|
const checkVectorY = Math.sign(checkEdge.vertex1.y - checkEdge.vertex2.y)
|
|
const intersectPoints = []
|
|
roof.lines
|
|
.filter((line) => (currentVectorX === 0 ? line.x1 === line.x2 : line.y1 === line.y2))
|
|
.forEach((line) => {
|
|
const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
|
|
const is = edgesIntersection(checkEdge, lineEdge)
|
|
if (is) {
|
|
const isVectorX = Math.sign(checkEdge.vertex1.x - is.x)
|
|
const isVectorY = Math.sign(checkEdge.vertex1.y - is.y)
|
|
const isLineOtherPoint =
|
|
is.x === line.x1 && is.y === line.y1
|
|
? { x: line.x2, y: line.y2 }
|
|
: {
|
|
x: line.x1,
|
|
y: line.y1,
|
|
}
|
|
const lineVectorX = Math.sign(isLineOtherPoint.x - is.x)
|
|
const lineVectorY = Math.sign(isLineOtherPoint.y - is.y)
|
|
if (checkVectorX === isVectorX && checkVectorY === isVectorY && lineVectorX === currentVectorX && lineVectorY === currentVectorY) {
|
|
intersectPoints.push(is)
|
|
}
|
|
}
|
|
})
|
|
|
|
let intersect = intersectPoints[0]
|
|
if (currentVectorX === 0) {
|
|
polygonPoints.push({ x: intersect.x, y: roofY1.toNumber() }, { x: intersect.x, y: roofY2.toNumber() })
|
|
} else {
|
|
polygonPoints.push({ x: roofX1.toNumber(), y: intersect.y }, { x: roofX2.toNumber(), y: intersect.y })
|
|
}
|
|
|
|
if (prevLineRidge === nextLineRidge) {
|
|
/** 4각*/
|
|
polygonPoints.push({ x: prevLineRidge.x1, y: prevLineRidge.y1 }, { x: prevLineRidge.x2, y: prevLineRidge.y2 })
|
|
} else {
|
|
/** 6각이상*/
|
|
let isOverLap =
|
|
currentVectorX === 0
|
|
? (prevLineRidge.y1 <= nextLineRidge.y1 && prevLineRidge.y2 >= nextLineRidge.y1) ||
|
|
(prevLineRidge.y1 >= nextLineRidge.y1 && prevLineRidge.y2 <= nextLineRidge.y1) ||
|
|
(prevLineRidge.y1 <= nextLineRidge.y2 && prevLineRidge.y2 >= nextLineRidge.y2) ||
|
|
(prevLineRidge.y1 >= nextLineRidge.y2 && prevLineRidge.y2 <= nextLineRidge.y2)
|
|
: (prevLineRidge.x1 <= nextLineRidge.x1 && prevLineRidge.x2 >= nextLineRidge.x1) ||
|
|
(prevLineRidge.x1 >= nextLineRidge.x1 && prevLineRidge.x2 <= nextLineRidge.x1) ||
|
|
(prevLineRidge.x1 <= nextLineRidge.x2 && prevLineRidge.x2 >= nextLineRidge.x2) ||
|
|
(prevLineRidge.x1 >= nextLineRidge.x2 && prevLineRidge.x2 <= nextLineRidge.x2)
|
|
if (isOverLap) {
|
|
const prevDistance = currentVectorX === 0 ? Math.abs(prevLineRidge.x1 - roofX1.toNumber()) : Math.abs(prevLineRidge.y1 - roofY1.toNumber())
|
|
const nextDistance = currentVectorX === 0 ? Math.abs(nextLineRidge.x1 - roofX1.toNumber()) : Math.abs(nextLineRidge.y1 - roofY1.toNumber())
|
|
/** 현재 지붕 라인과 먼 라인의 포인트를 온전히 사용한다. */
|
|
if (prevDistance <= nextDistance) {
|
|
polygonPoints.push(
|
|
{ x: nextLineRidge.x1, y: nextLineRidge.y1 },
|
|
{
|
|
x: nextLineRidge.x2,
|
|
y: nextLineRidge.y2,
|
|
},
|
|
)
|
|
/** 이전라인과 교차한 마루의 포인트*/
|
|
const prevRidgePoint1 =
|
|
currentVectorX === 0
|
|
? roofY1.toNumber() === prevLineRidge.y1
|
|
? { x: prevLineRidge.x1, y: prevLineRidge.y1 }
|
|
: { x: prevLineRidge.x2, y: prevLineRidge.y2 }
|
|
: roofX1.toNumber() === prevLineRidge.x1
|
|
? { x: prevLineRidge.x1, y: prevLineRidge.y1 }
|
|
: { x: prevLineRidge.x2, y: prevLineRidge.y2 }
|
|
|
|
polygonPoints.push(prevRidgePoint1)
|
|
|
|
/** 다음 라인과 교차한 마루의 포인트 중 라인과 접하지 않은 포인트*/
|
|
const checkRidgePoint =
|
|
currentVectorX === 0
|
|
? roofY2.toNumber() !== nextLineRidge.y1
|
|
? { x: nextLineRidge.x1, y: nextLineRidge.y1 }
|
|
: { x: nextLineRidge.x2, y: nextLineRidge.y2 }
|
|
: roofX2.toNumber() !== nextLineRidge.x1
|
|
? { x: nextLineRidge.x1, y: nextLineRidge.y1 }
|
|
: { x: nextLineRidge.x2, y: nextLineRidge.y2 }
|
|
|
|
const prevRidgePoint2 =
|
|
currentVectorX === 0
|
|
? { x: prevRidgePoint1.x, y: checkRidgePoint.y }
|
|
: {
|
|
x: checkRidgePoint.x,
|
|
y: prevRidgePoint1.y,
|
|
}
|
|
polygonPoints.push(prevRidgePoint2)
|
|
} else {
|
|
polygonPoints.push(
|
|
{ x: prevLineRidge.x1, y: prevLineRidge.y1 },
|
|
{
|
|
x: prevLineRidge.x2,
|
|
y: prevLineRidge.y2,
|
|
},
|
|
)
|
|
|
|
/** 다음라인과 교차한 마루의 포인트*/
|
|
const nextRidgePoint1 =
|
|
currentVectorX === 0
|
|
? roofY2.toNumber() === nextLineRidge.y1
|
|
? { x: nextLineRidge.x1, y: nextLineRidge.y1 }
|
|
: { x: nextLineRidge.x2, y: nextLineRidge.y2 }
|
|
: roofX2.toNumber() === nextLineRidge.x1
|
|
? { x: nextLineRidge.x1, y: nextLineRidge.y1 }
|
|
: { x: nextLineRidge.x2, y: nextLineRidge.y2 }
|
|
polygonPoints.push(nextRidgePoint1)
|
|
|
|
/** 이전 라인과 교차한 마루의 포인트 중 라인과 접하지 않은 포인트*/
|
|
const checkRidgePoint =
|
|
currentVectorX === 0
|
|
? roofY1.toNumber() !== prevLineRidge.y1
|
|
? { x: prevLineRidge.x1, y: prevLineRidge.y1 }
|
|
: { x: prevLineRidge.x2, y: prevLineRidge.y2 }
|
|
: roofX1.toNumber() !== prevLineRidge.x1
|
|
? { x: prevLineRidge.x1, y: prevLineRidge.y1 }
|
|
: { x: prevLineRidge.x2, y: prevLineRidge.y2 }
|
|
const nextRidgePoint2 =
|
|
currentVectorX === 0
|
|
? { x: nextRidgePoint1.x, y: checkRidgePoint.y }
|
|
: {
|
|
x: checkRidgePoint.x,
|
|
y: nextRidgePoint1.y,
|
|
}
|
|
polygonPoints.push(nextRidgePoint2)
|
|
}
|
|
} else {
|
|
/** 마루가 겹치지 않을때 */
|
|
const otherRidgeLines = []
|
|
|
|
baseGableRidgeLines
|
|
.filter((ridge) => ridge !== prevLineRidge && ridge !== nextLineRidge)
|
|
.filter((ridge) => (currentVectorX === 0 ? ridge.x1 === ridge.x2 : ridge.y1 === ridge.y2))
|
|
.filter((ridge) =>
|
|
currentVectorX === 0 ? nextVectorX === Math.sign(nextLine.x1 - ridge.x1) : nextVectorY === Math.sign(nextLine.y1 - ridge.y1),
|
|
)
|
|
.forEach((ridge) => {
|
|
const size = currentVectorX === 0 ? Math.abs(nextLine.x1 - ridge.x1) : Math.abs(nextLine.y1 - ridge.y1)
|
|
otherRidgeLines.push({ ridge, size })
|
|
})
|
|
if (otherRidgeLines.length > 0) {
|
|
const otherRidge = otherRidgeLines.sort((a, b) => a.size - b.size)[0].ridge
|
|
/**
|
|
* otherRidge이 prevRidgeLine, nextRidgeLine 과 currentLine의 사이에 있는지 확인해서 분할하여 작업
|
|
* 지붕의 덮힘이 다르기 때문
|
|
*/
|
|
const isInside =
|
|
currentVectorX === 0
|
|
? Math.abs(currentLine.x1 - otherRidge.x1) < Math.abs(currentLine.x1 - prevLineRidge.x1) &&
|
|
Math.abs(currentLine.x1 - otherRidge.x1) < Math.abs(currentLine.x1 - nextLineRidge.x1)
|
|
: Math.abs(currentLine.y1 - otherRidge.y1) < Math.abs(currentLine.y1 - prevLineRidge.y1) &&
|
|
Math.abs(currentLine.y1 - otherRidge.y1) < Math.abs(currentLine.y1 - nextLineRidge.y1)
|
|
|
|
if (isInside) {
|
|
polygonPoints.push(
|
|
{ x: prevLineRidge.x1, y: prevLineRidge.y1 },
|
|
{ x: prevLineRidge.x2, y: prevLineRidge.y2 },
|
|
{ x: nextLineRidge.x1, y: nextLineRidge.y1 },
|
|
{ x: nextLineRidge.x2, y: nextLineRidge.y2 },
|
|
)
|
|
|
|
let ridgeAllPoints = [
|
|
{ x: prevLineRidge.x1, y: prevLineRidge.y1 },
|
|
{ x: prevLineRidge.x2, y: prevLineRidge.y2 },
|
|
{ x: nextLineRidge.x1, y: nextLineRidge.y1 },
|
|
{ x: nextLineRidge.x2, y: nextLineRidge.y2 },
|
|
]
|
|
let ridgePoints = []
|
|
ridgeAllPoints.forEach((point) => {
|
|
let isOnLine = false
|
|
roof.lines.forEach((line) => {
|
|
if (isPointOnLine({ x1: line.x1, y1: line.y1, x2: line.x2, y2: line.y2 }, point)) {
|
|
isOnLine = true
|
|
}
|
|
})
|
|
if (!isOnLine) {
|
|
ridgePoints.push(point)
|
|
}
|
|
})
|
|
if (ridgePoints.length === 2) {
|
|
if (Math.sign(otherRidge.x1 - otherRidge.x2) === 0) {
|
|
polygonPoints.push(
|
|
{ x: otherRidge.x1, y: ridgePoints[0].y },
|
|
{
|
|
x: otherRidge.x1,
|
|
y: ridgePoints[1].y,
|
|
},
|
|
)
|
|
} else {
|
|
polygonPoints.push(
|
|
{ x: ridgePoints[0].x, y: otherRidge.y1 },
|
|
{
|
|
x: ridgePoints[1].x,
|
|
y: otherRidge.y1,
|
|
},
|
|
)
|
|
}
|
|
}
|
|
} else {
|
|
polygonPoints.push({ x: otherRidge.x1, y: otherRidge.y1 }, { x: otherRidge.x2, y: otherRidge.y2 })
|
|
|
|
let ridgePoints = [
|
|
{ x: prevLineRidge.x1, y: prevLineRidge.y1 },
|
|
{ x: prevLineRidge.x2, y: prevLineRidge.y2 },
|
|
{ x: nextLineRidge.x1, y: nextLineRidge.y1 },
|
|
{ x: nextLineRidge.x2, y: nextLineRidge.y2 },
|
|
]
|
|
|
|
ridgePoints.forEach((point) => {
|
|
let isOnLine = false
|
|
roof.lines.forEach((line) => {
|
|
if (isPointOnLine({ x1: line.x1, y1: line.y1, x2: line.x2, y2: line.y2 }, point)) {
|
|
isOnLine = true
|
|
}
|
|
})
|
|
if (isOnLine) {
|
|
polygonPoints.push(point)
|
|
}
|
|
})
|
|
|
|
if (Math.sign(otherRidge.x1 - otherRidge.x2) === 0) {
|
|
const prevY =
|
|
(prevLineRidge.y1 <= otherRidge.y1 && otherRidge.y1 <= prevLineRidge.y2) ||
|
|
(prevLineRidge.y1 >= otherRidge.y1 && otherRidge.y1 >= prevLineRidge.y2)
|
|
? otherRidge.y1
|
|
: otherRidge.y2
|
|
const nextY =
|
|
(nextLineRidge.y1 <= otherRidge.y1 && otherRidge.y1 <= nextLineRidge.y2) ||
|
|
(nextLineRidge.y1 >= otherRidge.y1 && otherRidge.y1 >= nextLineRidge.y2)
|
|
? otherRidge.y1
|
|
: otherRidge.y2
|
|
polygonPoints.push({ x: prevLineRidge.x1, y: prevY }, { x: nextLineRidge.x1, y: nextY })
|
|
} else {
|
|
const prevX =
|
|
(prevLineRidge.x1 <= otherRidge.x1 && otherRidge.x1 <= prevLineRidge.x2) ||
|
|
(prevLineRidge.x1 >= otherRidge.x1 && otherRidge.x1 >= prevLineRidge.x2)
|
|
? otherRidge.x1
|
|
: otherRidge.x2
|
|
const nextX =
|
|
(nextLineRidge.x1 <= otherRidge.x1 && otherRidge.x1 <= nextLineRidge.x2) ||
|
|
(nextLineRidge.x1 >= otherRidge.x1 && otherRidge.x1 >= nextLineRidge.x2)
|
|
? otherRidge.x1
|
|
: otherRidge.x2
|
|
polygonPoints.push({ x: prevX, y: prevLineRidge.y1 }, { x: nextX, y: nextLineRidge.y1 })
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
const sortedPolygonPoints = getSortedPoint(polygonPoints)
|
|
|
|
/** 외벽선 밖으로 나가있는 포인트*/
|
|
const outsidePoints = polygonPoints.filter((point) => {
|
|
let isOutside = true
|
|
roof.lines.forEach((line) => {
|
|
if (
|
|
(line.x1 <= point.x && line.x2 >= point.x && line.y1 <= point.y && line.y2 >= point.y) ||
|
|
(line.x1 >= point.x && line.x2 <= point.x && line.y1 >= point.y && line.y2 <= point.y)
|
|
) {
|
|
isOutside = false
|
|
}
|
|
})
|
|
baseGableRidgeLines.forEach((line) => {
|
|
if (
|
|
(line.x1 <= point.x && line.x2 >= point.x && line.y1 <= point.y && line.y2 >= point.y) ||
|
|
(line.x1 >= point.x && line.x2 <= point.x && line.y1 >= point.y && line.y2 <= point.y)
|
|
) {
|
|
isOutside = false
|
|
}
|
|
})
|
|
return isOutside
|
|
})
|
|
|
|
if (outsidePoints.length > 0) {
|
|
sortedPolygonPoints.forEach((currentPoint, index) => {
|
|
if (outsidePoints.includes(currentPoint)) {
|
|
const prevPoint = sortedPolygonPoints[(index - 1 + sortedPolygonPoints.length) % sortedPolygonPoints.length]
|
|
const nextPoint = sortedPolygonPoints[(index + 1) % sortedPolygonPoints.length]
|
|
const vectorX = Math.sign(currentPoint.x - prevPoint.x)
|
|
const vectorY = Math.sign(currentPoint.y - prevPoint.y)
|
|
|
|
const checkEdge = {
|
|
vertex1: { x: prevPoint.x, y: prevPoint.y },
|
|
vertex2: { x: currentPoint.x, y: currentPoint.y },
|
|
}
|
|
const intersectPoints = []
|
|
roof.lines
|
|
.filter((line) => (vectorX === 0 ? line.x1 !== line.x2 : line.y1 !== line.y2))
|
|
.forEach((line) => {
|
|
const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
|
|
const is = edgesIntersection(checkEdge, lineEdge)
|
|
if (is && !is.isIntersectionOutside) {
|
|
const isVectorX = Math.sign(is.x - prevPoint.x)
|
|
const isVectorY = Math.sign(is.y - prevPoint.y)
|
|
if ((vectorX === 0 && vectorY === isVectorY) || (vectorY === 0 && vectorX === isVectorX)) {
|
|
intersectPoints.push(is)
|
|
}
|
|
}
|
|
})
|
|
if (intersectPoints.length > 0) {
|
|
const intersection = intersectPoints[0]
|
|
if (vectorX === 0) {
|
|
currentPoint.y = intersection.y
|
|
nextPoint.y = intersection.y
|
|
} else {
|
|
currentPoint.x = intersection.x
|
|
nextPoint.x = intersection.x
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
sortedPolygonPoints.forEach((startPoint, index) => {
|
|
let endPoint
|
|
if (index === sortedPolygonPoints.length - 1) {
|
|
endPoint = sortedPolygonPoints[0]
|
|
} else {
|
|
endPoint = sortedPolygonPoints[index + 1]
|
|
}
|
|
|
|
const hipLine = drawHipLine([startPoint.x, startPoint.y, endPoint.x, endPoint.y], canvas, roof, textMode, null, currentDegree, currentDegree)
|
|
if (currentVectorX === 0) {
|
|
if (Math.sign(startPoint.x - endPoint.x) === 0) {
|
|
hipLine.attributes.actualSize = hipLine.attributes.planeSize
|
|
}
|
|
} else {
|
|
if (Math.sign(startPoint.y - endPoint.y) === 0) {
|
|
hipLine.attributes.actualSize = hipLine.attributes.planeSize
|
|
}
|
|
}
|
|
baseHipLines.push({ x1: hipLine.x1, y1: hipLine.y1, x2: hipLine.x2, y2: hipLine.y2, line: hipLine })
|
|
})
|
|
}
|
|
})
|
|
|
|
drawGablePolygonSecond.forEach((current) => {
|
|
const { currentBaseLine, prevBaseLine, nextBaseLine } = current
|
|
const currentLine = currentBaseLine.line
|
|
const prevLine = prevBaseLine.line
|
|
const nextLine = nextBaseLine.line
|
|
let { x1, x2, y1, y2 } = currentBaseLine
|
|
|
|
const currentVectorX = Math.sign(currentLine.x2 - currentLine.x1)
|
|
const currentVectorY = Math.sign(currentLine.y2 - currentLine.y1)
|
|
const currentDegree = currentLine.attributes.pitch > 0 ? getDegreeByChon(currentLine.attributes.pitch) : currentLine.attributes.degree
|
|
|
|
const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint)
|
|
const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint)
|
|
|
|
const polygonPoints = []
|
|
if (Big(prevAngle).minus(Big(nextAngle)).abs().eq(180)) {
|
|
const currentRidge = baseGableRidgeLines.find((line) =>
|
|
currentVectorX === 0
|
|
? (line.y1 === y1 && line.y2 === y2) || (line.y1 === y2 && line.y2 === y1)
|
|
: (line.x1 === x1 && line.x2 === x2) || (line.x1 === x2 && line.x2 === x1),
|
|
)
|
|
if (currentRidge) {
|
|
const ridgeVectorX = Math.sign(currentRidge.x1 - currentRidge.x2)
|
|
const ridgeVectorY = Math.sign(currentRidge.y1 - currentRidge.y2)
|
|
|
|
let checkEdge
|
|
if (currentVectorX === 0) {
|
|
checkEdge = {
|
|
vertex1: { x: currentRidge.x1, y: currentRidge.y1 },
|
|
vertex2: { x: currentLine.x1, y: currentRidge.y1 },
|
|
}
|
|
} else {
|
|
checkEdge = {
|
|
vertex1: { x: currentRidge.x1, y: currentRidge.y1 },
|
|
vertex2: { x: currentRidge.x1, y: currentLine.y1 },
|
|
}
|
|
}
|
|
const isRoofLines = []
|
|
roof.lines
|
|
.filter((line) => (currentVectorX === 0 ? line.x1 === line.x2 : line.y1 === line.y2))
|
|
.forEach((line) => {
|
|
const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
|
|
const is = edgesIntersection(checkEdge, lineEdge)
|
|
if (is) {
|
|
const checkVectorX = Math.sign(checkEdge.vertex1.x - checkEdge.vertex2.x)
|
|
const checkVectorY = Math.sign(checkEdge.vertex1.y - checkEdge.vertex2.y)
|
|
const isVectorX = Math.sign(checkEdge.vertex1.x - is.x)
|
|
const isVectorY = Math.sign(checkEdge.vertex1.y - is.y)
|
|
if ((ridgeVectorX === 0 && checkVectorX === isVectorX) || (ridgeVectorY === 0 && checkVectorY === isVectorY)) {
|
|
const size = ridgeVectorX === 0 ? Math.abs(checkEdge.vertex1.x - is.x) : Math.abs(checkEdge.vertex1.y - is.y)
|
|
isRoofLines.push({ line, size })
|
|
}
|
|
}
|
|
})
|
|
isRoofLines.sort((a, b) => a.size - b.size)
|
|
const roofLine = isRoofLines[0].line
|
|
polygonPoints.push({ x: currentRidge.x1, y: currentRidge.y1 }, { x: currentRidge.x2, y: currentRidge.y2 })
|
|
if (ridgeVectorX === 0) {
|
|
polygonPoints.push({ x: roofLine.x1, y: currentRidge.y1 }, { x: roofLine.x1, y: currentRidge.y2 })
|
|
} else {
|
|
polygonPoints.push({ x: currentRidge.x1, y: roofLine.y1 }, { x: currentRidge.x2, y: roofLine.y1 })
|
|
}
|
|
}
|
|
} else {
|
|
const prevEdge = { vertex1: { x: prevLine.x1, y: prevLine.y1 }, vertex2: { x: prevLine.x2, y: prevLine.y2 } }
|
|
const prevRidge = baseGableRidgeLines.find((ridge) => {
|
|
const ridgeEdge = { vertex1: { x: ridge.x1, y: ridge.y1 }, vertex2: { x: ridge.x2, y: ridge.y2 } }
|
|
const is = edgesIntersection(prevEdge, ridgeEdge)
|
|
if (is && !is.isIntersectionOutside) {
|
|
return ridge
|
|
}
|
|
})
|
|
|
|
const nextEdge = { vertex1: { x: nextLine.x1, y: nextLine.y1 }, vertex2: { x: nextLine.x2, y: nextLine.y2 } }
|
|
const nextRidge = baseGableRidgeLines.find((ridge) => {
|
|
const ridgeEdge = { vertex1: { x: ridge.x1, y: ridge.y1 }, vertex2: { x: ridge.x2, y: ridge.y2 } }
|
|
const is = edgesIntersection(nextEdge, ridgeEdge)
|
|
if (is && !is.isIntersectionOutside) {
|
|
return ridge
|
|
}
|
|
})
|
|
|
|
let currentRidge
|
|
|
|
if (prevRidge) {
|
|
if (
|
|
currentVectorX === 0 &&
|
|
((prevRidge.y1 <= currentLine.y1 && prevRidge.y2 >= currentLine.y1 && prevRidge.y1 <= currentLine.y2 && prevRidge.y2 >= currentLine.y2) ||
|
|
(prevRidge.y1 >= currentLine.y1 && prevRidge.y2 <= currentLine.y1 && prevRidge.y1 >= currentLine.y2 && prevRidge.y2 <= currentLine.y2))
|
|
) {
|
|
currentRidge = prevRidge
|
|
}
|
|
if (
|
|
currentVectorY === 0 &&
|
|
((prevRidge.x1 <= currentLine.x1 && prevRidge.x2 >= currentLine.x1 && prevRidge.x1 <= currentLine.x2 && prevRidge.x2 >= currentLine.x2) ||
|
|
(prevRidge.x1 >= currentLine.x1 && prevRidge.x2 <= currentLine.x1 && prevRidge.x1 >= currentLine.x2 && prevRidge.x2 <= currentLine.x2))
|
|
) {
|
|
currentRidge = prevRidge
|
|
}
|
|
}
|
|
if (nextRidge) {
|
|
if (
|
|
currentVectorX === 0 &&
|
|
((nextRidge.y1 <= currentLine.y1 && nextRidge.y2 >= currentLine.y1 && nextRidge.y1 <= currentLine.y2 && nextRidge.y2 >= currentLine.y2) ||
|
|
(nextRidge.y1 >= currentLine.y1 && nextRidge.y2 <= currentLine.y1 && nextRidge.y1 >= currentLine.y2 && nextRidge.y2 <= currentLine.y2))
|
|
) {
|
|
currentRidge = nextRidge
|
|
}
|
|
if (
|
|
currentVectorY === 0 &&
|
|
((nextRidge.x1 <= currentLine.x1 && nextRidge.x2 >= currentLine.x1 && nextRidge.x1 <= currentLine.x2 && nextRidge.x2 >= currentLine.x2) ||
|
|
(nextRidge.x1 >= currentLine.x1 && nextRidge.x2 <= currentLine.x1 && nextRidge.x1 >= currentLine.x2 && nextRidge.x2 <= currentLine.x2))
|
|
) {
|
|
currentRidge = nextRidge
|
|
}
|
|
}
|
|
if (currentRidge) {
|
|
polygonPoints.push({ x: currentRidge.x1, y: currentRidge.y1 }, { x: currentRidge.x2, y: currentRidge.y2 })
|
|
|
|
/** 마루에서 현재라인으로 향하는 외벽선까지의 포인트를 확인 하여 처리*/
|
|
let checkEdge
|
|
if (currentVectorX === 0) {
|
|
checkEdge = {
|
|
vertex1: { x: currentRidge.x1, y: currentRidge.y1 },
|
|
vertex2: { x: currentLine.x1, y: currentRidge.y1 },
|
|
}
|
|
} else {
|
|
checkEdge = {
|
|
vertex1: { x: currentRidge.x1, y: currentRidge.y1 },
|
|
vertex2: { x: currentRidge.x1, y: currentLine.y1 },
|
|
}
|
|
}
|
|
|
|
const checkVectorX = Math.sign(checkEdge.vertex1.x - checkEdge.vertex2.x)
|
|
const checkVectorY = Math.sign(checkEdge.vertex1.y - checkEdge.vertex2.y)
|
|
const intersectPoints = []
|
|
roof.lines
|
|
.filter((line) => (currentVectorX === 0 ? line.x1 === line.x2 : line.y1 === line.y2))
|
|
.forEach((line) => {
|
|
const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
|
|
const is = edgesIntersection(checkEdge, lineEdge)
|
|
if (is) {
|
|
const isVectorX = Math.sign(checkEdge.vertex1.x - is.x)
|
|
const isVectorY = Math.sign(checkEdge.vertex1.y - is.y)
|
|
const isLineOtherPoint =
|
|
is.x === line.x1 && is.y === line.y1
|
|
? { x: line.x2, y: line.y2 }
|
|
: {
|
|
x: line.x1,
|
|
y: line.y1,
|
|
}
|
|
const isInPoint =
|
|
checkVectorX === 0
|
|
? (currentRidge.x1 <= isLineOtherPoint.x && isLineOtherPoint.x <= currentRidge.x2) ||
|
|
(currentRidge.x1 >= isLineOtherPoint.x && isLineOtherPoint.x >= currentRidge.x2)
|
|
: (currentRidge.y1 <= isLineOtherPoint.y && isLineOtherPoint.y <= currentRidge.y2) ||
|
|
(currentRidge.y1 >= isLineOtherPoint.y && isLineOtherPoint.y >= currentRidge.y2)
|
|
if (checkVectorX === isVectorX && checkVectorY === isVectorY && isInPoint) {
|
|
const size = Big(checkEdge.vertex1.x).minus(is.x).abs().pow(2).plus(Big(checkEdge.vertex1.y).minus(is.y).abs().pow(2)).sqrt()
|
|
intersectPoints.push({ is, size })
|
|
}
|
|
}
|
|
})
|
|
intersectPoints.sort((a, b) => a.size - b.size)
|
|
let intersect = intersectPoints[0].is
|
|
if (currentVectorX === 0) {
|
|
polygonPoints.push({ x: intersect.x, y: currentRidge.y1 }, { x: intersect.x, y: currentRidge.y2 })
|
|
} else {
|
|
polygonPoints.push({ x: currentRidge.x1, y: intersect.y }, { x: currentRidge.x2, y: intersect.y })
|
|
}
|
|
}
|
|
}
|
|
|
|
const sortedPolygonPoints = getSortedPoint(polygonPoints)
|
|
sortedPolygonPoints.forEach((startPoint, index) => {
|
|
let endPoint
|
|
if (index === sortedPolygonPoints.length - 1) {
|
|
endPoint = sortedPolygonPoints[0]
|
|
} else {
|
|
endPoint = sortedPolygonPoints[index + 1]
|
|
}
|
|
const hipLine = drawHipLine([startPoint.x, startPoint.y, endPoint.x, endPoint.y], canvas, roof, textMode, null, currentDegree, currentDegree)
|
|
if (currentVectorX === 0) {
|
|
if (Math.sign(startPoint.x - endPoint.x) === 0) {
|
|
hipLine.attributes.actualSize = hipLine.attributes.planeSize
|
|
}
|
|
} else {
|
|
if (Math.sign(startPoint.y - endPoint.y) === 0) {
|
|
hipLine.attributes.actualSize = hipLine.attributes.planeSize
|
|
}
|
|
}
|
|
baseHipLines.push({ x1: hipLine.x1, y1: hipLine.y1, x2: hipLine.x2, y2: hipLine.y2, line: hipLine })
|
|
})
|
|
})
|
|
|
|
/** 케라바 지붕에 연결된 마루 중 처마라인이 그려지지 않은 경우 확*/
|
|
baseGableRidgeLines.forEach((ridge) => {
|
|
const ridgeVectorX = Math.sign(ridge.x1 - ridge.x2)
|
|
const ridgeVectorY = Math.sign(ridge.y1 - ridge.y2)
|
|
|
|
const firstGableLines = []
|
|
const secondGableLines = []
|
|
|
|
baseGableLines
|
|
.filter((line) => (ridgeVectorX === 0 ? line.x1 !== line.x2 : line.y1 !== line.y2))
|
|
.filter((line) => (line.x1 === ridge.x1 && line.y1 === ridge.y1) || (line.x2 === ridge.x1 && line.y2 === ridge.y1))
|
|
.forEach((line) => firstGableLines.push(line))
|
|
baseGableLines
|
|
.filter((line) => (ridgeVectorX === 0 ? line.x1 !== line.x2 : line.y1 !== line.y2))
|
|
.filter((line) => (line.x1 === ridge.x2 && line.y1 === ridge.y2) || (line.x2 === ridge.x2 && line.y2 === ridge.y2))
|
|
.forEach((line) => secondGableLines.push(line))
|
|
|
|
baseHipLines
|
|
.filter((line) => (ridgeVectorX === 0 ? line.x1 !== line.x2 : line.y1 !== line.y2))
|
|
.filter((line) => (line.x1 === ridge.x1 && line.y1 === ridge.y1) || (line.x2 === ridge.x1 && line.y2 === ridge.y1))
|
|
.forEach((line) => firstGableLines.push(line))
|
|
baseHipLines
|
|
.filter((line) => (ridgeVectorX === 0 ? line.x1 !== line.x2 : line.y1 !== line.y2))
|
|
.filter((line) => (line.x1 === ridge.x2 && line.y1 === ridge.y2) || (line.x2 === ridge.x2 && line.y2 === ridge.y2))
|
|
.forEach((line) => secondGableLines.push(line))
|
|
|
|
let degree1, degree2
|
|
if (firstGableLines.length < 2 || secondGableLines.length < 2) {
|
|
drawBaseLines.forEach((currentBaseLine, index) => {
|
|
let prevBaseLine = drawBaseLines[(index - 1 + drawBaseLines.length) % drawBaseLines.length]
|
|
let nextBaseLine = drawBaseLines[(index + 1) % drawBaseLines.length]
|
|
const currentLine = currentBaseLine.line
|
|
const prevLine = prevBaseLine.line
|
|
const nextLine = nextBaseLine.line
|
|
const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint)
|
|
const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint)
|
|
|
|
if (
|
|
gableType.includes(currentLine.attributes?.type) &&
|
|
eavesType.includes(prevLine.attributes?.type) &&
|
|
eavesType.includes(nextLine.attributes?.type) &&
|
|
Big(prevAngle).minus(Big(nextAngle)).abs().eq(180)
|
|
) {
|
|
if (
|
|
ridgeVectorX === 0 &&
|
|
currentLine.x1 !== currentLine.x2 &&
|
|
((currentLine.x1 <= ridge.x1 && ridge.x1 <= currentLine.x2) || (currentLine.x2 <= ridge.x1 && ridge.x1 <= currentLine.x1))
|
|
) {
|
|
degree1 = prevLine.attributes.pitch > 0 ? getDegreeByChon(prevLine.attributes.pitch) : prevLine.attributes.degree
|
|
degree2 = nextLine.attributes.pitch > 0 ? getDegreeByChon(nextLine.attributes.pitch) : nextLine.attributes.degree
|
|
}
|
|
|
|
if (
|
|
ridgeVectorY === 0 &&
|
|
currentLine.y1 !== currentLine.y2 &&
|
|
((currentLine.y1 <= ridge.y1 && ridge.y1 <= currentLine.y2) || (currentLine.y2 <= ridge.y1 && ridge.y1 <= currentLine.y1))
|
|
) {
|
|
degree1 = prevLine.attributes.pitch > 0 ? getDegreeByChon(prevLine.attributes.pitch) : prevLine.attributes.degree
|
|
degree2 = nextLine.attributes.pitch > 0 ? getDegreeByChon(nextLine.attributes.pitch) : nextLine.attributes.degree
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
if (firstGableLines.length < 2) {
|
|
const connectRoof = roof.lines.find(
|
|
(line) =>
|
|
(line.x1 <= ridge.x1 && line.x2 >= ridge.x1 && line.y1 <= ridge.y1 && line.y2 >= ridge.y1) ||
|
|
(line.x2 <= ridge.x1 && line.x1 >= ridge.x1 && line.y2 <= ridge.y1 && line.y1 >= ridge.y1),
|
|
)
|
|
if (connectRoof) {
|
|
let hipPoint1 = [connectRoof.x1, connectRoof.y1, ridge.x1, ridge.y1]
|
|
let hipPoint2 = [connectRoof.x2, connectRoof.y2, ridge.x1, ridge.y1]
|
|
let intersectPoints1
|
|
let intersectPoints2
|
|
baseHipLines
|
|
.filter((line) => line.x1 !== line.x2 && line.y1 !== line.y2)
|
|
.forEach((hip) => {
|
|
const line = hip.line
|
|
if (
|
|
(hipPoint1[0] <= line.x1 && line.x1 <= hipPoint1[2] && hipPoint1[1] <= line.y1 && line.y1 <= hipPoint1[3]) ||
|
|
(hipPoint1[2] <= line.x1 && line.x1 <= hipPoint1[0] && hipPoint1[3] <= line.y1 && line.y1 <= hipPoint1[1])
|
|
) {
|
|
intersectPoints1 = { x: line.x1, y: line.y1 }
|
|
}
|
|
if (
|
|
(hipPoint1[0] <= line.x2 && line.x2 <= hipPoint1[2] && hipPoint1[1] <= line.y2 && line.y2 <= hipPoint1[3]) ||
|
|
(hipPoint1[2] <= line.x2 && line.x2 <= hipPoint1[0] && hipPoint1[3] <= line.y2 && line.y2 <= hipPoint1[1])
|
|
) {
|
|
intersectPoints1 = { x: line.x2, y: line.y2 }
|
|
}
|
|
if (
|
|
(hipPoint2[0] <= line.x1 && line.x1 <= hipPoint2[2] && hipPoint2[1] <= line.y1 && line.y1 <= hipPoint2[3]) ||
|
|
(hipPoint2[2] <= line.x1 && line.x1 <= hipPoint2[0] && hipPoint2[3] <= line.y1 && line.y1 <= hipPoint2[1])
|
|
) {
|
|
intersectPoints2 = { x: line.x1, y: line.y1 }
|
|
}
|
|
if (
|
|
(hipPoint2[0] <= line.x2 && line.x2 <= hipPoint2[2] && hipPoint2[1] <= line.y2 && line.y2 <= hipPoint2[3]) ||
|
|
(hipPoint2[2] <= line.x2 && line.x2 <= hipPoint2[0] && hipPoint2[3] <= line.y2 && line.y2 <= hipPoint2[1])
|
|
) {
|
|
intersectPoints2 = { x: line.x2, y: line.y2 }
|
|
}
|
|
})
|
|
if (intersectPoints1) {
|
|
hipPoint1 = [intersectPoints1.x, intersectPoints1.y, ridge.x1, ridge.y1]
|
|
}
|
|
if (intersectPoints2) {
|
|
hipPoint2 = [intersectPoints2.x, intersectPoints2.y, ridge.x1, ridge.y1]
|
|
}
|
|
|
|
if (hipPoint1) {
|
|
let alreadyHip = false
|
|
baseHipLines
|
|
.filter(
|
|
(line) =>
|
|
(line.line.x1 === hipPoint1[0] && line.line.y1 === hipPoint1[1] && line.line.x2 === hipPoint1[2] && line.line.y2 === hipPoint1[3]) ||
|
|
(line.line.x2 === hipPoint1[0] && line.line.y2 === hipPoint1[1] && line.line.x1 === hipPoint1[2] && line.line.y1 === hipPoint1[3]),
|
|
)
|
|
.forEach((line) => {
|
|
alreadyHip = true
|
|
})
|
|
baseGableLines
|
|
.filter(
|
|
(line) =>
|
|
(line.x1 === hipPoint1[0] && line.y1 === hipPoint1[1] && line.x2 === hipPoint1[2] && line.y2 === hipPoint1[3]) ||
|
|
(line.x2 === hipPoint1[0] && line.y2 === hipPoint1[1] && line.x1 === hipPoint1[2] && line.y1 === hipPoint1[3]),
|
|
)
|
|
.forEach((line) => {
|
|
alreadyHip = true
|
|
})
|
|
|
|
if (!alreadyHip) {
|
|
const hipLine1 = drawHipLine(hipPoint1, canvas, roof, textMode, null, degree1, degree1)
|
|
baseHipLines.push({ x1: hipLine1.x1, y1: hipLine1.y1, x2: hipLine1.x2, y2: hipLine1.y2, line: hipLine1 })
|
|
}
|
|
}
|
|
if (hipPoint2) {
|
|
let alreadyHip = false
|
|
baseHipLines
|
|
.filter(
|
|
(line) =>
|
|
(line.line.x1 === hipPoint2[0] && line.line.y1 === hipPoint2[1] && line.line.x2 === hipPoint2[2] && line.line.y2 === hipPoint2[3]) ||
|
|
(line.line.x2 === hipPoint2[0] && line.line.y2 === hipPoint2[1] && line.line.x1 === hipPoint2[2] && line.line.y1 === hipPoint2[3]),
|
|
)
|
|
.forEach((line) => {
|
|
alreadyHip = true
|
|
})
|
|
baseGableLines
|
|
.filter(
|
|
(line) =>
|
|
(line.x1 === hipPoint2[0] && line.y1 === hipPoint2[1] && line.x2 === hipPoint2[2] && line.y2 === hipPoint2[3]) ||
|
|
(line.x2 === hipPoint2[0] && line.y2 === hipPoint2[1] && line.x1 === hipPoint2[2] && line.y1 === hipPoint2[3]),
|
|
)
|
|
.forEach((line) => {
|
|
alreadyHip = true
|
|
})
|
|
if (!alreadyHip) {
|
|
const hipLine2 = drawHipLine(hipPoint2, canvas, roof, textMode, null, degree2, degree2)
|
|
baseHipLines.push({ x1: hipLine2.x1, y1: hipLine2.y1, x2: hipLine2.x2, y2: hipLine2.y2, line: hipLine2 })
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (secondGableLines.length < 2) {
|
|
const connectRoof = roof.lines.find(
|
|
(line) =>
|
|
(line.x1 <= ridge.x2 && line.x2 >= ridge.x2 && line.y1 <= ridge.y2 && line.y2 >= ridge.y2) ||
|
|
(line.x2 <= ridge.x2 && line.x1 >= ridge.x2 && line.y2 <= ridge.y2 && line.y1 >= ridge.y2),
|
|
)
|
|
if (connectRoof) {
|
|
let hipPoint1 = [connectRoof.x1, connectRoof.y1, ridge.x2, ridge.y2]
|
|
let hipPoint2 = [connectRoof.x2, connectRoof.y2, ridge.x2, ridge.y2]
|
|
let intersectPoints1
|
|
let intersectPoints2
|
|
|
|
baseHipLines
|
|
.filter((line) => line.x1 !== line.x2 && line.y1 !== line.y2)
|
|
.forEach((hip) => {
|
|
const line = hip.line
|
|
if (
|
|
(hipPoint1[0] <= line.x1 && line.x1 <= hipPoint1[2] && hipPoint1[1] <= line.y1 && line.y1 <= hipPoint1[3]) ||
|
|
(hipPoint1[2] <= line.x1 && line.x1 <= hipPoint1[0] && hipPoint1[3] <= line.y1 && line.y1 <= hipPoint1[1])
|
|
) {
|
|
intersectPoints1 = { x: line.x1, y: line.y1 }
|
|
}
|
|
if (
|
|
(hipPoint1[0] <= line.x2 && line.x2 <= hipPoint1[2] && hipPoint1[1] <= line.y2 && line.y2 <= hipPoint1[3]) ||
|
|
(hipPoint1[2] <= line.x2 && line.x2 <= hipPoint1[0] && hipPoint1[3] <= line.y2 && line.y2 <= hipPoint1[1])
|
|
) {
|
|
intersectPoints1 = { x: line.x2, y: line.y2 }
|
|
}
|
|
if (
|
|
(hipPoint2[0] <= line.x1 && line.x1 <= hipPoint2[2] && hipPoint2[1] <= line.y1 && line.y1 <= hipPoint2[3]) ||
|
|
(hipPoint2[2] <= line.x1 && line.x1 <= hipPoint2[0] && hipPoint2[3] <= line.y1 && line.y1 <= hipPoint2[1])
|
|
) {
|
|
intersectPoints2 = { x: line.x1, y: line.y1 }
|
|
}
|
|
if (
|
|
(hipPoint2[0] <= line.x2 && line.x2 <= hipPoint2[2] && hipPoint2[1] <= line.y2 && line.y2 <= hipPoint2[3]) ||
|
|
(hipPoint2[2] <= line.x2 && line.x2 <= hipPoint2[0] && hipPoint2[3] <= line.y2 && line.y2 <= hipPoint2[1])
|
|
) {
|
|
intersectPoints2 = { x: line.x2, y: line.y2 }
|
|
}
|
|
})
|
|
|
|
if (intersectPoints1) {
|
|
hipPoint1 = [intersectPoints1.x, intersectPoints1.y, ridge.x2, ridge.y2]
|
|
}
|
|
if (intersectPoints2) {
|
|
hipPoint2 = [intersectPoints2.x, intersectPoints2.y, ridge.x2, ridge.y2]
|
|
}
|
|
|
|
if (hipPoint1) {
|
|
let alreadyHip = false
|
|
baseHipLines
|
|
.filter(
|
|
(line) =>
|
|
(line.line.x1 === hipPoint1[0] && line.line.y1 === hipPoint1[1] && line.line.x2 === hipPoint1[2] && line.line.y2 === hipPoint1[3]) ||
|
|
(line.line.x2 === hipPoint1[0] && line.line.y2 === hipPoint1[1] && line.line.x1 === hipPoint1[2] && line.line.y1 === hipPoint1[3]),
|
|
)
|
|
.forEach((line) => {
|
|
alreadyHip = true
|
|
})
|
|
baseGableLines
|
|
.filter(
|
|
(line) =>
|
|
(line.x1 === hipPoint1[0] && line.y1 === hipPoint1[1] && line.x2 === hipPoint1[2] && line.y2 === hipPoint1[3]) ||
|
|
(line.x2 === hipPoint1[0] && line.y2 === hipPoint1[1] && line.x1 === hipPoint1[2] && line.y1 === hipPoint1[3]),
|
|
)
|
|
.forEach((line) => {
|
|
alreadyHip = true
|
|
})
|
|
|
|
if (!alreadyHip) {
|
|
const hipLine1 = drawHipLine(hipPoint1, canvas, roof, textMode, null, degree1, degree1)
|
|
baseHipLines.push({ x1: hipLine1.x1, y1: hipLine1.y1, x2: hipLine1.x2, y2: hipLine1.y2, line: hipLine1 })
|
|
}
|
|
}
|
|
if (hipPoint2) {
|
|
let alreadyHip = false
|
|
baseHipLines
|
|
.filter(
|
|
(line) =>
|
|
(line.line.x1 === hipPoint2[0] && line.line.y1 === hipPoint2[1] && line.line.x2 === hipPoint2[2] && line.line.y2 === hipPoint2[3]) ||
|
|
(line.line.x2 === hipPoint2[0] && line.line.y2 === hipPoint2[1] && line.line.x1 === hipPoint2[2] && line.line.y1 === hipPoint2[3]),
|
|
)
|
|
.forEach((line) => {
|
|
alreadyHip = true
|
|
})
|
|
baseGableLines
|
|
.filter(
|
|
(line) =>
|
|
(line.x1 === hipPoint2[0] && line.y1 === hipPoint2[1] && line.x2 === hipPoint2[2] && line.y2 === hipPoint2[3]) ||
|
|
(line.x2 === hipPoint2[0] && line.y2 === hipPoint2[1] && line.x1 === hipPoint2[2] && line.y1 === hipPoint2[3]),
|
|
)
|
|
.forEach((line) => {
|
|
alreadyHip = true
|
|
})
|
|
if (!alreadyHip) {
|
|
const hipLine2 = drawHipLine(hipPoint2, canvas, roof, textMode, null, degree2, degree2)
|
|
baseHipLines.push({ x1: hipLine2.x1, y1: hipLine2.y1, x2: hipLine2.x2, y2: hipLine2.y2, line: hipLine2 })
|
|
}
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|
|
/** ⨆ 모양 처마에 추녀마루를 그린다. */
|
|
drawEavesFirstLines.forEach((current) => {
|
|
// 확인용 라인, 포인트 제거
|
|
const { currentBaseLine, prevBaseLine, nextBaseLine } = current
|
|
const currentLine = currentBaseLine.line
|
|
const prevLine = prevBaseLine.line
|
|
const nextLine = nextBaseLine.line
|
|
let { x1, x2, y1, y2, size } = currentBaseLine
|
|
let beforePrevLine, afterNextLine
|
|
|
|
/** 이전 라인의 경사 */
|
|
const prevDegree = prevLine.attributes.pitch > 0 ? getDegreeByChon(prevLine.attributes.pitch) : prevLine.attributes.degree
|
|
/** 다음 라인의 경사 */
|
|
const currentDegree = currentLine.attributes.pitch > 0 ? getDegreeByChon(currentLine.attributes.pitch) : currentLine.attributes.degree
|
|
|
|
/** 이전 라인의 전라인, 다음 라인의 다음라인을 찾는다 */
|
|
drawBaseLines.forEach((line, index) => {
|
|
if (line === prevBaseLine) {
|
|
beforePrevLine = drawBaseLines[(index - 1 + drawBaseLines.length) % drawBaseLines.length]
|
|
}
|
|
if (line === nextBaseLine) {
|
|
afterNextLine = drawBaseLines[(index + 1) % drawBaseLines.length]
|
|
}
|
|
})
|
|
|
|
/** 이전, 다음라인의 사잇각의 vector를 구한다. */
|
|
let prevVector = getHalfAngleVector(prevLine, currentLine)
|
|
let nextVector = getHalfAngleVector(currentLine, nextLine)
|
|
|
|
let prevHipVector = { x: Big(prevVector.x), y: Big(prevVector.y) }
|
|
let nextHipVector = { x: Big(nextVector.x), y: Big(nextVector.y) }
|
|
|
|
/** 각 라인의 흐름 방향을 확인한다. */
|
|
const currentAngle = calculateAngle(currentLine.startPoint, currentLine.endPoint)
|
|
const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint)
|
|
const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint)
|
|
const beforePrevAngle = calculateAngle(beforePrevLine.line.startPoint, beforePrevLine.line.endPoint)
|
|
const afterNextAngle = calculateAngle(afterNextLine.line.startPoint, afterNextLine.line.endPoint)
|
|
|
|
/** 현재 라인의 길이 추녀마루 길이의 기준이 된다. */
|
|
let currentSize = Big(size).div(10)
|
|
|
|
/** 이전 라인과의 사이 추녀마루의 각도를 확인한다, 각도가 지붕안쪽으로 향하지 않을때 반대로 처리한다.*/
|
|
const prevCheckPoint = {
|
|
x: Big(x1).plus(Big(prevHipVector.x).times(10)),
|
|
y: Big(y1).plus(Big(prevHipVector.y).times(10)),
|
|
}
|
|
if (!checkWallPolygon.inPolygon(prevCheckPoint)) {
|
|
prevHipVector = { x: Big(prevHipVector.x).neg().toNumber(), y: Big(prevHipVector.y).neg().toNumber() }
|
|
}
|
|
|
|
/** 다음 라인과의 사이 추녀마루의 각도를 확인한다, 각도가 지붕안쪽으로 향하지 않을때 반대로 처리한다.*/
|
|
const nextCheckPoint = {
|
|
x: Big(x2).plus(Big(nextHipVector.x).times(10)),
|
|
y: Big(y2).plus(Big(nextHipVector.y).times(10)),
|
|
}
|
|
if (!checkWallPolygon.inPolygon(nextCheckPoint)) {
|
|
nextHipVector = { x: Big(nextHipVector.x).neg().toNumber(), y: Big(nextHipVector.y).neg().toNumber() }
|
|
}
|
|
|
|
/** 현재 라인의 길이를 기준으로 추녀 마루의 길이를 삼각함수를 사용하여 판단한다.*/
|
|
let hipLength = currentSize.div(2).pow(2).plus(currentSize.div(2).pow(2)).sqrt()
|
|
|
|
/**
|
|
* 현재 라인에서 2번째 전 라인과 2번째 후 라인의 각도가 같을때 -_- 와 같은 형태로 판단하고
|
|
* 맞은 편 외벽선까지의 거리를 확인후 currentSize 를 조정한다.
|
|
*/
|
|
if (beforePrevLine === afterNextLine || (currentAngle === beforePrevAngle && currentAngle === afterNextAngle)) {
|
|
const xVector = Big(nextLine.x2).minus(Big(nextLine.x1))
|
|
const yVector = Big(nextLine.y2).minus(Big(nextLine.y1))
|
|
const currentMidX = Big(x1).plus(Big(x2)).div(2)
|
|
const currentMidY = Big(y1).plus(Big(y2)).div(2)
|
|
|
|
const midLineEdge = {
|
|
vertex1: { x: currentMidX.toNumber(), y: currentMidY.toNumber() },
|
|
vertex2: {
|
|
x: currentMidX.plus(currentSize.times(Math.sign(xVector))).toNumber(),
|
|
y: currentMidY.plus(currentSize.times(Math.sign(yVector))).toNumber(),
|
|
},
|
|
}
|
|
|
|
/** 현재 라인의 중심 지점에서 현재라인의 길이만큼 다음라인의 방향만큼 거리를 확인한다*/
|
|
baseLines
|
|
.filter((line) => line !== currentLine)
|
|
.forEach((line) => {
|
|
const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
|
|
const intersection = edgesIntersection(midLineEdge, lineEdge)
|
|
/** 현재라인의 길이만큼 거리가 모자라면 해당 길이 만큼을 현재라인의 길이로 판단하고 나머지 계산을 진행한다.*/
|
|
if (intersection && !intersection.isIntersectionOutside) {
|
|
const intersectionSize = Big(intersection.x)
|
|
.minus(Big(currentMidX))
|
|
.plus(Big(intersection.y).minus(Big(currentMidY)))
|
|
if (intersectionSize.lt(currentSize)) {
|
|
currentSize = intersectionSize
|
|
}
|
|
}
|
|
})
|
|
|
|
hipLength = currentSize.div(2).pow(2).plus(currentSize.div(2).pow(2)).sqrt()
|
|
} else {
|
|
if (currentAngle !== beforePrevAngle && currentAngle !== afterNextAngle && beforePrevLine !== afterNextLine) {
|
|
const beforePrevX1 = beforePrevLine.x1,
|
|
beforePrevY1 = beforePrevLine.y1,
|
|
beforePrevX2 = beforePrevLine.x2,
|
|
beforePrevY2 = beforePrevLine.y2
|
|
const afterNextX1 = afterNextLine.x1,
|
|
afterNextY1 = afterNextLine.y1,
|
|
afterNextX2 = afterNextLine.x2,
|
|
afterNextY2 = afterNextLine.y2
|
|
|
|
/** beforePrevLine 과 afterNextLine 을 연결하는 사각형의 경우 6각으로 판단 */
|
|
const connectBAPoint = { x1: afterNextX2, y1: afterNextY2, x2: beforePrevX1, y2: beforePrevY1 }
|
|
const isConnect =
|
|
baseLines.filter(
|
|
(line) =>
|
|
line.x1 === connectBAPoint.x1 && line.y1 === connectBAPoint.y1 && line.x2 === connectBAPoint.x2 && line.y2 === connectBAPoint.y2,
|
|
).length > 0
|
|
|
|
/** 6각 */
|
|
// console.log('isConnect :', isConnect)
|
|
if (isConnect) {
|
|
const checkScale = currentSize.pow(2).plus(currentSize.pow(2)).sqrt()
|
|
const intersectBaseLine = []
|
|
if (baseHipLines.filter((line) => line.x1 === x1 && line.y1 === y1).length === 0) {
|
|
const prevEndPoint = {
|
|
x: Big(x1).plus(Big(prevHipVector.x).times(checkScale)),
|
|
y: Big(y1).plus(Big(prevHipVector.y).times(checkScale)),
|
|
}
|
|
baseLines
|
|
.filter((line) => line !== currentLine && line !== prevLine && line !== nextLine)
|
|
.forEach((line) => {
|
|
const intersection = edgesIntersection(
|
|
{ vertex1: { x: x1, y: y1 }, vertex2: prevEndPoint },
|
|
{ vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } },
|
|
)
|
|
if (intersection && !intersection.isIntersectionOutside) {
|
|
intersectBaseLine.push({
|
|
intersection,
|
|
distance: Big(intersection.x)
|
|
.minus(Big(x1))
|
|
.pow(2)
|
|
.plus(Big(intersection.y).minus(Big(y1)).pow(2))
|
|
.sqrt(),
|
|
})
|
|
}
|
|
})
|
|
}
|
|
if (baseHipLines.filter((line) => line.x1 === x2 && line.y1 === y2).length === 0) {
|
|
const nextEndPoint = {
|
|
x: Big(x2).plus(Big(nextHipVector.x).times(checkScale)),
|
|
y: Big(y2).plus(Big(nextHipVector.y).times(checkScale)),
|
|
}
|
|
baseLines
|
|
.filter((line) => line !== currentLine && line !== prevLine && line !== nextLine)
|
|
.forEach((line) => {
|
|
const intersection = edgesIntersection(
|
|
{ vertex1: { x: x2, y: y2 }, vertex2: nextEndPoint },
|
|
{ vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } },
|
|
)
|
|
if (intersection && !intersection.isIntersectionOutside) {
|
|
intersectBaseLine.push({
|
|
intersection,
|
|
distance: Big(intersection.x)
|
|
.minus(Big(x2))
|
|
.pow(2)
|
|
.plus(Big(intersection.y).minus(Big(y2)).pow(2))
|
|
.sqrt(),
|
|
})
|
|
}
|
|
})
|
|
}
|
|
const intersection = intersectBaseLine.reduce((prev, current) => (prev.distance < current.distance ? prev : current), intersectBaseLine[0])
|
|
if (intersection) {
|
|
hipLength = intersection.distance
|
|
}
|
|
} else {
|
|
const rightAngleLine = baseLines
|
|
.filter(
|
|
(line) =>
|
|
line !== prevLine &&
|
|
line !== nextLine &&
|
|
(prevAngle === calculateAngle(line.startPoint, line.endPoint) || nextAngle === calculateAngle(line.startPoint, line.endPoint)),
|
|
)
|
|
.filter((line) => {
|
|
const index = baseLines.indexOf(line)
|
|
const prevLine = baseLines[(index - 1 + baseLines.length) % baseLines.length]
|
|
const nextLine = baseLines[(index + 1) % baseLines.length]
|
|
const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint)
|
|
const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint)
|
|
switch (prevAngle) {
|
|
case 90:
|
|
return nextAngle === -90
|
|
case -90:
|
|
return nextAngle === 90
|
|
case 0:
|
|
return nextAngle === 180
|
|
case 180:
|
|
return nextAngle === 0
|
|
}
|
|
})
|
|
|
|
const oppositeCurrentLine = baseLines
|
|
.filter((line) => {
|
|
const angle = calculateAngle(line.startPoint, line.endPoint)
|
|
switch (currentAngle) {
|
|
case 90:
|
|
return angle === -90
|
|
case -90:
|
|
return angle === 90
|
|
case 0:
|
|
return angle === 180
|
|
case 180:
|
|
return angle === 0
|
|
}
|
|
})
|
|
.filter((line) => {
|
|
const index = baseLines.indexOf(line)
|
|
const prevLine = baseLines[(index - 1 + baseLines.length) % baseLines.length]
|
|
const nextLine = baseLines[(index + 1) % baseLines.length]
|
|
const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint)
|
|
const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint)
|
|
switch (prevAngle) {
|
|
case 90:
|
|
return nextAngle === -90
|
|
case -90:
|
|
return nextAngle === 90
|
|
case 0:
|
|
return nextAngle === 180
|
|
case 180:
|
|
return nextAngle === 0
|
|
}
|
|
})
|
|
|
|
let checkHipPoints
|
|
if (baseHipLines.filter((line) => line.x1 === x1 && line.y1 === y1).length === 0) {
|
|
checkHipPoints = [
|
|
x1,
|
|
y1,
|
|
Big(x1).plus(Big(prevHipVector.x).times(hipLength)).round(2),
|
|
Big(y1).plus(Big(prevHipVector.y).times(hipLength)).round(2),
|
|
]
|
|
}
|
|
|
|
if (baseHipLines.filter((line) => line.x1 === x2 && line.y1 === y2).length === 0) {
|
|
checkHipPoints = [
|
|
x2,
|
|
y2,
|
|
Big(x2).plus(Big(nextHipVector.x).times(hipLength)).round(2),
|
|
Big(y2).plus(Big(nextHipVector.y).times(hipLength)).round(2),
|
|
]
|
|
}
|
|
|
|
if (checkHipPoints) {
|
|
const intersectPoints = []
|
|
rightAngleLine.forEach((line) => {
|
|
const intersection = edgesIntersection(
|
|
{
|
|
vertex1: { x: checkHipPoints[0], y: checkHipPoints[1] },
|
|
vertex2: { x: checkHipPoints[2], y: checkHipPoints[3] },
|
|
},
|
|
{ vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } },
|
|
)
|
|
if (intersection) {
|
|
intersectPoints.push({
|
|
intersection,
|
|
distance: Big(intersection.x)
|
|
.minus(Big(checkHipPoints[0]))
|
|
.pow(2)
|
|
.plus(Big(intersection.y).minus(Big(checkHipPoints[1])).pow(2))
|
|
.sqrt(),
|
|
})
|
|
}
|
|
})
|
|
|
|
oppositeCurrentLine.forEach((line) => {
|
|
const intersection = edgesIntersection(
|
|
{
|
|
vertex1: { x: checkHipPoints[0], y: checkHipPoints[1] },
|
|
vertex2: { x: checkHipPoints[2], y: checkHipPoints[3] },
|
|
},
|
|
{ vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } },
|
|
)
|
|
if (intersection) {
|
|
intersectPoints.push({
|
|
intersection,
|
|
distance: Big(intersection.x)
|
|
.minus(Big(checkHipPoints[0]))
|
|
.pow(2)
|
|
.plus(Big(intersection.y).minus(Big(checkHipPoints[1])).pow(2))
|
|
.sqrt(),
|
|
})
|
|
}
|
|
})
|
|
|
|
const intersection = intersectPoints.reduce((prev, current) => (prev.distance.lt(current.distance) ? prev : current), intersectPoints[0])
|
|
if (intersection) {
|
|
hipLength = intersection.distance.div(2)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
let prevHipLine, nextHipLine
|
|
/** 이전라인과의 연결지점에 추녀마루를 그린다. */
|
|
if (baseHipLines.filter((line) => line.x1 === x1 && line.y1 === y1).length === 0) {
|
|
const prevEndPoint = {
|
|
x: Big(x1).plus(Big(prevHipVector.x).times(hipLength)).round(1),
|
|
y: Big(y1).plus(Big(prevHipVector.y).times(hipLength)).round(1),
|
|
}
|
|
|
|
const intersectRidgeLine = []
|
|
baseRidgeLines.forEach((line) => {
|
|
const intersection = edgesIntersection(
|
|
{ vertex1: { x: x1, y: y1 }, vertex2: prevEndPoint },
|
|
{ vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } },
|
|
)
|
|
if (intersection && !intersection.isIntersectionOutside) {
|
|
intersectRidgeLine.push({
|
|
intersection,
|
|
distance: Big(intersection.x)
|
|
.minus(Big(x1))
|
|
.pow(2)
|
|
.plus(Big(intersection.y).minus(Big(y1)).pow(2))
|
|
.sqrt(),
|
|
})
|
|
}
|
|
})
|
|
const intersectRidge = intersectRidgeLine.reduce((prev, current) => (prev.distance < current.distance ? prev : current), intersectRidgeLine[0])
|
|
|
|
if (intersectRidge) {
|
|
prevEndPoint.x = Big(intersectRidge.intersection.x).round(1)
|
|
prevEndPoint.y = Big(intersectRidge.intersection.y).round(1)
|
|
}
|
|
|
|
const xVector = Math.sign(Big(prevEndPoint.x).minus(Big(x1)).neg().toNumber())
|
|
const yVector = Math.sign(Big(prevEndPoint.y).minus(Big(y1)).neg().toNumber())
|
|
/** 지붕 선까지의 곂침에 따른 길이를 파악하기 위한 scale*/
|
|
let scale = Big(currentLine.attributes.offset).pow(2).plus(Big(prevLine.attributes.offset).pow(2)).sqrt()
|
|
scale = scale.eq(0) ? Big(1) : scale
|
|
|
|
/** scale 만큼 추녀마루를 늘려서 겹치는 포인트를 확인한다. */
|
|
const hipEdge = {
|
|
vertex1: { x: Big(x1).plus(scale.times(xVector)).toNumber(), y: Big(y1).plus(scale.times(yVector)).toNumber() },
|
|
vertex2: prevEndPoint,
|
|
}
|
|
|
|
let intersectPoints = []
|
|
/** 외벽선에서 추녀 마루가 겹치는 경우에 대한 확인*/
|
|
roof.lines.forEach((line) => {
|
|
const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
|
|
const intersection = edgesIntersection(hipEdge, lineEdge)
|
|
if (
|
|
intersection &&
|
|
!intersection.isIntersectionOutside &&
|
|
Math.sign(prevEndPoint.x - x1) === Math.sign(prevEndPoint.x - intersection.x) &&
|
|
Math.sign(prevEndPoint.y - y1) === Math.sign(prevEndPoint.y - intersection.y)
|
|
) {
|
|
const intersectSize = prevEndPoint.x
|
|
.minus(Big(intersection.x))
|
|
.pow(2)
|
|
.plus(prevEndPoint.y.minus(Big(intersection.y)).pow(2))
|
|
.abs()
|
|
.sqrt()
|
|
.toNumber()
|
|
|
|
intersectPoints.push({
|
|
intersection,
|
|
size: intersectSize,
|
|
})
|
|
}
|
|
})
|
|
|
|
intersectPoints = intersectPoints.reduce((prev, current) => {
|
|
return prev.size < current.size ? prev : current
|
|
}, intersectPoints[0])
|
|
|
|
/** 겹치는 외벽선이 있을때 추녀마루를 외벽선 까지 늘려서 수정*/
|
|
if (intersectPoints && intersectPoints.intersection) {
|
|
prevHipLine = drawHipLine(
|
|
[intersectPoints.intersection.x, intersectPoints.intersection.y, prevEndPoint.x.toNumber(), prevEndPoint.y.toNumber()],
|
|
canvas,
|
|
roof,
|
|
textMode,
|
|
null,
|
|
prevDegree,
|
|
currentDegree,
|
|
)
|
|
baseHipLines.push({ x1, y1, x2: prevEndPoint.x.toNumber(), y2: prevEndPoint.y.toNumber(), line: prevHipLine })
|
|
}
|
|
}
|
|
if (baseHipLines.filter((line) => line.x1 === x2 && line.y1 === y2).length === 0) {
|
|
const nextEndPoint = {
|
|
x: Big(x2).plus(Big(nextHipVector.x).times(hipLength)).round(1),
|
|
y: Big(y2).plus(Big(nextHipVector.y).times(hipLength)).round(1),
|
|
}
|
|
|
|
const intersectRidgeLine = []
|
|
baseRidgeLines.forEach((line) => {
|
|
const intersection = edgesIntersection(
|
|
{ vertex1: { x: x2, y: y2 }, vertex2: nextEndPoint },
|
|
{ vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } },
|
|
)
|
|
|
|
if (intersection && !intersection.isIntersectionOutside) {
|
|
intersectRidgeLine.push({
|
|
intersection,
|
|
distance: Big(intersection.x)
|
|
.minus(Big(x1))
|
|
.pow(2)
|
|
.plus(Big(intersection.y).minus(Big(y1)).pow(2))
|
|
.sqrt(),
|
|
})
|
|
}
|
|
})
|
|
const intersectRidge = intersectRidgeLine.reduce((prev, current) => (prev.distance < current.distance ? prev : current), intersectRidgeLine[0])
|
|
|
|
if (intersectRidge) {
|
|
nextEndPoint.x = Big(intersectRidge.intersection.x).round(1)
|
|
nextEndPoint.y = Big(intersectRidge.intersection.y).round(1)
|
|
}
|
|
|
|
const xVector = Math.sign(Big(nextEndPoint.x).minus(Big(x2)).neg().toNumber())
|
|
const yVector = Math.sign(Big(nextEndPoint.y).minus(Big(y2)).neg().toNumber())
|
|
let scale = Big(currentLine.attributes.offset).pow(2).plus(Big(nextLine.attributes.offset).pow(2)).sqrt()
|
|
scale = scale.eq(0) ? Big(1) : scale
|
|
|
|
const hipEdge = {
|
|
vertex1: { x: Big(x2).plus(scale.times(xVector)).toNumber(), y: Big(y2).plus(scale.times(yVector)).toNumber() },
|
|
vertex2: nextEndPoint,
|
|
}
|
|
|
|
let intersectPoints = []
|
|
roof.lines.forEach((line) => {
|
|
const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
|
|
const intersection = edgesIntersection(hipEdge, lineEdge)
|
|
if (
|
|
intersection &&
|
|
!intersection.isIntersectionOutside &&
|
|
Math.sign(nextEndPoint.x - x2) === Math.sign(nextEndPoint.x - intersection.x) &&
|
|
Math.sign(nextEndPoint.y - y2) === Math.sign(nextEndPoint.y - intersection.y)
|
|
) {
|
|
const intersectSize = nextEndPoint.x
|
|
.minus(Big(intersection.x))
|
|
.pow(2)
|
|
.plus(nextEndPoint.y.minus(Big(intersection.y)).pow(2))
|
|
.abs()
|
|
.sqrt()
|
|
.toNumber()
|
|
|
|
intersectPoints.push({
|
|
intersection,
|
|
size: intersectSize,
|
|
})
|
|
}
|
|
})
|
|
|
|
intersectPoints = intersectPoints.reduce((prev, current) => {
|
|
return prev.size < current.size ? prev : current
|
|
}, intersectPoints[0])
|
|
|
|
if (intersectPoints && intersectPoints.intersection) {
|
|
nextHipLine = drawHipLine(
|
|
[intersectPoints.intersection.x, intersectPoints.intersection.y, nextEndPoint.x.toNumber(), nextEndPoint.y.toNumber()],
|
|
canvas,
|
|
roof,
|
|
textMode,
|
|
null,
|
|
prevDegree,
|
|
currentDegree,
|
|
)
|
|
baseHipLines.push({
|
|
x1: x2,
|
|
y1: y2,
|
|
x2: nextEndPoint.x.toNumber(),
|
|
y2: nextEndPoint.y.toNumber(),
|
|
line: nextHipLine,
|
|
})
|
|
}
|
|
}
|
|
|
|
/** 두 선이 교차하면 해당 포인트까지로 선 조정*/
|
|
if (prevHipLine !== undefined && nextHipLine !== undefined) {
|
|
const prevEdge = {
|
|
vertex1: { x: prevHipLine.x1, y: prevHipLine.y1 },
|
|
vertex2: { x: prevHipLine.x2, y: prevHipLine.y2 },
|
|
}
|
|
const nextEdge = {
|
|
vertex1: { x: nextHipLine.x1, y: nextHipLine.y1 },
|
|
vertex2: { x: nextHipLine.x2, y: nextHipLine.y2 },
|
|
}
|
|
const intersection = edgesIntersection(prevEdge, nextEdge)
|
|
if (intersection) {
|
|
/** 포인트 조정*/
|
|
baseHipLines
|
|
.filter((line) => line.line === prevHipLine || line.line === nextHipLine)
|
|
.forEach((line) => {
|
|
line.x2 = intersection.x
|
|
line.y2 = intersection.y
|
|
line.line.set({ x2: intersection.x, y2: intersection.y })
|
|
})
|
|
prevHipLine.x2 = intersection.x
|
|
prevHipLine.y2 = intersection.y
|
|
const prevSize = reCalculateSize(prevHipLine)
|
|
|
|
prevHipLine.attributes.planeSize = prevSize.planeSize
|
|
prevHipLine.attributes.actualSize = prevSize.actualSize
|
|
prevHipLine.fire('modified')
|
|
|
|
nextHipLine.x2 = intersection.x
|
|
nextHipLine.y2 = intersection.y
|
|
const nextSize = reCalculateSize(nextHipLine)
|
|
|
|
nextHipLine.attributes.planeSize = nextSize.planeSize
|
|
nextHipLine.attributes.actualSize = nextSize.actualSize
|
|
nextHipLine.fire('modified')
|
|
canvas.renderAll()
|
|
}
|
|
}
|
|
|
|
/** 두 추녀마루가 한점에서 만나는 경우 해당 점을 기점으로 마루를 작성한다.*/
|
|
if (
|
|
prevHipLine !== undefined &&
|
|
nextHipLine !== undefined &&
|
|
Big(prevHipLine.x2).minus(Big(nextHipLine.x2)).abs().lte(1) &&
|
|
Big(prevHipLine.y2).minus(Big(nextHipLine.y2)).abs().lte(1)
|
|
) {
|
|
const startPoint = { x: prevHipLine.x2, y: prevHipLine.y2 }
|
|
let ridgeSize = 0
|
|
|
|
const currentMidX = Big(currentLine.x2).plus(Big(currentLine.x1)).div(2)
|
|
const currentMidY = Big(currentLine.y2).plus(Big(currentLine.y1)).div(2)
|
|
|
|
const xVector = Big(currentMidX).minus(Big(startPoint.x)).round(0, Big.roundDown)
|
|
const yVector = Big(currentMidY).minus(Big(startPoint.y)).round(0, Big.roundDown)
|
|
|
|
if (gableType.includes(beforePrevLine.line.attributes.type) || gableType.includes(afterNextLine.line.attributes.type)) {
|
|
const oppositeLine = gableType.includes(beforePrevLine.line.attributes.type) ? beforePrevLine.line : afterNextLine.line
|
|
const oppositeAngle = calculateAngle(oppositeLine.startPoint, oppositeLine.endPoint)
|
|
let checkEdge
|
|
if (Math.sign(oppositeLine.x1 - oppositeLine.x2) === 0) {
|
|
checkEdge = { vertex1: startPoint, vertex2: { x: oppositeLine.x1, y: startPoint.y } }
|
|
} else {
|
|
checkEdge = { vertex1: startPoint, vertex2: { x: startPoint.x, y: oppositeLine.y1 } }
|
|
}
|
|
if (currentAngle === oppositeAngle) {
|
|
const oppositeEdge = {
|
|
vertex1: { x: oppositeLine.x1, y: oppositeLine.y1 },
|
|
vertex2: { x: oppositeLine.x2, y: oppositeLine.y2 },
|
|
}
|
|
const intersection = edgesIntersection(oppositeEdge, checkEdge)
|
|
if (intersection) {
|
|
ridgeSize = Big(intersection.x)
|
|
.minus(Big(startPoint.x))
|
|
.pow(2)
|
|
.plus(Big(intersection.y).minus(Big(startPoint.y)).pow(2))
|
|
.sqrt()
|
|
}
|
|
} else {
|
|
const intersectPoints = []
|
|
roof.lines
|
|
.filter(
|
|
(line) =>
|
|
Math.sign(oppositeLine.x1 - oppositeLine.x2) === Math.sign(line.x1 - line.x2) &&
|
|
Math.sign(oppositeLine.y1 - oppositeLine.y2) === Math.sign(line.y1 - line.y2),
|
|
)
|
|
.forEach((line) => {
|
|
const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
|
|
const intersection = edgesIntersection(lineEdge, checkEdge)
|
|
if (intersection) {
|
|
const size = Big(startPoint.x)
|
|
.minus(Big(intersection.x))
|
|
.pow(2)
|
|
.plus(Big(startPoint.y).minus(Big(intersection.y)).pow(2))
|
|
.sqrt()
|
|
.toNumber()
|
|
intersectPoints.push({ intersection, size })
|
|
}
|
|
})
|
|
intersectPoints.sort((a, b) => a.size - b.size)
|
|
if (intersectPoints.length > 0) {
|
|
ridgeSize = Big(intersectPoints[0].size)
|
|
}
|
|
}
|
|
} else {
|
|
/** baseLines에서 가장 작은 x1과 가장 큰 x1, 가장 작은 y1과 가장 큰 y1을 계산*/
|
|
let minX = Infinity
|
|
let maxX = -Infinity
|
|
let minY = Infinity
|
|
let maxY = -Infinity
|
|
|
|
baseLines.forEach((line) => {
|
|
if (line.x1 < minX) {
|
|
minX = line.x1
|
|
}
|
|
if (line.x1 > maxX) {
|
|
maxX = line.x1
|
|
}
|
|
if (line.y1 < minY) {
|
|
minY = line.y1
|
|
}
|
|
if (line.y1 > maxY) {
|
|
maxY = line.y1
|
|
}
|
|
})
|
|
const checkLength = Big(maxX)
|
|
.minus(Big(minX))
|
|
.pow(2)
|
|
.plus(Big(maxY).minus(Big(minY)).pow(2))
|
|
.sqrt()
|
|
|
|
const checkEdges = {
|
|
vertex1: { x: startPoint.x, y: startPoint.y },
|
|
vertex2: {
|
|
x: Big(startPoint.x).minus(checkLength.times(Math.sign(xVector))),
|
|
y: Big(startPoint.y).minus(checkLength.times(Math.sign(yVector))),
|
|
},
|
|
}
|
|
|
|
/** 맞은편 벽 까지의 길이 판단을 위한 교차되는 line*/
|
|
const intersectBaseLine = []
|
|
baseLines
|
|
.filter((line) => {
|
|
/** currentAngle 의 반대 각도인 라인 */
|
|
const angle = calculateAngle(line.startPoint, line.endPoint)
|
|
switch (currentAngle) {
|
|
case 90:
|
|
return angle === -90
|
|
case -90:
|
|
return angle === 90
|
|
case 0:
|
|
return angle === 180
|
|
case 180:
|
|
return angle === 0
|
|
}
|
|
})
|
|
.filter((line) => {
|
|
const currentMinX = Math.min(x1, x2)
|
|
const currentMaxX = Math.max(x1, x2)
|
|
const currentMinY = Math.min(y1, y2)
|
|
const currentMaxY = Math.max(y1, y2)
|
|
const lineMinX = Math.min(line.x1, line.x2)
|
|
const lineMaxX = Math.max(line.x1, line.x2)
|
|
const lineMinY = Math.min(line.y1, line.y2)
|
|
const lineMaxY = Math.max(line.y1, line.y2)
|
|
|
|
/** currentLine 의 안쪽에 있거나 currentLine이 line의 안쪽에 있는 라인 */
|
|
if (Big(currentLine.y1).minus(Big(currentLine.y2)).abs().lte(1)) {
|
|
return (
|
|
(currentMinX <= lineMinX && lineMinX <= currentMaxX) ||
|
|
(currentMinX <= lineMaxX && lineMaxX <= currentMaxX) ||
|
|
(lineMinX <= currentMinX && currentMinX <= lineMaxX) ||
|
|
(lineMinX <= currentMaxX && currentMaxX <= lineMaxX)
|
|
)
|
|
} else {
|
|
return (
|
|
(currentMinY <= lineMinY && lineMinY <= currentMaxY) ||
|
|
(currentMinY <= lineMaxY && lineMaxY <= currentMaxY) ||
|
|
(lineMinY <= currentMinY && currentMinY <= lineMaxY) ||
|
|
(lineMinY <= currentMaxY && currentMaxY <= lineMaxY)
|
|
)
|
|
}
|
|
})
|
|
.forEach((line, index) => {
|
|
const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
|
|
const intersection = edgesIntersection(checkEdges, lineEdge)
|
|
if (intersection) {
|
|
intersectBaseLine.push({ intersection, line })
|
|
}
|
|
})
|
|
|
|
/** 맞은편 라인 */
|
|
const oppositeLine = intersectBaseLine.reduce((prev, current) => {
|
|
const prevDistance = Big(prev.intersection.x)
|
|
.minus(Big(startPoint.x))
|
|
.pow(2)
|
|
.plus(Big(prev.intersection.y).minus(Big(startPoint.y)).pow(2))
|
|
.sqrt()
|
|
const currentDistance = Big(current.intersection.x)
|
|
.minus(Big(startPoint.x))
|
|
.pow(2)
|
|
.plus(Big(current.intersection.y).minus(Big(startPoint.y)).pow(2))
|
|
.sqrt()
|
|
return prevDistance < currentDistance ? prev : current
|
|
}, intersectBaseLine[0])
|
|
|
|
/** 맞은편 라인까지의 길이 = 전체 길이 - 현재라인의 길이 */
|
|
const oppositeSize = oppositeLine
|
|
? Big(oppositeLine.intersection.x)
|
|
.minus(Big(startPoint.x))
|
|
.pow(2)
|
|
.plus(Big(oppositeLine.intersection.y).minus(Big(startPoint.y)).pow(2))
|
|
.sqrt()
|
|
.minus(currentSize.div(2))
|
|
.round(1)
|
|
: Infinity
|
|
|
|
/** 이전, 다음 라인중 길이가 짧은 길이*/
|
|
const lineMinSize = prevBaseLine.size < nextBaseLine.size ? Big(prevBaseLine.size).div(10) : Big(nextBaseLine.size).div(10)
|
|
|
|
/** 마루의 길이는 이전 다음 라인중 짧은것의 길이와 현재라인부터 맞은편 라인까지의 길이에서 현재 라인의 길이를 뺀 것중 짧은 길이 */
|
|
ridgeSize = Big(Math.min(oppositeSize, lineMinSize))
|
|
}
|
|
|
|
if (ridgeSize.gt(0) && baseRidgeCount < getMaxRidge(baseLines.length)) {
|
|
const points = [
|
|
startPoint.x,
|
|
startPoint.y,
|
|
Big(startPoint.x).minus(ridgeSize.times(Math.sign(xVector))),
|
|
Big(startPoint.y).minus(ridgeSize.times(Math.sign(yVector))),
|
|
]
|
|
|
|
/** 동일 라인이 있는지 확인. */
|
|
if (
|
|
baseRidgeLines.filter((line) => {
|
|
const ridgeMinX = Math.min(points[0], points[2])
|
|
const ridgeMaxX = Math.max(points[0], points[2])
|
|
const ridgeMinY = Math.min(points[1], points[3])
|
|
const ridgeMaxY = Math.max(points[1], points[3])
|
|
const lineMinX = Math.min(line.x1, line.x2)
|
|
const lineMaxX = Math.max(line.x1, line.x2)
|
|
const lineMinY = Math.min(line.y1, line.y2)
|
|
const lineMaxY = Math.max(line.y1, line.y2)
|
|
|
|
return ridgeMinX === lineMinX && ridgeMaxX === lineMaxX && ridgeMinY === lineMinY && ridgeMaxY === lineMaxY
|
|
}).length > 0
|
|
) {
|
|
return
|
|
}
|
|
|
|
const ridgeLine = drawRidgeLine(points, canvas, roof, textMode)
|
|
if (gableType.includes(beforePrevLine.line.attributes.type) || gableType.includes(afterNextLine.line.attributes.type)) {
|
|
baseGableRidgeLines.push(ridgeLine)
|
|
} else {
|
|
baseRidgeLines.push(ridgeLine)
|
|
}
|
|
baseRidgeCount = baseRidgeCount + 1
|
|
|
|
/** 포인트 조정*/
|
|
baseHipLines
|
|
.filter((line) => line.line === prevHipLine || line.line === nextHipLine)
|
|
.forEach((line) => {
|
|
line.x2 = points[0]
|
|
line.y2 = points[1]
|
|
line.line.set({ x2: points[0], y2: points[1] })
|
|
// line.line.fire('modified')
|
|
})
|
|
prevHipLine.x2 = points[0]
|
|
prevHipLine.y2 = points[1]
|
|
const prevSize = reCalculateSize(prevHipLine)
|
|
prevHipLine.attributes.planeSize = prevSize.planeSize
|
|
prevHipLine.attributes.actualSize = prevSize.actualSize
|
|
prevHipLine.fire('modified')
|
|
nextHipLine.x2 = points[0]
|
|
nextHipLine.y2 = points[1]
|
|
const nextSize = reCalculateSize(nextHipLine)
|
|
nextHipLine.attributes.planeSize = nextSize.planeSize
|
|
nextHipLine.attributes.actualSize = nextSize.actualSize
|
|
nextHipLine.fire('modified')
|
|
canvas.renderAll()
|
|
|
|
if (gableType.includes(beforePrevLine.line.attributes.type) || gableType.includes(afterNextLine.line.attributes.type)) {
|
|
const oppositeLine = gableType.includes(beforePrevLine.line.attributes.type) ? beforePrevLine.line : afterNextLine.line
|
|
const oppositeAngle = calculateAngle(oppositeLine.startPoint, oppositeLine.endPoint)
|
|
if (Math.sign(ridgeLine.x1 - ridgeLine.x2) === 0) {
|
|
const gableVector = Math.sign(ridgeLine.x1 - oppositeLine.x1)
|
|
const prevVector = ridgeLine.x1 === prevHipLine.x1 ? Math.sign(ridgeLine.x1 - prevHipLine.x2) : Math.sign(ridgeLine.x2 - prevHipLine.x1)
|
|
const nextVector = ridgeLine.x1 === nextHipLine.x1 ? Math.sign(ridgeLine.x1 - nextHipLine.x2) : Math.sign(ridgeLine.x2 - nextHipLine.x1)
|
|
|
|
const firstHipLine = gableVector === prevVector ? prevHipLine : nextHipLine
|
|
const firstDegree =
|
|
gableVector === Math.sign(ridgeLine.x1 - prevLine.x1)
|
|
? prevLine.attributes.pitch > 0
|
|
? getDegreeByChon(prevLine.attributes.pitch)
|
|
: prevLine.attributes.degree
|
|
: nextLine.attributes.pitch > 0
|
|
? getDegreeByChon(nextLine.attributes.pitch)
|
|
: nextLine.attributes.degree
|
|
|
|
const oppositeRoofPoints = [
|
|
ridgeLine.x2,
|
|
ridgeLine.y2,
|
|
ridgeLine.x1 === firstHipLine.x1 ? firstHipLine.x2 : firstHipLine.x1,
|
|
ridgeLine.y2,
|
|
]
|
|
const oppositeRoofLine = drawHipLine(oppositeRoofPoints, canvas, roof, textMode, null, firstDegree, firstDegree)
|
|
baseHipLines.push({
|
|
x1: oppositeRoofLine.x1,
|
|
y1: oppositeRoofLine.y1,
|
|
x2: oppositeRoofLine.x2,
|
|
y2: oppositeRoofLine.y2,
|
|
line: oppositeRoofLine,
|
|
})
|
|
|
|
const connectRoofPoints = [oppositeRoofLine.x2, oppositeRoofLine.y2]
|
|
if (ridgeLine.x1 === firstHipLine.x1) {
|
|
connectRoofPoints.push(firstHipLine.x2, firstHipLine.y2)
|
|
} else {
|
|
connectRoofPoints.push(firstHipLine.x1, firstHipLine.y1)
|
|
}
|
|
const connectRoofLine = drawRoofLine(connectRoofPoints, canvas, roof, textMode)
|
|
baseHipLines.push({
|
|
x1: connectRoofLine.x1,
|
|
y1: connectRoofLine.y1,
|
|
x2: connectRoofLine.x2,
|
|
y2: connectRoofLine.y2,
|
|
line: connectRoofLine,
|
|
})
|
|
|
|
/** 다른 방향의 추녀마루 */
|
|
const secondHipLine = gableVector === prevVector ? nextHipLine : prevHipLine
|
|
const secondDegree =
|
|
gableVector === Math.sign(ridgeLine.x1 - prevLine.x1)
|
|
? nextLine.attributes.pitch > 0
|
|
? getDegreeByChon(nextLine.attributes.pitch)
|
|
: nextLine.attributes.degree
|
|
: prevLine.attributes.pitch > 0
|
|
? getDegreeByChon(prevLine.attributes.pitch)
|
|
: prevLine.attributes.degree
|
|
|
|
const intersections = []
|
|
const checkEdge = {
|
|
vertex1: { x: ridgeLine.x2, y: ridgeLine.y2 },
|
|
vertex2: { x: ridgeLine.x1 === secondHipLine.x1 ? secondHipLine.x2 : secondHipLine.x1, y: ridgeLine.y2 },
|
|
}
|
|
|
|
baseGableRidgeLines
|
|
.filter((ridge) => ridge !== ridgeLine)
|
|
.forEach((ridge) => {
|
|
const ridgeEdge = { vertex1: { x: ridge.x1, y: ridge.y1 }, vertex2: { x: ridge.x2, y: ridge.y2 } }
|
|
const intersection = edgesIntersection(ridgeEdge, checkEdge)
|
|
if (intersection && !intersections.includes(intersection)) {
|
|
const size = Big(intersection.x)
|
|
.minus(Big(ridgeLine.x2))
|
|
.pow(2)
|
|
.plus(Big(intersection.y).minus(Big(ridgeLine.y2)).pow(2))
|
|
.sqrt()
|
|
.toNumber()
|
|
intersections.push({ intersection, size, ridge })
|
|
}
|
|
})
|
|
intersections.sort((a, b) => a.size - b.size)
|
|
|
|
if (intersections.length > 0) {
|
|
const intersection = intersections[0].intersection
|
|
const intersectRidge = intersections[0].ridge
|
|
const oppositeRoofPoints = [ridgeLine.x2, ridgeLine.y2, intersection.x, intersection.y]
|
|
const oppositeRoofLine = drawHipLine(oppositeRoofPoints, canvas, roof, textMode, null, secondDegree, secondDegree)
|
|
baseHipLines.push({
|
|
x1: oppositeLine.x1,
|
|
y1: oppositeLine.y1,
|
|
x2: oppositeLine.x2,
|
|
y2: oppositeLine.y2,
|
|
line: oppositeRoofLine,
|
|
})
|
|
|
|
const ridgeVector = Math.sign(ridgeLine.y1 - ridgeLine.y2)
|
|
const connectRoofPoints = [oppositeRoofLine.x2, oppositeRoofLine.y2]
|
|
if (ridgeVector === Math.sign(oppositeRoofLine.y1 - intersectRidge.y2)) {
|
|
connectRoofPoints.push(intersectRidge.x2, intersectRidge.y2)
|
|
} else {
|
|
connectRoofPoints.push(intersectRidge.x1, intersectRidge.y1)
|
|
}
|
|
|
|
const connectRoofLine = drawRoofLine(connectRoofPoints, canvas, roof, textMode)
|
|
baseHipLines.push({
|
|
x1: connectRoofLine.x1,
|
|
y1: connectRoofLine.y1,
|
|
x2: connectRoofLine.x2,
|
|
y2: connectRoofLine.y2,
|
|
line: connectRoofLine,
|
|
})
|
|
}
|
|
} else {
|
|
const gableVector = Math.sign(ridgeLine.y1 - oppositeLine.y1)
|
|
const prevVector = ridgeLine.y1 === prevHipLine.y1 ? Math.sign(ridgeLine.y1 - prevHipLine.y2) : Math.sign(ridgeLine.y1 - prevHipLine.y1)
|
|
const nextVector = ridgeLine.y1 === nextHipLine.y1 ? Math.sign(ridgeLine.y1 - nextHipLine.y2) : Math.sign(ridgeLine.y1 - nextHipLine.y1)
|
|
|
|
/** 마루와 박공지붕을 연결하기위한 추녀마루 라인 */
|
|
const firstHipLine = gableVector === prevVector ? prevHipLine : nextHipLine
|
|
const firstDegree =
|
|
gableVector === Math.sign(ridgeLine.y1 - prevLine.y1)
|
|
? prevLine.attributes.pitch > 0
|
|
? getDegreeByChon(prevLine.attributes.pitch)
|
|
: prevLine.attributes.degree
|
|
: nextLine.attributes.pitch > 0
|
|
? getDegreeByChon(nextLine.attributes.pitch)
|
|
: nextLine.attributes.degree
|
|
|
|
const oppositeRoofPoints = [
|
|
ridgeLine.x2,
|
|
ridgeLine.y2,
|
|
ridgeLine.x2,
|
|
ridgeLine.y1 === firstHipLine.y1 ? firstHipLine.y2 : firstHipLine.y1,
|
|
]
|
|
const oppositeRoofLine = drawHipLine(oppositeRoofPoints, canvas, roof, textMode, null, firstDegree, firstDegree)
|
|
baseHipLines.push({
|
|
x1: oppositeRoofLine.x1,
|
|
y1: oppositeRoofLine.y1,
|
|
x2: oppositeRoofLine.x2,
|
|
y2: oppositeRoofLine.y2,
|
|
line: oppositeRoofLine,
|
|
})
|
|
|
|
const connectRoofPoints = [oppositeRoofLine.x2, oppositeRoofLine.y2]
|
|
if (ridgeLine.y1 === firstHipLine.y1) {
|
|
connectRoofPoints.push(firstHipLine.x2, firstHipLine.y2)
|
|
} else {
|
|
connectRoofPoints.push(firstHipLine.x1, firstHipLine.y1)
|
|
}
|
|
|
|
const connectRoofLine = drawRoofLine(connectRoofPoints, canvas, roof, textMode)
|
|
baseHipLines.push({
|
|
x1: connectRoofLine.x1,
|
|
y1: connectRoofLine.y1,
|
|
x2: connectRoofLine.x2,
|
|
y2: connectRoofLine.y2,
|
|
line: connectRoofLine,
|
|
})
|
|
|
|
/** 다른 방향의 추녀마루 */
|
|
const secondHipLine = gableVector === prevVector ? nextHipLine : prevHipLine
|
|
const secondDegree =
|
|
gableVector === Math.sign(ridgeLine.y1 - prevLine.y1)
|
|
? nextLine.attributes.pitch > 0
|
|
? getDegreeByChon(nextLine.attributes.pitch)
|
|
: nextLine.attributes.degree
|
|
: prevLine.attributes.pitch > 0
|
|
? getDegreeByChon(prevLine.attributes.pitch)
|
|
: prevLine.attributes.degree
|
|
|
|
const intersections = []
|
|
const checkEdge = {
|
|
vertex1: { x: ridgeLine.x2, y: ridgeLine.y2 },
|
|
vertex2: { x: ridgeLine.x2, y: ridgeLine.y1 === secondHipLine.y1 ? secondHipLine.y2 : secondHipLine.y1 },
|
|
}
|
|
baseGableRidgeLines
|
|
.filter((ridge) => ridge !== ridgeLine)
|
|
.forEach((ridge) => {
|
|
const ridgeEdge = { vertex1: { x: ridge.x1, y: ridge.y1 }, vertex2: { x: ridge.x2, y: ridge.y2 } }
|
|
const intersection = edgesIntersection(ridgeEdge, checkEdge)
|
|
if (intersection && !intersections.includes(intersection)) {
|
|
const size = Big(intersection.x)
|
|
.minus(Big(ridgeLine.x2))
|
|
.pow(2)
|
|
.plus(Big(intersection.y).minus(Big(ridgeLine.y2)).pow(2))
|
|
.sqrt()
|
|
.toNumber()
|
|
intersections.push({ intersection, size, ridge })
|
|
}
|
|
})
|
|
intersections.sort((a, b) => a.size - b.size)
|
|
if (intersections.length > 0) {
|
|
const intersection = intersections[0].intersection
|
|
const intersectRidge = intersections[0].ridge
|
|
const oppositeRoofPoints = [ridgeLine.x2, ridgeLine.y2, intersection.x, intersection.y]
|
|
const oppositeRoofLine = drawHipLine(oppositeRoofPoints, canvas, roof, textMode, null, secondDegree, secondDegree)
|
|
baseHipLines.push({
|
|
x1: oppositeLine.x1,
|
|
y1: oppositeLine.y1,
|
|
x2: oppositeLine.x2,
|
|
y2: oppositeLine.y2,
|
|
line: oppositeRoofLine,
|
|
})
|
|
|
|
const ridgeVector = Math.sign(ridgeLine.x1 - ridgeLine.x2)
|
|
const connectRoofPoints = [oppositeRoofLine.x2, oppositeRoofLine.y2]
|
|
if (ridgeVector === Math.sign(oppositeRoofLine.x1 - intersectRidge.x2)) {
|
|
connectRoofPoints.push(intersectRidge.x2, intersectRidge.y2)
|
|
} else {
|
|
connectRoofPoints.push(intersectRidge.x1, intersectRidge.y1)
|
|
}
|
|
|
|
const connectRoofLine = drawRoofLine(connectRoofPoints, canvas, roof, textMode)
|
|
baseHipLines.push({
|
|
x1: connectRoofLine.x1,
|
|
y1: connectRoofLine.y1,
|
|
x2: connectRoofLine.x2,
|
|
y2: connectRoofLine.y2,
|
|
line: connectRoofLine,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|
|
/** 중복제거 */
|
|
baseRidgeLines.forEach((ridge) => {
|
|
baseRidgeLines
|
|
.filter((ridge2) => ridge !== ridge2)
|
|
.forEach((ridge2) => {
|
|
let overlap = segmentsOverlap(ridge, ridge2)
|
|
if (overlap) {
|
|
roof.canvas.remove(ridge)
|
|
roof.canvas.remove(ridge2)
|
|
baseRidgeLines = baseRidgeLines.filter((r) => r !== ridge && r !== ridge2)
|
|
|
|
baseRidgeCount = baseRidgeCount - 2
|
|
|
|
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 = drawRidgeLine([x1, y1, x2, y2], canvas, roof, textMode)
|
|
baseRidgeLines.push(newRidge)
|
|
baseRidgeCount = baseRidgeCount + 1
|
|
}
|
|
})
|
|
})
|
|
|
|
/** ㄴ 모양 처마에 추녀마루를 그린다. */
|
|
drawEavesSecondLines.forEach((current) => {
|
|
const { currentBaseLine, prevBaseLine, nextBaseLine } = current
|
|
const currentLine = currentBaseLine.line
|
|
const prevLine = prevBaseLine.line
|
|
const nextLine = nextBaseLine.line
|
|
let { x1, x2, y1, y2, size } = currentBaseLine
|
|
|
|
/** 이전 라인의 경사 */
|
|
const prevDegree = prevLine.attributes.pitch > 0 ? getDegreeByChon(prevLine.attributes.pitch) : prevLine.attributes.degree
|
|
/** 다음 라인의 경사 */
|
|
const currentDegree = currentLine.attributes.pitch > 0 ? getDegreeByChon(currentLine.attributes.pitch) : currentLine.attributes.degree
|
|
|
|
/** 이전, 다음라인의 사잇각의 vector를 구한다. */
|
|
let prevVector = getHalfAngleVector(prevLine, currentLine)
|
|
let nextVector = getHalfAngleVector(currentLine, nextLine)
|
|
|
|
let prevHipVector = { x: Big(prevVector.x), y: Big(prevVector.y) }
|
|
let nextHipVector = { x: Big(nextVector.x), y: Big(nextVector.y) }
|
|
|
|
/** 각 라인의 흐름 방향을 확인한다. */
|
|
const currentAngle = calculateAngle(currentLine.startPoint, currentLine.endPoint)
|
|
|
|
/** 현재 라인의 길이 추녀마루 길이의 기준이 된다. */
|
|
let hipLength = Big(x2)
|
|
.minus(Big(x1))
|
|
.plus(Big(y2).minus(Big(y1)))
|
|
.abs()
|
|
|
|
/** 이전 라인과의 사이 추녀마루의 각도를 확인한다, 각도가 지붕안쪽으로 향하지 않을때 반대로 처리한다.*/
|
|
const prevCheckPoint = {
|
|
x: Big(x1).plus(Big(prevHipVector.x).times(10)),
|
|
y: Big(y1).plus(Big(prevHipVector.y).times(10)),
|
|
}
|
|
if (!checkWallPolygon.inPolygon(prevCheckPoint)) {
|
|
prevHipVector = { x: Big(prevHipVector.x).neg(), y: Big(prevHipVector.y).neg() }
|
|
}
|
|
|
|
/** 다음 라인과의 사이 추녀마루의 각도를 확인한다, 각도가 지붕안쪽으로 향하지 않을때 반대로 처리한다.*/
|
|
const nextCheckPoint = {
|
|
x: Big(x2).plus(Big(nextHipVector.x).times(10)),
|
|
y: Big(y2).plus(Big(nextHipVector.y).times(10)),
|
|
}
|
|
if (!checkWallPolygon.inPolygon(nextCheckPoint)) {
|
|
nextHipVector = { x: Big(nextHipVector.x).neg(), y: Big(nextHipVector.y).neg() }
|
|
}
|
|
|
|
let prevHipLine, nextHipLine
|
|
/** 이전라인과의 연결지점에 추녀마루를 그린다. */
|
|
if (baseHipLines.filter((line) => line.x1 === x1 && line.y1 === y1).length === 0 && eavesType.includes(prevLine.attributes.type)) {
|
|
let prevEndPoint = {
|
|
x: Big(x1).plus(Big(prevHipVector.x).times(hipLength)).round(2),
|
|
y: Big(y1).plus(Big(prevHipVector.y).times(hipLength)).round(2),
|
|
}
|
|
|
|
const prevEndEdge = { vertex1: { x: x1, y: y1 }, vertex2: prevEndPoint }
|
|
|
|
const intersectBaseLine = []
|
|
baseLines
|
|
.filter((line) => line !== currentLine && line !== prevLine && line !== nextLine)
|
|
.filter((line) => {
|
|
if (currentAngle === 0 || currentAngle === 180) {
|
|
return Big(line.y1).minus(Big(y1)).s === nextHipVector.y.s || Big(line.y2).minus(Big(y1)).s === nextHipVector.y.s
|
|
} else {
|
|
return Big(line.x1).minus(Big(x1)).s === nextHipVector.x.s || Big(line.x2).minus(Big(x1)).s === nextHipVector.x.s
|
|
}
|
|
})
|
|
.forEach((line) => {
|
|
const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
|
|
const intersection = edgesIntersection(prevEndEdge, lineEdge)
|
|
|
|
if (intersection && Big(intersection.x - x1).s === nextHipVector.x.s && Big(intersection.y - y1).s === nextHipVector.y.s) {
|
|
const size = Big(intersection.x - x1)
|
|
.abs()
|
|
.pow(2)
|
|
.plus(
|
|
Big(intersection.y - y1)
|
|
.pow(2)
|
|
.abs(),
|
|
)
|
|
.sqrt()
|
|
if (size.gt(0)) {
|
|
intersectBaseLine.push({
|
|
intersection,
|
|
size,
|
|
})
|
|
}
|
|
}
|
|
})
|
|
|
|
const intersectBase = intersectBaseLine.reduce((prev, current) => {
|
|
return prev.size < current.size ? prev : current
|
|
}, intersectBaseLine[0])
|
|
|
|
if (intersectBase) {
|
|
prevEndPoint = {
|
|
x: Big(x1)
|
|
.plus(Big(nextHipVector.x).times(intersectBase.size.div(2)))
|
|
.round(2),
|
|
y: Big(y1)
|
|
.plus(Big(nextHipVector.y).times(intersectBase.size.div(2)))
|
|
.round(2),
|
|
}
|
|
}
|
|
|
|
const intersectRidgeLine = []
|
|
baseRidgeLines.forEach((line) => {
|
|
const intersection = edgesIntersection(
|
|
{ vertex1: { x: x1, y: y1 }, vertex2: prevEndPoint },
|
|
{ vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } },
|
|
)
|
|
if (intersection && !intersection.isIntersectionOutside) {
|
|
intersectRidgeLine.push({
|
|
intersection,
|
|
distance: Big(intersection.x)
|
|
.minus(Big(x1))
|
|
.abs()
|
|
.pow(2)
|
|
.plus(Big(intersection.y).minus(Big(y1)).pow(2).abs())
|
|
.sqrt(),
|
|
})
|
|
}
|
|
})
|
|
|
|
const intersectRidge = intersectRidgeLine.reduce((prev, current) => (prev.distance < current.distance ? prev : current), intersectRidgeLine[0])
|
|
|
|
if (intersectRidge) {
|
|
prevEndPoint.x = Big(intersectRidge.intersection.x).round(1)
|
|
prevEndPoint.y = Big(intersectRidge.intersection.y).round(1)
|
|
}
|
|
|
|
const xVector = Math.sign(Big(prevEndPoint.x).minus(Big(x1)).neg().toNumber())
|
|
const yVector = Math.sign(Big(prevEndPoint.y).minus(Big(y1)).neg().toNumber())
|
|
/** 지붕 선까지의 곂침에 따른 길이를 파악하기 위한 scale*/
|
|
let scale = Big(currentLine.attributes.offset).pow(2).plus(Big(prevLine.attributes.offset).pow(2)).sqrt()
|
|
scale = scale.eq(0) ? Big(1) : scale
|
|
|
|
/** scale 만큼 추녀마루를 늘려서 겹치는 포인트를 확인한다. */
|
|
const hipEdge = {
|
|
vertex1: { x: Big(x1).plus(scale.times(xVector)).toNumber(), y: Big(y1).plus(scale.times(yVector)).toNumber() },
|
|
vertex2: prevEndPoint,
|
|
}
|
|
|
|
let intersectPoints = []
|
|
/** 외벽선에서 추녀 마루가 겹치는 경우에 대한 확인*/
|
|
roof.lines.forEach((line) => {
|
|
const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
|
|
const intersection = edgesIntersection(hipEdge, lineEdge)
|
|
if (
|
|
intersection &&
|
|
!intersection.isIntersectionOutside &&
|
|
Math.sign(prevEndPoint.x - x1) === Math.sign(prevEndPoint.x - intersection.x) &&
|
|
Math.sign(prevEndPoint.y - y1) === Math.sign(prevEndPoint.y - intersection.y)
|
|
) {
|
|
const intersectSize = prevEndPoint.x
|
|
.minus(Big(intersection.x))
|
|
.abs()
|
|
.pow(2)
|
|
.plus(prevEndPoint.y.minus(Big(intersection.y)).pow(2).abs())
|
|
.sqrt()
|
|
.toNumber()
|
|
|
|
intersectPoints.push({
|
|
intersection,
|
|
size: intersectSize,
|
|
})
|
|
}
|
|
})
|
|
|
|
intersectPoints = intersectPoints.reduce((prev, current) => {
|
|
return prev.size < current.size ? prev : current
|
|
}, intersectPoints[0])
|
|
|
|
/** 겹치는 외벽선이 있을때 추녀마루를 외벽선 까지 늘려서 수정*/
|
|
if (intersectPoints && intersectPoints.intersection) {
|
|
prevHipLine = drawHipLine(
|
|
[intersectPoints.intersection.x, intersectPoints.intersection.y, prevEndPoint.x.toNumber(), prevEndPoint.y.toNumber()],
|
|
canvas,
|
|
roof,
|
|
textMode,
|
|
null,
|
|
prevDegree,
|
|
currentDegree,
|
|
)
|
|
baseHipLines.push({ x1, y1, x2: prevEndPoint.x.toNumber(), y2: prevEndPoint.y.toNumber(), line: prevHipLine })
|
|
}
|
|
}
|
|
/** 다음라인과의 연결지점에 추녀마루를 그린다. */
|
|
if (baseHipLines.filter((line) => line.x1 === x2 && line.y1 === y2).length === 0 && eavesType.includes(nextLine.attributes.type)) {
|
|
let nextEndPoint = {
|
|
x: Big(x2).plus(Big(nextHipVector.x).times(hipLength)).round(2),
|
|
y: Big(y2).plus(Big(nextHipVector.y).times(hipLength)).round(2),
|
|
}
|
|
|
|
const nextEndEdge = {
|
|
vertex1: { x: x2, y: y2 },
|
|
vertex2: nextEndPoint,
|
|
}
|
|
|
|
const intersectBaseLine = []
|
|
baseLines
|
|
.filter((line) => line !== currentLine && line !== prevLine && line !== nextLine)
|
|
.filter((line) => {
|
|
if (currentAngle === 0 || currentAngle === 180) {
|
|
return Big(line.y1).minus(Big(y1)).s === nextHipVector.y.s || Big(line.y2).minus(Big(y1)).s === nextHipVector.y.s
|
|
} else {
|
|
return Big(line.x1).minus(Big(x1)).s === nextHipVector.x.s || Big(line.x2).minus(Big(x1)).s === nextHipVector.x.s
|
|
}
|
|
})
|
|
.forEach((line) => {
|
|
const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
|
|
const intersection = edgesIntersection(nextEndEdge, lineEdge)
|
|
|
|
if (intersection && Big(intersection.x - x2).s === nextHipVector.x.s && Big(intersection.y - y2).s === nextHipVector.y.s) {
|
|
const size = Big(intersection.x - x2)
|
|
.abs()
|
|
.pow(2)
|
|
.plus(
|
|
Big(intersection.y - y2)
|
|
.abs()
|
|
.pow(2),
|
|
)
|
|
.sqrt()
|
|
if (size.gt(0)) {
|
|
intersectBaseLine.push({
|
|
intersection,
|
|
size,
|
|
})
|
|
}
|
|
}
|
|
})
|
|
|
|
const intersectBase = intersectBaseLine.reduce((prev, current) => {
|
|
return prev.size.lt(current.size) ? prev : current
|
|
}, intersectBaseLine[0])
|
|
|
|
if (intersectBase) {
|
|
nextEndPoint = {
|
|
x: Big(x2)
|
|
.plus(Big(nextHipVector.x).times(intersectBase.size.div(2)))
|
|
.round(2),
|
|
y: Big(y2)
|
|
.plus(Big(nextHipVector.y).times(intersectBase.size.div(2)))
|
|
.round(2),
|
|
}
|
|
}
|
|
|
|
const intersectRidgeLine = []
|
|
baseRidgeLines.forEach((line) => {
|
|
const intersection = edgesIntersection(
|
|
{ vertex1: { x: x2, y: y2 }, vertex2: nextEndPoint },
|
|
{ vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } },
|
|
)
|
|
|
|
if (intersection && !intersection.isIntersectionOutside) {
|
|
intersectRidgeLine.push({
|
|
intersection,
|
|
distance: Big(intersection.x)
|
|
.minus(Big(x1))
|
|
.pow(2)
|
|
.plus(Big(intersection.y).minus(Big(y1)).pow(2))
|
|
.sqrt(),
|
|
})
|
|
}
|
|
})
|
|
const intersectRidge = intersectRidgeLine.reduce((prev, current) => (prev.distance < current.distance ? prev : current), intersectRidgeLine[0])
|
|
|
|
if (intersectRidge) {
|
|
nextEndPoint.x = Big(intersectRidge.intersection.x).round(1)
|
|
nextEndPoint.y = Big(intersectRidge.intersection.y).round(1)
|
|
}
|
|
|
|
const xVector = Math.sign(Big(nextEndPoint.x).minus(Big(x2)).neg().toNumber())
|
|
const yVector = Math.sign(Big(nextEndPoint.y).minus(Big(y2)).neg().toNumber())
|
|
let scale = Big(currentLine.attributes.offset).pow(2).plus(Big(nextLine.attributes.offset).pow(2)).sqrt()
|
|
scale = scale.eq(0) ? Big(1) : scale
|
|
|
|
const hipEdge = {
|
|
vertex1: { x: Big(x2).plus(scale.times(xVector)).toNumber(), y: Big(y2).plus(scale.times(yVector)).toNumber() },
|
|
vertex2: nextEndPoint,
|
|
}
|
|
|
|
let intersectPoints = []
|
|
roof.lines.forEach((line) => {
|
|
const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
|
|
const intersection = edgesIntersection(hipEdge, lineEdge)
|
|
if (
|
|
intersection &&
|
|
!intersection.isIntersectionOutside &&
|
|
Math.sign(nextEndPoint.x - x2) === Math.sign(nextEndPoint.x - intersection.x) &&
|
|
Math.sign(nextEndPoint.y - y2) === Math.sign(nextEndPoint.y - intersection.y)
|
|
) {
|
|
const intersectSize = nextEndPoint.x
|
|
.minus(Big(intersection.x))
|
|
.pow(2)
|
|
.plus(nextEndPoint.y.minus(Big(intersection.y)).pow(2))
|
|
.abs()
|
|
.sqrt()
|
|
.toNumber()
|
|
|
|
intersectPoints.push({
|
|
intersection,
|
|
size: intersectSize,
|
|
})
|
|
}
|
|
})
|
|
|
|
intersectPoints = intersectPoints.reduce((prev, current) => {
|
|
return prev.size < current.size ? prev : current
|
|
}, intersectPoints[0])
|
|
|
|
if (intersectPoints && intersectPoints.intersection) {
|
|
nextHipLine = drawHipLine(
|
|
[intersectPoints.intersection.x, intersectPoints.intersection.y, nextEndPoint.x.toNumber(), nextEndPoint.y.toNumber()],
|
|
canvas,
|
|
roof,
|
|
textMode,
|
|
null,
|
|
prevDegree,
|
|
currentDegree,
|
|
)
|
|
baseHipLines.push({
|
|
x1: x2,
|
|
y1: y2,
|
|
x2: nextEndPoint.x.toNumber(),
|
|
y2: nextEndPoint.y.toNumber(),
|
|
line: nextHipLine,
|
|
})
|
|
}
|
|
}
|
|
})
|
|
|
|
/** baseHipLine 이 ridge에 붙지 않은 경우 확인 */
|
|
baseHipLines.forEach((hipLine) => {
|
|
const ridgeCount = baseRidgeLines.filter(
|
|
(ridgeLine) => (hipLine.x2 === ridgeLine.x1 && hipLine.y2 === ridgeLine.y1) || (hipLine.x2 === ridgeLine.x2 && hipLine.y2 === ridgeLine.y2),
|
|
).length
|
|
if (ridgeCount === 0) {
|
|
const hipXVector = Big(hipLine.x2).minus(hipLine.x1)
|
|
const hipYVector = Big(hipLine.y2).minus(hipLine.y1)
|
|
const hipSize = hipXVector.abs().pow(2).plus(hipYVector.abs().pow(2)).sqrt()
|
|
|
|
const intersectRidgePoints = []
|
|
|
|
const hipLineEdge = { vertex1: { x: hipLine.x1, y: hipLine.y1 }, vertex2: { x: hipLine.x2, y: hipLine.y2 } }
|
|
baseRidgeLines.forEach((ridgeLine) => {
|
|
const ridgeLineEdge = {
|
|
vertex1: { x: ridgeLine.x1, y: ridgeLine.y1 },
|
|
vertex2: { x: ridgeLine.x2, y: ridgeLine.y2 },
|
|
}
|
|
const intersection = edgesIntersection(hipLineEdge, ridgeLineEdge)
|
|
|
|
if (intersection) {
|
|
const intersectXVector = Big(intersection.x).minus(Big(hipLine.x1))
|
|
const intersectYVector = Big(intersection.y).minus(Big(hipLine.y1))
|
|
const intersectSize = intersectXVector.pow(2).plus(intersectYVector.pow(2)).sqrt()
|
|
|
|
if (!intersection.isIntersectionOutside) {
|
|
intersectRidgePoints.push({
|
|
x: intersection.x,
|
|
y: intersection.y,
|
|
size: intersectSize,
|
|
})
|
|
} else if (
|
|
((intersection.x === ridgeLine.x1 && intersection.y === ridgeLine.y1) ||
|
|
(intersection.x === ridgeLine.x2 && intersection.y === ridgeLine.y2)) &&
|
|
Math.sign(hipXVector.toNumber()) === Math.sign(intersectXVector.toNumber()) &&
|
|
Math.sign(hipYVector.toNumber()) === Math.sign(intersectYVector.toNumber()) &&
|
|
intersectSize.gt(0) &&
|
|
intersectSize.lt(hipSize)
|
|
) {
|
|
intersectRidgePoints.push({
|
|
x: intersection.x,
|
|
y: intersection.y,
|
|
size: intersectSize,
|
|
})
|
|
}
|
|
}
|
|
})
|
|
intersectRidgePoints.sort((prev, current) => prev.size.minus(current.size).toNumber())
|
|
if (intersectRidgePoints.length > 0) {
|
|
const oldPlaneSize = hipLine.line.attributes.planeSize
|
|
const oldActualSize = hipLine.line.attributes.actualSize
|
|
const theta = Big(Math.acos(Big(oldPlaneSize).div(oldActualSize)))
|
|
.times(180)
|
|
.div(Math.PI)
|
|
const planeSize = calcLinePlaneSize({
|
|
x1: hipLine.line.x1,
|
|
y1: hipLine.line.y1,
|
|
x2: hipLine.line.x2,
|
|
y2: hipLine.line.y2,
|
|
})
|
|
hipLine.x2 = intersectRidgePoints[0].x
|
|
hipLine.y2 = intersectRidgePoints[0].y
|
|
hipLine.line.set({ x2: intersectRidgePoints[0].x, y2: intersectRidgePoints[0].y })
|
|
hipLine.line.attributes.planeSize = planeSize
|
|
hipLine.line.attributes.actualSize = planeSize === oldActualSize ? 0 : Big(planeSize).div(theta).round(1).toNumber()
|
|
hipLine.line.fire('modified')
|
|
}
|
|
}
|
|
})
|
|
|
|
/** 지붕선에 맞닫지 않은 부분을 확인하여 처리 한다.*/
|
|
/** 1. 그려진 마루 중 추녀마루가 부재하는 경우를 판단. 부재는 연결된 라인이 홀수인 경우로 판단한다.*/
|
|
let unFinishedRidge = []
|
|
baseRidgeLines.forEach((current) => {
|
|
let checkPoint = [
|
|
{ x: current.x1, y: current.y1, line: current, cnt: 0, onRoofLine: false },
|
|
{ x: current.x2, y: current.y2, line: current, cnt: 0, onRoofLine: false },
|
|
]
|
|
baseHipLines.forEach((line) => {
|
|
if ((line.x1 === current.x1 && line.y1 === current.y1) || (line.x2 === current.x1 && line.y2 === current.y1)) {
|
|
checkPoint[0].cnt = checkPoint[0].cnt + 1
|
|
}
|
|
if ((line.x1 === current.x2 && line.y1 === current.y2) || (line.x2 === current.x2 && line.y2 === current.y2)) {
|
|
checkPoint[1].cnt = checkPoint[1].cnt + 1
|
|
}
|
|
})
|
|
|
|
/** 마루의 포인트가 지붕선 위에 있는경우는 제외 (케라바 등)*/
|
|
roof.lines.forEach((line) => {
|
|
if (
|
|
line.x1 === line.x2 &&
|
|
checkPoint[0].x === line.x1 &&
|
|
((line.y1 <= checkPoint[0].y && line.y2 >= checkPoint[0].y) || (line.y1 >= checkPoint[0].y && line.y2 <= checkPoint[0].y))
|
|
) {
|
|
checkPoint[0].onRoofLine = true
|
|
}
|
|
if (
|
|
line.y1 === line.y2 &&
|
|
checkPoint[0].y === line.y1 &&
|
|
((line.x1 <= checkPoint[0].x && line.x2 >= checkPoint[0].x) || (line.x1 >= checkPoint[0].x && line.x2 <= checkPoint[0].x))
|
|
) {
|
|
checkPoint[0].onRoofLine = true
|
|
}
|
|
if (
|
|
line.x1 === line.x2 &&
|
|
checkPoint[1].x === line.x1 &&
|
|
((line.y1 <= checkPoint[1].y && line.y2 >= checkPoint[1].y) || (line.y1 >= checkPoint[1].y && line.y2 <= checkPoint[1].y))
|
|
) {
|
|
checkPoint[1].onRoofLine = true
|
|
}
|
|
if (
|
|
line.y1 === line.y2 &&
|
|
checkPoint[1].y === line.y1 &&
|
|
((line.x1 <= checkPoint[1].x && line.x2 >= checkPoint[1].x) || (line.x1 >= checkPoint[1].x && line.x2 <= checkPoint[1].x))
|
|
) {
|
|
checkPoint[1].onRoofLine = true
|
|
}
|
|
})
|
|
// console.log('checkPoint : ', checkPoint)
|
|
if ((checkPoint[0].cnt === 0 || checkPoint[0].cnt % 2 !== 0) && !checkPoint[0].onRoofLine) {
|
|
unFinishedRidge.push(checkPoint[0])
|
|
}
|
|
if ((checkPoint[1].cnt === 0 || checkPoint[1].cnt % 2 !== 0) && !checkPoint[1].onRoofLine) {
|
|
unFinishedRidge.push(checkPoint[1])
|
|
}
|
|
})
|
|
|
|
/** 2. 그려진 추녀마루 중 완성되지 않은 것을 찾는다. 완성되지 않았다는 것은 연결된 포인트가 홀수인 경우로 판단한다.*/
|
|
const findUnFinishedPoints = (baseHipLines) => {
|
|
let unFinishedPoint = []
|
|
baseHipLines.forEach((current) => {
|
|
let checkPoint = [
|
|
{ x: current.x1, y: current.y1, checked: true, line: current.line },
|
|
{ x: current.x2, y: current.y2, checked: true, line: current.line },
|
|
]
|
|
baseLines.forEach((line) => {
|
|
if ((line.x1 === current.x1 && line.y1 === current.y1) || (line.x2 === current.x1 && line.y2 === current.y1)) {
|
|
checkPoint[0].checked = false
|
|
}
|
|
if ((line.x1 === current.x2 && line.y1 === current.y2) || (line.x2 === current.x2 && line.y2 === current.y2)) {
|
|
checkPoint[1].checked = false
|
|
}
|
|
})
|
|
|
|
const samePoints = []
|
|
checkPoint
|
|
.filter((point) => point.checked)
|
|
.forEach((point) => {
|
|
baseHipLines.forEach((line) => {
|
|
if (line.x1 === point.x && line.y1 === point.y) {
|
|
samePoints.push({ x: point.x, y: point.y, line: line })
|
|
}
|
|
if (line.x2 === point.x && line.y2 === point.y) {
|
|
samePoints.push({ x: point.x, y: point.y, line: line })
|
|
}
|
|
})
|
|
})
|
|
if (samePoints.length > 0 && samePoints.length % 2 !== 0) {
|
|
unFinishedPoint.push(samePoints[0])
|
|
}
|
|
})
|
|
return unFinishedPoint
|
|
}
|
|
|
|
let unFinishedPoint = findUnFinishedPoints(baseHipLines)
|
|
|
|
/**3. 라인이 부재인 마루의 모자란 라인을 찾는다. 라인은 그려진 추녀마루 중에 완성되지 않은 추녀마루와 확인한다.*/
|
|
/**3-1 라인을 그릴때 각도가 필요하기 때문에 각도를 구한다. 각도는 전체 지부의 각도가 같다면 하나로 처리 */
|
|
let degreeAllLine = []
|
|
baseLines
|
|
.filter((line) => eavesType.includes(line.attributes.type))
|
|
.forEach((line) => {
|
|
const pitch = line.attributes.pitch
|
|
const degree = line.attributes.degree
|
|
degreeAllLine.push(pitch > 0 ? getDegreeByChon(pitch) : degree)
|
|
})
|
|
|
|
let currentDegree, prevDegree
|
|
degreeAllLine = [...new Set(degreeAllLine)]
|
|
currentDegree = degreeAllLine[0]
|
|
if (degreeAllLine.length === 1) {
|
|
prevDegree = currentDegree
|
|
} else {
|
|
prevDegree = degreeAllLine[1]
|
|
}
|
|
|
|
/** 라인이 부재한 마루에 라인을 찾아 그린다.*/
|
|
unFinishedRidge.forEach((current) => {
|
|
let checkPoints = []
|
|
|
|
unFinishedPoint
|
|
.filter(
|
|
(point) =>
|
|
point.x !== current.x &&
|
|
point.y !== current.y &&
|
|
Big(point.x)
|
|
.minus(Big(current.x))
|
|
.abs()
|
|
.minus(Big(point.y).minus(Big(current.y)).abs())
|
|
.abs()
|
|
.lt(1),
|
|
)
|
|
.forEach((point) => {
|
|
const pointEdge = { vertex1: { x: point.x, y: point.y }, vertex2: { x: current.x, y: current.y } }
|
|
let isIntersection = false
|
|
baseLines.forEach((line) => {
|
|
const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
|
|
const intersection = edgesIntersection(pointEdge, lineEdge)
|
|
if (intersection && !intersection.isIntersectionOutside) {
|
|
isIntersection = true
|
|
}
|
|
})
|
|
if (!isIntersection) {
|
|
checkPoints.push({
|
|
point,
|
|
size: Big(point.x)
|
|
.minus(Big(current.x))
|
|
.abs()
|
|
.pow(2)
|
|
.plus(Big(point.y).minus(Big(current.y)).abs().pow(2))
|
|
.sqrt(),
|
|
})
|
|
}
|
|
})
|
|
if (checkPoints.length > 0) {
|
|
checkPoints.sort((a, b) => a.size - b.size)
|
|
const maxCnt = Big(2).minus(Big(current.cnt)).toNumber()
|
|
for (let i = 0; i < maxCnt; i++) {
|
|
const checkPoint = checkPoints[i]
|
|
if (checkPoint) {
|
|
let point = [checkPoint.point.x, checkPoint.point.y, current.x, current.y]
|
|
let hipBasePoint
|
|
|
|
baseHipLines.forEach((line) => {
|
|
const checkAngel1 = calculateAngle({ x: point[0], y: point[1] }, { x: point[2], y: point[3] })
|
|
const checkAngel2 = calculateAngle(
|
|
{ x: line.line.x1, y: line.line.y1 },
|
|
{
|
|
x: line.line.x2,
|
|
y: line.line.y2,
|
|
},
|
|
)
|
|
|
|
const isConnectLine =
|
|
((line.line.x1 === point[0] && line.line.y1 === point[1]) || (line.line.x2 === point[0] && line.line.y2 === point[1])) &&
|
|
checkAngel1 === checkAngel2
|
|
const isOverlap = segmentsOverlap(line.line, { x1: point[0], y1: point[1], x2: point[2], y2: point[3] })
|
|
const isSameLine =
|
|
(point[0] === line.x2 && point[1] === line.y2 && point[2] === line.x1 && point[3] === line.y1) ||
|
|
(point[0] === line.x1 && point[1] === line.y1 && point[2] === line.x2 && point[3] === line.y2)
|
|
|
|
if (isConnectLine || isOverlap || isSameLine) {
|
|
/** 겹치는 추녀마루와 하나의 선으로 변경*/
|
|
const mergePoint = [
|
|
{ x: point[0], y: point[1] },
|
|
{ x: point[2], y: point[3] },
|
|
{ x: line.line.x1, y: line.line.y1 },
|
|
{ x: line.line.x2, y: line.line.y2 },
|
|
]
|
|
/** baseHipLines도 조정*/
|
|
mergePoint.sort((a, b) => a.x - b.x)
|
|
|
|
hipBasePoint = { x1: line.x1, y1: line.y1, x2: line.x2, y2: line.y2 }
|
|
point = [mergePoint[0].x, mergePoint[0].y, mergePoint[3].x, mergePoint[3].y]
|
|
const theta = Big(Math.acos(Big(line.line.attributes.planeSize).div(line.line.attributes.actualSize)))
|
|
.times(180)
|
|
.div(Math.PI)
|
|
.round(1)
|
|
prevDegree = theta.toNumber()
|
|
currentDegree = theta.toNumber()
|
|
canvas.remove(line.line)
|
|
baseHipLines = baseHipLines.filter((baseLine) => baseLine.line !== line.line)
|
|
}
|
|
})
|
|
|
|
const hipLine = drawHipLine(point, canvas, roof, textMode, null, prevDegree, currentDegree)
|
|
if (hipBasePoint) {
|
|
baseHipLines.push({ x1: hipBasePoint.x1, y1: hipBasePoint.y1, x2: point[2], y2: point[3], line: hipLine })
|
|
} else {
|
|
baseHipLines.push({ x1: point[0], y1: point[1], x2: point[2], y2: point[3], line: hipLine })
|
|
}
|
|
current.cnt = current.cnt + 1
|
|
}
|
|
}
|
|
}
|
|
|
|
/** 라인이 다 그려지지 않은 경우 */
|
|
if (current.cnt % 2 !== 0) {
|
|
let basePoints = baseLinePoints
|
|
.filter((point) =>
|
|
Big(point.x)
|
|
.minus(Big(current.x))
|
|
.abs()
|
|
.minus(Big(point.y).minus(Big(current.y)).abs())
|
|
.abs()
|
|
.lt(1),
|
|
)
|
|
.filter((point) => {
|
|
const pointEdge = { vertex1: { x: current.x, y: current.y }, vertex2: { x: point.x, y: point.y } }
|
|
|
|
const intersectPoints = []
|
|
baseLines.forEach((line) => {
|
|
const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
|
|
const intersection = edgesIntersection(pointEdge, lineEdge)
|
|
if (intersection && !intersection.isIntersectionOutside) {
|
|
intersectPoints.push(intersection)
|
|
}
|
|
})
|
|
return (
|
|
!intersectPoints.filter(
|
|
(intersect) => !(Big(intersect.x).minus(Big(point.x)).abs().lt(1) && Big(intersect.y).minus(Big(point.y)).abs().lt(1)),
|
|
).length > 0
|
|
)
|
|
})
|
|
|
|
/** hip을 그리기 위한 기본 길이*/
|
|
let hipSize = Big(0)
|
|
if (basePoints.length > 0) {
|
|
basePoints.sort((a, b) => {
|
|
const aSize = Big(a.x)
|
|
.minus(Big(current.x))
|
|
.abs()
|
|
.pow(2)
|
|
.plus(Big(a.y).minus(Big(current.y)).abs().pow(2))
|
|
.sqrt()
|
|
const bSize = Big(b.x)
|
|
.minus(Big(current.x))
|
|
.abs()
|
|
.pow(2)
|
|
.plus(Big(b.y).minus(Big(current.y)).abs().pow(2))
|
|
.sqrt()
|
|
return aSize - bSize
|
|
})
|
|
const baseHips = baseHipLines.filter((line) => line.x1 === basePoints[0].x && line.y1 === basePoints[0].y)
|
|
if (baseHips.length > 0) {
|
|
hipSize = Big(baseHips[0].line.attributes.planeSize)
|
|
}
|
|
}
|
|
|
|
if (hipSize.eq(0)) {
|
|
const ridge = current.line
|
|
basePoints = baseHipLines
|
|
.filter((line) => (line.x2 === ridge.x1 && line.y2 === ridge.y1) || (line.x2 === ridge.x2 && line.y2 === ridge.y2))
|
|
.filter((line) => baseLines.filter((baseLine) => baseLine.x1 === line.x1 && baseLine.y1 === line.y1).length > 0)
|
|
basePoints.sort((a, b) => a.line.attributes.planeSize - b.line.attributes.planeSize)
|
|
hipSize = Big(basePoints[0].line.attributes.planeSize)
|
|
}
|
|
hipSize = hipSize.pow(2).div(2).sqrt().round().div(10).toNumber()
|
|
|
|
/** 현재 라인을 기준으로 45, 135, 225, 315 방향을 확인하기 위한 좌표, hip은 45도 방향으로만 그린다. */
|
|
const checkEdge45 = {
|
|
vertex1: { x: current.x, y: current.y },
|
|
vertex2: { x: current.x + hipSize, y: current.y - hipSize },
|
|
}
|
|
const checkEdge135 = {
|
|
vertex1: { x: current.x, y: current.y },
|
|
vertex2: { x: current.x + hipSize, y: current.y + hipSize },
|
|
}
|
|
const checkEdge225 = {
|
|
vertex1: { x: current.x, y: current.y },
|
|
vertex2: { x: current.x - hipSize, y: current.y + hipSize },
|
|
}
|
|
const checkEdge315 = {
|
|
vertex1: { x: current.x, y: current.y },
|
|
vertex2: { x: current.x - hipSize, y: current.y - hipSize },
|
|
}
|
|
|
|
let intersectPoints = []
|
|
let notIntersect45 = true,
|
|
notIntersect135 = true,
|
|
notIntersect225 = true,
|
|
notIntersect315 = true
|
|
baseLines.forEach((line) => {
|
|
const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
|
|
const intersection45 = edgesIntersection(checkEdge45, lineEdge)
|
|
const intersection135 = edgesIntersection(checkEdge135, lineEdge)
|
|
const intersection225 = edgesIntersection(checkEdge225, lineEdge)
|
|
const intersection315 = edgesIntersection(checkEdge315, lineEdge)
|
|
|
|
if (intersection45 && !intersection45.isIntersectionOutside) {
|
|
intersectPoints.push(intersection45)
|
|
notIntersect45 = false
|
|
}
|
|
if (intersection135 && !intersection135.isIntersectionOutside) {
|
|
intersectPoints.push(intersection135)
|
|
notIntersect135 = false
|
|
}
|
|
if (intersection225 && !intersection225.isIntersectionOutside) {
|
|
intersectPoints.push(intersection225)
|
|
notIntersect225 = false
|
|
}
|
|
if (intersection315 && !intersection315.isIntersectionOutside) {
|
|
intersectPoints.push(intersection315)
|
|
notIntersect315 = false
|
|
}
|
|
})
|
|
/** baseLine과 교차하지 않는 포인트를 추가한다.*/
|
|
if (notIntersect45) {
|
|
intersectPoints.push(checkEdge45.vertex2)
|
|
}
|
|
if (notIntersect135) {
|
|
intersectPoints.push(checkEdge135.vertex2)
|
|
}
|
|
if (notIntersect225) {
|
|
intersectPoints.push(checkEdge225.vertex2)
|
|
}
|
|
if (notIntersect315) {
|
|
intersectPoints.push(checkEdge315.vertex2)
|
|
}
|
|
/** baseLine의 각 좌표와 교차하는 경우로 한정*/
|
|
intersectPoints = intersectPoints.filter((is) => baseLinePoints.filter((point) => point.x === is.x && point.y === is.y).length > 0)
|
|
/** baseLine과 교차하는 좌표의 경우 이미 그려진 추녀마루가 존재한다면 제외한다. */
|
|
intersectPoints = intersectPoints.filter((point) => baseHipLines.filter((hip) => hip.x1 === point.x && hip.y1 === point.y).length === 0)
|
|
/** 중복제거 */
|
|
intersectPoints = intersectPoints.filter((point, index, self) => index === self.findIndex((p) => p.x === point.x && p.y === point.y))
|
|
|
|
intersectPoints.forEach((is) => {
|
|
const points = [current.x, current.y, is.x, is.y]
|
|
/** 추녀마루의 연결점이 처마라인이 아닌경우 return */
|
|
let hasGable = false
|
|
baseLines
|
|
.filter((line) => (line.x1 === points[2] && line.y1 === points[3]) || (line.x2 === points[2] && line.y2 === points[3]))
|
|
.forEach((line) => {
|
|
if (!eavesType.includes(line.attributes.type)) {
|
|
hasGable = true
|
|
}
|
|
})
|
|
if (hasGable) return
|
|
const pointEdge = { vertex1: { x: points[0], y: points[1] }, vertex2: { x: points[2], y: points[3] } }
|
|
const vectorX = Math.sign(Big(points[2]).minus(Big(points[0])).toNumber())
|
|
const vectorY = Math.sign(Big(points[3]).minus(Big(points[1])).toNumber())
|
|
const roofIntersections = []
|
|
roof.lines.forEach((line) => {
|
|
const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
|
|
const intersection = edgesIntersection(pointEdge, lineEdge)
|
|
if (intersection) {
|
|
const vectorIntersectionX = Math.sign(Big(intersection.x).minus(Big(points[0])).toNumber())
|
|
const vectorIntersectionY = Math.sign(Big(intersection.y).minus(Big(points[1])).toNumber())
|
|
if (vectorIntersectionX === vectorX && vectorIntersectionY === vectorY) {
|
|
roofIntersections.push({
|
|
x: intersection.x,
|
|
y: intersection.y,
|
|
size: calcLinePlaneSize({ x1: points[0], y1: points[1], x2: intersection.x, y2: intersection.y }),
|
|
})
|
|
}
|
|
}
|
|
})
|
|
roofIntersections.sort((a, b) => a.size - b.size)
|
|
|
|
const hipLine = drawHipLine(
|
|
[points[0], points[1], roofIntersections[0].x, roofIntersections[0].y],
|
|
canvas,
|
|
roof,
|
|
textMode,
|
|
null,
|
|
prevDegree,
|
|
currentDegree,
|
|
)
|
|
baseHipLines.push({ x1: points[0], y1: points[1], x2: points[2], y2: points[3], line: hipLine })
|
|
current.cnt = current.cnt + 1
|
|
})
|
|
}
|
|
})
|
|
|
|
/** hip이 짝수개가 맞다아있는데 마루와 연결되지 않는 포인트를 찾는다. 그려지지 않은 마루를 찾기 위함.*/
|
|
let noRidgeHipPoints = baseHipLines
|
|
.filter((current) => current.x1 !== current.x2 && current.y1 !== current.y2)
|
|
.filter((current) => {
|
|
const lines = baseHipLines
|
|
.filter((line) => line !== current)
|
|
.filter((line) => (line.x1 === current.x2 && line.y1 === current.y2) || (line.x2 === current.x2 && line.y2 === current.y2))
|
|
|
|
return lines.length !== 0 && lines.length % 2 !== 0
|
|
})
|
|
.filter(
|
|
(current) =>
|
|
baseRidgeLines.filter((line) => (line.x1 === current.x2 && line.y1 === current.y2) || (line.x2 === current.x2 && line.y2 === current.y2))
|
|
.length === 0 &&
|
|
baseGableRidgeLines.filter((line) => (line.x1 === current.x2 && line.y1 === current.y2) || (line.x2 === current.x2 && line.y2 === current.y2))
|
|
.length === 0,
|
|
)
|
|
|
|
noRidgeHipPoints.forEach((current) => {
|
|
const orthogonalPoints = noRidgeHipPoints.filter((point) => {
|
|
if (point !== current && ((current.x2 === point.x2 && current.y2 !== point.y2) || (current.x2 !== point.x2 && current.y2 === point.y2))) {
|
|
return true
|
|
}
|
|
})
|
|
|
|
/** 직교 하는 포인트가 존재 할때 마루를 그린다. */
|
|
if (orthogonalPoints.length > 0 && baseRidgeCount < getMaxRidge(baseLines.length)) {
|
|
baseRidgeCount = baseRidgeCount + 1
|
|
const points = [current.x2, current.y2, orthogonalPoints[0].x2, orthogonalPoints[0].y2]
|
|
const ridgeLine = drawRidgeLine(points, canvas, roof, textMode)
|
|
baseRidgeLines.push(ridgeLine)
|
|
}
|
|
})
|
|
|
|
/** 중복제거*/
|
|
baseRidgeLines.forEach((current) => {
|
|
const sameRidge = baseRidgeLines.filter(
|
|
(line) =>
|
|
line !== current &&
|
|
((line.x1 === current.x1 && line.y1 === current.y1 && line.x2 === current.x2 && line.y2 === current.y2) ||
|
|
(line.x1 === current.x2 && line.y1 === current.y2 && line.x2 === current.x1 && line.y2 === current.y1)),
|
|
)
|
|
if (sameRidge.length > 0) {
|
|
sameRidge.forEach((duplicateLine) => {
|
|
const index = baseRidgeLines.indexOf(duplicateLine)
|
|
if (index !== -1) {
|
|
baseRidgeLines.splice(index, 1)
|
|
}
|
|
canvas.remove(duplicateLine)
|
|
})
|
|
}
|
|
})
|
|
|
|
/** 직교 하는 포인트가 없는 경우 남은 포인트 처리 */
|
|
const checkEdgeLines = []
|
|
noRidgeHipPoints.forEach((current) => {
|
|
noRidgeHipPoints.forEach((current) => {
|
|
noRidgeHipPoints
|
|
.filter((point) => point !== current)
|
|
.forEach((point) => {
|
|
checkEdgeLines.push(
|
|
{ vertex1: { x: current.x2, y: current.y2 }, vertex2: { x: current.x2, y: point.y2 } },
|
|
{ vertex1: { x: current.x2, y: point.y2 }, vertex2: { x: point.x2, y: point.y2 } },
|
|
{ vertex1: { x: point.x2, y: point.y2 }, vertex2: { x: point.x2, y: current.y2 } },
|
|
{ vertex1: { x: point.x2, y: current.y2 }, vertex2: { x: current.x2, y: current.y2 } },
|
|
)
|
|
})
|
|
})
|
|
})
|
|
|
|
/** 연결되지 않은 포인트를 찾아서 해당 포인트를 가지고 있는 라인을 찾는다. */
|
|
let unFinishedPoints = []
|
|
let unFinishedLines = []
|
|
let intersectPoints = []
|
|
baseHipLines.forEach((line) => {
|
|
if (baseLinePoints.filter((point) => point.x === line.x1 && point.y === line.y1).length === 0) {
|
|
unFinishedPoints.push({ x: line.x1, y: line.y1 })
|
|
}
|
|
if (baseLinePoints.filter((point) => point.x === line.x2 && point.y === line.y2).length === 0) {
|
|
unFinishedPoints.push({ x: line.x2, y: line.y2 })
|
|
}
|
|
})
|
|
unFinishedPoints
|
|
.filter((point) => unFinishedPoints.filter((p) => p !== point && p.x === point.x && p.y === point.y).length === 0)
|
|
.forEach((point) => {
|
|
baseHipLines
|
|
.filter((line) => (line.x1 === point.x && line.y1 === point.y) || (line.x2 === point.x && line.y2 === point.y))
|
|
.forEach((line) => unFinishedLines.push(line))
|
|
})
|
|
|
|
unFinishedLines.forEach((line) => {
|
|
const xVector = Math.sign(Big(line.x2).minus(Big(line.x1)))
|
|
const yVector = Math.sign(Big(line.y2).minus(Big(line.y1)))
|
|
let lineIntersectPoints = []
|
|
checkEdgeLines.forEach((edge) => {
|
|
const intersectEdge = edgesIntersection(edge, {
|
|
vertex1: { x: line.x1, y: line.y1 },
|
|
vertex2: { x: line.x2, y: line.y2 },
|
|
})
|
|
if (intersectEdge) {
|
|
const isXVector = Math.sign(Big(intersectEdge.x).minus(Big(line.x1))) === xVector
|
|
const isYVector = Math.sign(Big(intersectEdge.y).minus(Big(line.y1))) === yVector
|
|
if (isXVector && isYVector) {
|
|
lineIntersectPoints.push({
|
|
intersection: intersectEdge,
|
|
size: Big(intersectEdge.x)
|
|
.minus(Big(line.x1))
|
|
.abs()
|
|
.pow(2)
|
|
.plus(Big(intersectEdge.y).minus(Big(line.y1)).abs().pow(2))
|
|
.sqrt()
|
|
.round(1)
|
|
.toNumber(),
|
|
})
|
|
}
|
|
}
|
|
})
|
|
|
|
lineIntersectPoints = lineIntersectPoints.filter(
|
|
(point, index, self) => index === self.findIndex((p) => p.intersection.x === point.intersection.x && p.intersection.y === point.intersection.y),
|
|
)
|
|
lineIntersectPoints.sort((a, b) => a.size - b.size)
|
|
if (lineIntersectPoints.length > 0) {
|
|
intersectPoints.push({ intersection: lineIntersectPoints[0].intersection, line })
|
|
}
|
|
})
|
|
|
|
/** 마루를 그릴수 있는지 찾는다. */
|
|
noRidgeHipPoints.forEach((hipPoint) => {
|
|
const ridgePoints = []
|
|
intersectPoints
|
|
.filter(
|
|
(intersectPoint) =>
|
|
(intersectPoint.intersection.x !== hipPoint.x2 && intersectPoint.intersection.y === hipPoint.y2) ||
|
|
(intersectPoint.intersection.x === hipPoint.x2 && intersectPoint.intersection.y !== hipPoint.y2),
|
|
)
|
|
.forEach((intersectPoint) => {
|
|
ridgePoints.push({
|
|
intersection: intersectPoint,
|
|
distance: Big(intersectPoint.intersection.x)
|
|
.minus(Big(hipPoint.x2))
|
|
.abs()
|
|
.pow(2)
|
|
.plus(Big(intersectPoint.intersection.y).minus(Big(hipPoint.y2)).abs().pow(2))
|
|
.sqrt()
|
|
.round(1)
|
|
.toNumber(),
|
|
})
|
|
})
|
|
ridgePoints.sort((a, b) => a.distance - b.distance)
|
|
|
|
if (ridgePoints.length > 0 && baseRidgeCount < getMaxRidge(baseLines.length)) {
|
|
const intersection = ridgePoints[0].intersection
|
|
const isPoint = intersection.intersection
|
|
const points = [hipPoint.x2, hipPoint.y2, isPoint.x, isPoint.y]
|
|
const ridgeLine = drawRidgeLine(points, canvas, roof, textMode)
|
|
baseRidgeCount = baseRidgeCount + 1
|
|
baseRidgeLines.push(ridgeLine)
|
|
|
|
let baseHipLine = baseHipLines.filter((line) => line === intersection.line)[0]
|
|
baseHipLine.x2 = isPoint.x
|
|
baseHipLines.y2 = isPoint.y
|
|
/** 보조선 라인 조정*/
|
|
const hipLine = intersection.line.line
|
|
/** 평면길이 */
|
|
const planeSize = calcLinePlaneSize({
|
|
x1: hipLine.x1,
|
|
y1: hipLine.y1,
|
|
x2: isPoint.x,
|
|
y2: isPoint.y,
|
|
})
|
|
/** 실제길이 */
|
|
const actualSize =
|
|
prevDegree === currentDegree
|
|
? calcLineActualSize(
|
|
{
|
|
x1: hipLine.x1,
|
|
y1: hipLine.y1,
|
|
x2: isPoint.x,
|
|
y2: isPoint.y,
|
|
},
|
|
currentDegree,
|
|
)
|
|
: 0
|
|
hipLine.set({
|
|
x2: isPoint.x,
|
|
y2: isPoint.y,
|
|
attributes: { roofId: roof.id, planeSize, actualSize },
|
|
})
|
|
hipLine.fire('modified')
|
|
intersectPoints = intersectPoints.filter((isp) => isp !== intersection)
|
|
noRidgeHipPoints = noRidgeHipPoints.filter((hip) => hip !== hipPoint)
|
|
} else {
|
|
const linePoints = []
|
|
intersectPoints.forEach((intersectPoint) => {
|
|
const intersection = intersectPoint.intersection
|
|
const xVector = Math.sign(Big(intersection.x).minus(Big(hipPoint.x2)))
|
|
const yVector = Math.sign(Big(intersection.y).minus(Big(hipPoint.y2)))
|
|
|
|
const checkEdge = {
|
|
vertex1: { x: intersection.x, y: intersection.y },
|
|
vertex2: {
|
|
x: Big(intersection.x).plus(Big(xVector).times(10)).toNumber(),
|
|
y: Big(intersection.y).plus(Big(yVector).times(10)).toNumber(),
|
|
},
|
|
}
|
|
const intersectX = edgesIntersection(
|
|
{
|
|
vertex1: { x: hipPoint.x2, y: hipPoint.y2 },
|
|
vertex2: { x: Big(hipPoint.x2).plus(Big(xVector).neg().times(10)).toNumber(), y: hipPoint.y2 },
|
|
},
|
|
checkEdge,
|
|
)
|
|
const intersectY = edgesIntersection(
|
|
{
|
|
vertex1: { x: hipPoint.x2, y: hipPoint.y2 },
|
|
vertex2: { x: hipPoint.x2, y: Big(hipPoint.y2).plus(Big(yVector).neg().times(10)).toNumber() },
|
|
},
|
|
checkEdge,
|
|
)
|
|
|
|
let distanceX = Infinity,
|
|
distanceY = Infinity
|
|
|
|
if (intersectX) {
|
|
distanceX = Big(intersectX.x)
|
|
.minus(Big(intersection.x))
|
|
.abs()
|
|
.pow(2)
|
|
.plus(Big(intersectX.y).minus(Big(intersection.y)).abs().pow(2))
|
|
.sqrt()
|
|
.round(1)
|
|
.toNumber()
|
|
}
|
|
if (intersectY) {
|
|
distanceY = Big(intersectY.x)
|
|
.minus(Big(intersection.x))
|
|
.abs()
|
|
.pow(2)
|
|
.plus(Big(intersectY.y).minus(Big(intersection.y)).abs().pow(2))
|
|
.sqrt()
|
|
.round(1)
|
|
.toNumber()
|
|
}
|
|
|
|
if (distanceX < distanceY) {
|
|
linePoints.push({ intersection: intersectX, intersectPoint })
|
|
}
|
|
if (distanceX > distanceY) {
|
|
linePoints.push({ intersection: intersectY, intersectPoint })
|
|
}
|
|
})
|
|
|
|
const linePoint = linePoints.reduce((prev, current) => {
|
|
const prevDistance = Big(prev.intersection.x)
|
|
.minus(Big(hipPoint.x2))
|
|
.abs()
|
|
.pow(2)
|
|
.plus(Big(prev.intersection.y).minus(Big(hipPoint.y2)).abs().pow(2))
|
|
.sqrt()
|
|
const currentDistance = Big(current.intersection.x)
|
|
.minus(Big(hipPoint.x2))
|
|
.abs()
|
|
.pow(2)
|
|
.plus(Big(current.intersection.y).minus(Big(hipPoint.y2)).abs().pow(2))
|
|
.sqrt()
|
|
if (prevDistance < currentDistance) {
|
|
return prev
|
|
} else {
|
|
return current
|
|
}
|
|
}, linePoints[0])
|
|
|
|
if (!linePoint) return
|
|
const hipStartPoint = [hipPoint.x2, hipPoint.y2, linePoint.intersection.x, linePoint.intersection.y]
|
|
/** 직선인 경우 마루를 그린다.*/
|
|
if (
|
|
((hipStartPoint[0] === hipStartPoint[2] && hipStartPoint[1] !== hipStartPoint[3]) ||
|
|
(hipStartPoint[0] !== hipStartPoint[2] && hipStartPoint[1] === hipStartPoint[3])) &&
|
|
baseRidgeCount < getMaxRidge(baseLines.length)
|
|
) {
|
|
const ridgeLine = drawRidgeLine(hipStartPoint, canvas, roof, textMode)
|
|
baseRidgeCount = baseRidgeCount + 1
|
|
baseRidgeLines.push(ridgeLine)
|
|
noRidgeHipPoints = noRidgeHipPoints.filter((hip) => hip !== hipPoint)
|
|
}
|
|
/** 대각선인경우 hip을 그린다. */
|
|
if (
|
|
Big(hipStartPoint[0])
|
|
.minus(Big(hipStartPoint[2]))
|
|
.abs()
|
|
.minus(Big(hipStartPoint[1]).minus(Big(hipStartPoint[3])).abs())
|
|
.abs()
|
|
.lt(1)
|
|
) {
|
|
// console.log('힙1')
|
|
const hipLine = drawHipLine(hipStartPoint, canvas, roof, textMode, null, prevDegree, currentDegree)
|
|
baseHipLines.push({
|
|
x1: hipStartPoint[0],
|
|
y1: hipStartPoint[1],
|
|
x2: hipStartPoint[2],
|
|
y2: hipStartPoint[3],
|
|
line: hipLine,
|
|
})
|
|
noRidgeHipPoints = noRidgeHipPoints.filter((hip) => hip !== hipPoint)
|
|
}
|
|
|
|
const isStartPoint = [
|
|
linePoint.intersection.x,
|
|
linePoint.intersection.y,
|
|
linePoint.intersectPoint.intersection.x,
|
|
linePoint.intersectPoint.intersection.y,
|
|
]
|
|
if (
|
|
((isStartPoint[0] === isStartPoint[2] && isStartPoint[1] !== isStartPoint[3]) ||
|
|
(isStartPoint[0] !== isStartPoint[2] && isStartPoint[1] === isStartPoint[3])) &&
|
|
baseRidgeCount < getMaxRidge(baseLines.length)
|
|
) {
|
|
const ridgeLine = drawRidgeLine(isStartPoint, canvas, roof, textMode)
|
|
baseRidgeCount = baseRidgeCount + 1
|
|
baseRidgeLines.push(ridgeLine)
|
|
}
|
|
if (
|
|
Big(isStartPoint[0])
|
|
.minus(Big(isStartPoint[2]))
|
|
.abs()
|
|
.minus(Big(isStartPoint[1]).minus(Big(isStartPoint[3])).abs())
|
|
.abs()
|
|
.lt(1)
|
|
) {
|
|
const hipLine = drawHipLine(isStartPoint, canvas, roof, textMode, null, prevDegree, currentDegree)
|
|
baseHipLines.push({
|
|
x1: isStartPoint[0],
|
|
y1: isStartPoint[1],
|
|
x2: isStartPoint[2],
|
|
y2: isStartPoint[3],
|
|
line: hipLine,
|
|
})
|
|
}
|
|
}
|
|
})
|
|
|
|
const ridgeAllPoints = []
|
|
baseRidgeLines.forEach((line) => ridgeAllPoints.push({ x: line.x1, y: line.y1 }, { x: line.x2, y: line.y2 }))
|
|
|
|
/** hip 중에 지붕의 라인과 만나지 않은 선을 확인.*/
|
|
baseHipLines
|
|
.filter(
|
|
(hip) => baseLinePoints.filter((point) => (point.x === hip.x1 && point.y === hip.y1) || (point.x === hip.x2 && point.y === hip.y2)).length > 0,
|
|
)
|
|
.filter((hip) => {
|
|
const hipEdge = { vertex1: { x: hip.line.x1, y: hip.line.y1 }, vertex2: { x: hip.line.x2, y: hip.line.y2 } }
|
|
const hipVectorX = Math.sign(Big(hip.x1).minus(Big(hip.x2)))
|
|
const hipVectorY = Math.sign(Big(hip.y1).minus(Big(hip.y2)))
|
|
let isIntersect = false
|
|
roof.lines.forEach((line) => {
|
|
const edge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
|
|
const intersection = edgesIntersection(edge, hipEdge)
|
|
if (intersection && !intersection.isIntersectionOutside) {
|
|
const isVectorX = Math.sign(Big(intersection.x).minus(Big(hip.x2)))
|
|
const isVectorY = Math.sign(Big(intersection.y).minus(Big(hip.y2)))
|
|
if (isVectorX === hipVectorX && isVectorY === hipVectorY) {
|
|
isIntersect = true
|
|
}
|
|
}
|
|
})
|
|
return !isIntersect
|
|
})
|
|
.forEach((hip) => {
|
|
const hipLine = hip.line
|
|
if (hipLine) {
|
|
const hipVectorX = Big(hipLine.x2).plus(Big(hipLine.attributes.planeSize).times(Big(hipLine.x1).minus(Big(hipLine.x2)).neg().s))
|
|
const hipVectorY = Big(hipLine.y2).plus(Big(hipLine.attributes.planeSize).times(Big(hipLine.y1).minus(Big(hipLine.y2)).neg().s))
|
|
const overlapLineX = roof.lines
|
|
.filter((roofLine) => roofLine.x1 !== roofLine.x2 && roofLine.y1 === hipLine.y2 && roofLine.y2 === hipLine.y2)
|
|
.filter((roofLine) => {
|
|
const minX = Math.min(roofLine.x1, roofLine.x2, hipLine.x2)
|
|
const maxX = Math.max(roofLine.x1, roofLine.x2, hipLine.x2)
|
|
const checkLineEdge = { vertex1: { x: minX, y: hipLine.y2 }, vertex2: { x: maxX, y: hipLine.y2 } }
|
|
let isIntersect = false
|
|
baseHipLines.forEach((baseHipLine) => {
|
|
const edge = {
|
|
vertex1: { x: baseHipLine.x1, y: baseHipLine.y1 },
|
|
vertex2: { x: baseHipLine.x2, y: baseHipLine.y2 },
|
|
}
|
|
const intersection = edgesIntersection(edge, checkLineEdge)
|
|
if (intersection && !intersection.isIntersectionOutside) {
|
|
const isVectorX = Math.sign(Big(intersection.x).minus(Big(baseHipLine.x2)))
|
|
const isVectorY = Math.sign(Big(intersection.y).minus(Big(baseHipLine.y2)))
|
|
if (isVectorX === hipVectorX && isVectorY === hipVectorY) {
|
|
isIntersect = true
|
|
}
|
|
}
|
|
})
|
|
baseRidgeLines.forEach((baseRidgeLine) => {
|
|
const edge = {
|
|
vertex1: { x: baseRidgeLine.x1, y: baseRidgeLine.y1 },
|
|
vertex2: { x: baseRidgeLine.x2, y: baseRidgeLine.y2 },
|
|
}
|
|
const intersection = edgesIntersection(edge, checkLineEdge)
|
|
if (intersection && !intersection.isIntersectionOutside) {
|
|
const isVectorX = Math.sign(Big(intersection.x).minus(Big(hipLine.x2)))
|
|
const isVectorY = Math.sign(Big(intersection.y).minus(Big(hipLine.y2)))
|
|
if (isVectorX === hipVectorX && isVectorY === hipVectorY) {
|
|
isIntersect = true
|
|
}
|
|
}
|
|
})
|
|
return !isIntersect
|
|
})
|
|
const overlapLineY = roof.lines
|
|
.filter((roofLine) => roofLine.y1 !== roofLine.y2 && roofLine.x1 === hipLine.x2 && roofLine.x2 === hipLine.x2)
|
|
.filter((roofLine) => {
|
|
const minY = Math.min(roofLine.y1, roofLine.y2, hipLine.y2)
|
|
const maxY = Math.max(roofLine.y1, roofLine.y2, hipLine.y2)
|
|
const checkLineEdge = { vertex1: { x: hipLine.x2, y: minY }, vertex2: { x: hipLine.x2, y: maxY } }
|
|
let isIntersect = false
|
|
baseHipLines.forEach((baseHipLine) => {
|
|
const edge = {
|
|
vertex1: { x: baseHipLine.x1, y: baseHipLine.y1 },
|
|
vertex2: { x: baseHipLine.x2, y: baseHipLine.y2 },
|
|
}
|
|
const intersection = edgesIntersection(edge, checkLineEdge)
|
|
if (intersection && !intersection.isIntersectionOutside) {
|
|
const isVectorX = Math.sign(Big(intersection.x).minus(Big(hipLine.x2)))
|
|
const isVectorY = Math.sign(Big(intersection.y).minus(Big(hipLine.y2)))
|
|
if (isVectorX === hipVectorX && isVectorY === hipVectorY) {
|
|
isIntersect = true
|
|
}
|
|
}
|
|
})
|
|
baseRidgeLines.forEach((baseRidgeLine) => {
|
|
const edge = {
|
|
vertex1: { x: baseRidgeLine.x1, y: baseRidgeLine.y1 },
|
|
vertex2: { x: baseRidgeLine.x2, y: baseRidgeLine.y2 },
|
|
}
|
|
const intersection = edgesIntersection(edge, checkLineEdge)
|
|
if (intersection && !intersection.isIntersectionOutside) {
|
|
const isVectorX = Math.sign(Big(intersection.x).minus(Big(hipLine.x2)))
|
|
const isVectorY = Math.sign(Big(intersection.y).minus(Big(hipLine.y2)))
|
|
if (isVectorX === hipVectorX && isVectorY === hipVectorY) {
|
|
isIntersect = true
|
|
}
|
|
}
|
|
})
|
|
return !isIntersect
|
|
})
|
|
|
|
overlapLineX.reduce((prev, current) => {
|
|
const prevDistance = Big(prev.x1)
|
|
.minus(Big(hipLine.x2))
|
|
.abs()
|
|
.lt(Big(prev.x2).minus(Big(hipLine.x2)).abs())
|
|
? Big(prev.x1).minus(Big(hipLine.x2)).abs()
|
|
: Big(prev.x2).minus(Big(hipLine.x2)).abs()
|
|
const currentDistance = Big(current.x1)
|
|
.minus(Big(hipLine.x2))
|
|
.abs()
|
|
.lt(Big(current.x2).minus(Big(hipLine.x2)).abs())
|
|
? Big(current.x1).minus(Big(hipLine.x2)).abs()
|
|
: Big(current.x2).minus(Big(hipLine.x2)).abs()
|
|
|
|
return prevDistance.lt(currentDistance) ? prev : current
|
|
}, overlapLineX[0])
|
|
|
|
overlapLineY.reduce((prev, current) => {
|
|
const prevDistance = Big(prev.y1)
|
|
.minus(Big(hipLine.y2))
|
|
.abs()
|
|
.lt(Big(prev.y2).minus(Big(hipLine.y2)).abs())
|
|
? Big(prev.y1).minus(Big(hipLine.y2)).abs()
|
|
: Big(prev.y2).minus(Big(hipLine.y2)).abs()
|
|
const currentDistance = Big(current.y1)
|
|
.minus(Big(hipLine.y2))
|
|
.abs()
|
|
.lt(Big(current.y2).minus(Big(hipLine.y2)).abs())
|
|
? Big(current.y1).minus(Big(hipLine.y2)).abs()
|
|
: Big(current.y2).minus(Big(hipLine.y2)).abs()
|
|
return prevDistance.lt(currentDistance) ? prev : current
|
|
}, overlapLineY[0])
|
|
|
|
if (overlapLineX.length > 0) {
|
|
const overlapLine = overlapLineX[0]
|
|
const maxX = Math.max(overlapLine.x1, overlapLine.x2)
|
|
const point = [hipLine.x2, hipLine.y2, maxX, hipLine.y2]
|
|
const addLine = drawHipLine(point, canvas, roof, textMode, null, prevDegree, currentDegree)
|
|
baseHipLines.push({ x1: point[0], y1: point[1], x2: point[2], y2: point[3], line: addLine })
|
|
}
|
|
if (overlapLineY.length > 0) {
|
|
const overlapLine = overlapLineY[0]
|
|
const maxY = Math.max(overlapLine.y1, overlapLine.y2)
|
|
const point = [hipLine.x2, hipLine.y2, hipLine.x2, maxY]
|
|
const addLine = drawHipLine(point, canvas, roof, textMode, null, prevDegree, currentDegree)
|
|
baseHipLines.push({ x1: point[0], y1: point[1], x2: point[2], y2: point[3], line: addLine })
|
|
}
|
|
}
|
|
|
|
const modifiedBaseLine = baseLines.filter((line) => (hip.x2 === line.x1 && hip.y2 === line.y1) || (hip.x2 === line.x2 && hip.y2 === line.y2))
|
|
if (modifiedBaseLine.length === 0) return
|
|
const verticalLine = modifiedBaseLine.find(
|
|
(line) =>
|
|
(hip.x2 === line.attributes.originPoint.x1 && hip.x2 === line.attributes.originPoint.x2) ||
|
|
(hip.y2 === line.attributes.originPoint.y1 && hip.y2 === line.attributes.originPoint.y2),
|
|
)
|
|
const horizonLine = modifiedBaseLine.find((line) => line !== verticalLine)
|
|
|
|
const horizonRoof = roof.lines.find((line) => {
|
|
const originPoint = horizonLine.attributes.originPoint
|
|
if (originPoint.y1 === originPoint.y2) {
|
|
return (
|
|
line.y1 === line.y2 &&
|
|
Math.sign(originPoint.x1 - originPoint.x2) === Math.sign(line.x1 - line.x2) &&
|
|
Big(originPoint.y1).minus(Big(line.y1)).abs().minus(horizonLine.attributes.offset).abs().lt(1)
|
|
)
|
|
} else {
|
|
return (
|
|
line.x1 === line.x2 &&
|
|
Math.sign(originPoint.y1 - originPoint.y2) === Math.sign(line.y1 - line.y2) &&
|
|
Big(originPoint.x1).minus(Big(line.x1)).abs().minus(horizonLine.attributes.offset).abs().lt(1)
|
|
)
|
|
}
|
|
})
|
|
|
|
if (horizonRoof) {
|
|
let horizonPoint
|
|
if (horizonRoof.y1 === horizonRoof.y2) {
|
|
const minX = Math.min(horizonRoof.x1, horizonRoof.x2, horizonLine.attributes.originPoint.x1, horizonLine.attributes.originPoint.x2)
|
|
const maxX = Math.max(horizonRoof.x1, horizonRoof.x2, horizonLine.attributes.originPoint.x1, horizonLine.attributes.originPoint.x2)
|
|
horizonPoint = [minX, horizonRoof.y1, maxX, horizonRoof.y1]
|
|
} else {
|
|
const minY = Math.min(horizonRoof.y1, horizonRoof.y2, horizonLine.attributes.originPoint.y1, horizonLine.attributes.originPoint.y2)
|
|
const maxY = Math.max(horizonRoof.y1, horizonRoof.y2, horizonLine.attributes.originPoint.y1, horizonLine.attributes.originPoint.y2)
|
|
horizonPoint = [horizonRoof.x1, minY, horizonRoof.x1, maxY]
|
|
}
|
|
let addLine
|
|
const alreadyHorizonLines = baseHipLines.find(
|
|
(hipLine) =>
|
|
hipLine.x1 === horizonPoint[0] && hipLine.y1 === horizonPoint[1] && hipLine.x2 === horizonPoint[2] && hipLine.y2 === horizonPoint[3],
|
|
)
|
|
if (!alreadyHorizonLines) {
|
|
addLine = drawHipLine(horizonPoint, canvas, roof, textMode, null, prevDegree, currentDegree)
|
|
baseHipLines.push({
|
|
x1: horizonPoint[0],
|
|
y1: horizonPoint[1],
|
|
x2: horizonPoint[2],
|
|
y2: horizonPoint[3],
|
|
line: addLine,
|
|
})
|
|
} else {
|
|
addLine = alreadyHorizonLines
|
|
}
|
|
|
|
let verticalPoint
|
|
if (addLine.y1 === addLine.y2) {
|
|
verticalPoint = [hip.x2, hip.y2, hip.x2, addLine.y1]
|
|
} else {
|
|
verticalPoint = [hip.x2, hip.y2, addLine.x1, hip.y2]
|
|
}
|
|
const alreadyVerticalLine = baseHipLines.find(
|
|
(hipLine) =>
|
|
hipLine.x1 === verticalPoint[0] && hipLine.y1 === verticalPoint[1] && hipLine.x2 === verticalPoint[2] && hipLine.y2 === verticalPoint[3],
|
|
)
|
|
if (!alreadyVerticalLine) {
|
|
addLine = drawHipLine(verticalPoint, canvas, roof, textMode, null, prevDegree, currentDegree)
|
|
baseHipLines.push({
|
|
x1: verticalPoint[0],
|
|
y1: verticalPoint[1],
|
|
x2: verticalPoint[2],
|
|
y2: verticalPoint[3],
|
|
line: addLine,
|
|
})
|
|
}
|
|
}
|
|
})
|
|
|
|
ridgeAllPoints.forEach((current) => {
|
|
ridgeAllPoints
|
|
.filter((point) => point !== current)
|
|
.forEach((point) => {
|
|
let checkRidgeLine, checkHipLine
|
|
/** 직선인 경우 마루 확인*/
|
|
if (
|
|
baseRidgeCount < getMaxRidge(baseLines.length) &&
|
|
((point.x === current.x && point.y !== current.y) || (point.x !== current.x && point.y === current.y))
|
|
) {
|
|
checkRidgeLine = { x1: current.x, y1: current.y, x2: point.x, y2: point.y }
|
|
}
|
|
/** 대각선인 경우 hip확인*/
|
|
const hipX = Big(current.x).minus(Big(point.x)).abs()
|
|
const hipY = Big(current.y).minus(Big(point.y)).abs()
|
|
if (hipX.eq(hipY) && hipX.gt(0) && hipY.gt(0)) {
|
|
checkHipLine = { x1: current.x, y1: current.y, x2: point.x, y2: point.y }
|
|
}
|
|
|
|
if (checkRidgeLine) {
|
|
const ridgePoints = [checkRidgeLine.x1, checkRidgeLine.y1, checkRidgeLine.x2, checkRidgeLine.y2]
|
|
|
|
let baseIntersection = false
|
|
const ridgeInterSection = []
|
|
const ridgeEdge = {
|
|
vertex1: { x: ridgePoints[0], y: ridgePoints[1] },
|
|
vertex2: { x: ridgePoints[2], y: ridgePoints[3] },
|
|
}
|
|
baseLines.forEach((line) => {
|
|
const intersection = edgesIntersection(ridgeEdge, {
|
|
vertex1: { x: line.x1, y: line.y1 },
|
|
vertex2: { x: line.x2, y: line.y2 },
|
|
})
|
|
if (intersection && !intersection.isIntersectionOutside) {
|
|
ridgeInterSection.push(intersection)
|
|
}
|
|
})
|
|
baseRidgeLines.forEach((line) => {
|
|
const intersection = edgesIntersection(ridgeEdge, {
|
|
vertex1: { x: line.x1, y: line.y1 },
|
|
vertex2: { x: line.x2, y: line.y2 },
|
|
})
|
|
if (intersection && !intersection.isIntersectionOutside) {
|
|
ridgeInterSection.push(intersection)
|
|
}
|
|
})
|
|
baseHipLines.forEach((line) => {
|
|
const intersection = edgesIntersection(ridgeEdge, {
|
|
vertex1: { x: line.x1, y: line.y1 },
|
|
vertex2: { x: line.x2, y: line.y2 },
|
|
})
|
|
if (intersection && !intersection.isIntersectionOutside) {
|
|
ridgeInterSection.push(intersection)
|
|
}
|
|
})
|
|
const otherRidgeInterSection = ridgeInterSection.filter(
|
|
(intersection) =>
|
|
!(
|
|
(intersection.x === ridgePoints[0] && intersection.y === ridgePoints[1]) ||
|
|
(intersection.x === ridgePoints[2] && intersection.y === ridgePoints[3])
|
|
),
|
|
)
|
|
const alreadyRidges = baseRidgeLines.filter(
|
|
(line) =>
|
|
(line.x1 === ridgePoints[0] && line.y1 === ridgePoints[1] && line.x2 === ridgePoints[2] && line.y2 === ridgePoints[3]) ||
|
|
(line.x1 === ridgePoints[2] && line.y1 === ridgePoints[3] && line.x2 === ridgePoints[0] && line.y2 === ridgePoints[1]),
|
|
)
|
|
|
|
if (
|
|
!baseIntersection &&
|
|
alreadyRidges.length === 0 &&
|
|
otherRidgeInterSection.length === 0 &&
|
|
baseRidgeCount < getMaxRidge(baseLines.length)
|
|
) {
|
|
baseRidgeCount = baseRidgeCount + 1
|
|
const ridgeLine = drawRidgeLine(ridgePoints, canvas, roof, textMode)
|
|
baseRidgeLines.push(ridgeLine)
|
|
}
|
|
}
|
|
if (checkHipLine) {
|
|
const hipPoints = [checkHipLine.x1, checkHipLine.y1, checkHipLine.x2, checkHipLine.y2]
|
|
|
|
let baseIntersection = false
|
|
let hipInterSection = []
|
|
const hipEdge = {
|
|
vertex1: { x: hipPoints[0], y: hipPoints[1] },
|
|
vertex2: { x: hipPoints[2], y: hipPoints[3] },
|
|
}
|
|
baseLines.forEach((line) => {
|
|
const intersection = edgesIntersection(hipEdge, {
|
|
vertex1: { x: line.x1, y: line.y1 },
|
|
vertex2: { x: line.x2, y: line.y2 },
|
|
})
|
|
if (intersection && !intersection.isIntersectionOutside && eavesType.includes(line.attributes.type)) {
|
|
baseIntersection = true
|
|
}
|
|
})
|
|
baseRidgeLines.forEach((line) => {
|
|
const intersection = edgesIntersection(hipEdge, {
|
|
vertex1: { x: line.x1, y: line.y1 },
|
|
vertex2: { x: line.x2, y: line.y2 },
|
|
})
|
|
if (intersection && !intersection.isIntersectionOutside) {
|
|
hipInterSection.push(intersection)
|
|
}
|
|
})
|
|
baseHipLines.forEach((line) => {
|
|
const intersection = edgesIntersection(hipEdge, {
|
|
vertex1: { x: line.x1, y: line.y1 },
|
|
vertex2: { x: line.x2, y: line.y2 },
|
|
})
|
|
if (intersection && !intersection.isIntersectionOutside) {
|
|
hipInterSection.push(intersection)
|
|
}
|
|
})
|
|
const otherHipInterSection = hipInterSection.filter(
|
|
(intersection) =>
|
|
!(
|
|
(intersection.x === hipPoints[0] && intersection.y === hipPoints[1]) ||
|
|
(intersection.x === hipPoints[2] && intersection.y === hipPoints[3])
|
|
),
|
|
)
|
|
const alreadyHips = baseHipLines.filter(
|
|
(line) =>
|
|
(line.x1 === hipPoints[0] && line.y1 === hipPoints[1] && line.x2 === hipPoints[2] && line.y2 === hipPoints[3]) ||
|
|
(line.x1 === hipPoints[2] && line.y1 === hipPoints[3] && line.x2 === hipPoints[0] && line.y2 === hipPoints[1]),
|
|
)
|
|
|
|
if (!baseIntersection && alreadyHips.length === 0 && otherHipInterSection.length === 0) {
|
|
const hipLine = drawHipLine(hipPoints, canvas, roof, textMode, null, prevDegree, currentDegree)
|
|
baseHipLines.push({ x1: hipPoints[0], y1: hipPoints[1], x2: hipPoints[2], y2: hipPoints[3], line: hipLine })
|
|
}
|
|
}
|
|
})
|
|
})
|
|
|
|
/** 중복 제거 */
|
|
baseHipLines.forEach((hipLine) => {
|
|
baseHipLines.filter((hipLine2) => hipLine !== hipLine2).forEach((hipLine2) => {})
|
|
})
|
|
|
|
const innerLines = [...baseRidgeLines, ...baseGableRidgeLines, ...baseGableLines, ...baseHipLines.map((line) => line.line)]
|
|
const uniqueInnerLines = []
|
|
|
|
innerLines.forEach((currentLine) => {
|
|
if (currentLine.length === 0) {
|
|
canvas.remove(currentLine)
|
|
} else {
|
|
const sameLines = uniqueInnerLines.filter(
|
|
(line) =>
|
|
line !== currentLine &&
|
|
((line.x1 === currentLine.x1 && line.y1 === currentLine.y1 && line.x2 === currentLine.x2 && line.y2 === currentLine.y2) ||
|
|
(line.x1 === currentLine.x2 && line.y1 === currentLine.y2 && line.x2 === currentLine.x1 && line.y2 === currentLine.y1)),
|
|
)
|
|
|
|
if (sameLines.length === 0) {
|
|
uniqueInnerLines.push(currentLine)
|
|
} else {
|
|
canvas.remove(currentLine)
|
|
}
|
|
}
|
|
})
|
|
canvas.renderAll()
|
|
roof.innerLines = uniqueInnerLines
|
|
|
|
/*drawRidge(roof, canvas, textMode)
|
|
drawHips(roof, canvas, textMode)
|
|
connectLinePoint(roof, canvas, textMode)
|
|
modifyRidge(roof, canvas, textMode)
|
|
drawCenterLine(roof, canvas, textMode)*/
|
|
}
|
|
|
|
/**
|
|
* 추녀 마루를 그린다.
|
|
* @param points
|
|
* @param canvas
|
|
* @param roof
|
|
* @param textMode
|
|
* @param currentRoof
|
|
* @param prevDegree
|
|
* @param currentDegree
|
|
*/
|
|
const drawHipLine = (points, canvas, roof, textMode, currentRoof, prevDegree, currentDegree) => {
|
|
const hip = new QLine(points, {
|
|
parentId: roof.id,
|
|
fontSize: roof.fontSize,
|
|
stroke: '#1083E3',
|
|
strokeWidth: 2,
|
|
name: LINE_TYPE.SUBLINE.HIP,
|
|
textMode: textMode,
|
|
attributes: {
|
|
roofId: roof.id,
|
|
// currentRoofId: currentRoof.id,
|
|
planeSize: calcLinePlaneSize({
|
|
x1: points[0],
|
|
y1: points[1],
|
|
x2: points[2],
|
|
y2: points[3],
|
|
}),
|
|
actualSize:
|
|
prevDegree === currentDegree
|
|
? calcLineActualSize(
|
|
{
|
|
x1: points[0],
|
|
y1: points[1],
|
|
x2: points[2],
|
|
y2: points[3],
|
|
},
|
|
currentDegree,
|
|
)
|
|
: 0,
|
|
},
|
|
})
|
|
|
|
canvas.add(hip)
|
|
hip.bringToFront()
|
|
canvas.renderAll()
|
|
return hip
|
|
}
|
|
|
|
/**
|
|
* 라인의 흐름 방향에서 마주치는 지붕선의 포인트를 찾는다.
|
|
* @param roof
|
|
* @param baseLine
|
|
* @param endPoint
|
|
* @returns {*}
|
|
*/
|
|
const findRoofIntersection = (roof, baseLine, endPoint) => {
|
|
let intersectPoints = []
|
|
const { x1, y1, x2, y2 } = baseLine
|
|
|
|
const baseEdge = {
|
|
vertex1: { x: x1, y: y1 },
|
|
vertex2: { x: x2, y: y2 },
|
|
}
|
|
|
|
/** 외벽선에서 라인 겹치는 경우에 대한 확인*/
|
|
roof.lines.forEach((line) => {
|
|
const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
|
|
const intersection = edgesIntersection(baseEdge, lineEdge)
|
|
if (
|
|
intersection &&
|
|
!intersection.isIntersectionOutside &&
|
|
Math.sign(endPoint.x - baseLine.x1) === Math.sign(endPoint.x - intersection.x) &&
|
|
Math.sign(endPoint.y - baseLine.y1) === Math.sign(endPoint.y - intersection.y)
|
|
) {
|
|
const intersectSize = endPoint.x
|
|
.minus(Big(intersection.x))
|
|
.pow(2)
|
|
.plus(endPoint.y.minus(Big(intersection.y)).pow(2))
|
|
.abs()
|
|
.sqrt()
|
|
.toNumber()
|
|
|
|
intersectPoints.push({
|
|
intersection,
|
|
size: intersectSize,
|
|
})
|
|
}
|
|
})
|
|
|
|
return intersectPoints.reduce((prev, current) => {
|
|
return prev.size < current.size ? prev : current
|
|
}, intersectPoints[0])
|
|
}
|
|
|
|
/**
|
|
* 마루를 그린다.
|
|
* @param points
|
|
* @param canvas
|
|
* @param roof
|
|
* @param textMode
|
|
* @returns {*}
|
|
*/
|
|
const drawRidgeLine = (points, canvas, roof, textMode) => {
|
|
const ridge = new QLine(points, {
|
|
parentId: roof.id,
|
|
fontSize: roof.fontSize,
|
|
stroke: '#1083E3',
|
|
strokeWidth: 2,
|
|
name: LINE_TYPE.SUBLINE.RIDGE,
|
|
textMode: textMode,
|
|
attributes: {
|
|
roofId: roof.id,
|
|
planeSize: calcLinePlaneSize({
|
|
x1: points[0],
|
|
y1: points[1],
|
|
x2: points[2],
|
|
y2: points[3],
|
|
}),
|
|
actualSize: calcLinePlaneSize({
|
|
x1: points[0],
|
|
y1: points[1],
|
|
x2: points[2],
|
|
y2: points[3],
|
|
}),
|
|
},
|
|
})
|
|
canvas.add(ridge)
|
|
ridge.bringToFront()
|
|
canvas.renderAll()
|
|
|
|
return ridge
|
|
}
|
|
|
|
/**
|
|
* 지붕선을 그린다.
|
|
* @param points
|
|
* @param canvas
|
|
* @param roof
|
|
* @param textMode
|
|
* @returns {*}
|
|
*/
|
|
const drawRoofLine = (points, canvas, roof, textMode) => {
|
|
const ridge = new QLine(points, {
|
|
parentId: roof.id,
|
|
fontSize: roof.fontSize,
|
|
stroke: '#1083E3',
|
|
strokeWidth: 2,
|
|
name: LINE_TYPE.SUBLINE.HIP,
|
|
textMode: textMode,
|
|
attributes: {
|
|
roofId: roof.id,
|
|
planeSize: calcLinePlaneSize({
|
|
x1: points[0],
|
|
y1: points[1],
|
|
x2: points[2],
|
|
y2: points[3],
|
|
}),
|
|
actualSize: calcLinePlaneSize({
|
|
x1: points[0],
|
|
y1: points[1],
|
|
x2: points[2],
|
|
y2: points[3],
|
|
}),
|
|
},
|
|
})
|
|
canvas.add(ridge)
|
|
ridge.bringToFront()
|
|
canvas.renderAll()
|
|
|
|
return ridge
|
|
}
|
|
|
|
/**
|
|
* 벡터를 정규화(Normalization)하는 함수
|
|
* @param v
|
|
* @returns {{x: *, y: *}|{x: number, y: number}}
|
|
*/
|
|
const normalizeVector = (v) => {
|
|
/** 벡터의 크기(길이)*/
|
|
const magnitude = Big(v.x).pow(2).plus(Big(v.y).pow(2)).sqrt()
|
|
if (magnitude.eq(0)) return { x: 0, y: 0 } // 크기가 0일 경우 (예외 처리)
|
|
return { x: Big(v.x).div(magnitude).toNumber(), y: Big(v.y).div(magnitude).toNumber() }
|
|
}
|
|
|
|
/**
|
|
* 사잇각의 절반 방향 벡터 계산 함수
|
|
* @param line1
|
|
* @param line2
|
|
* @returns {{x: *, y: *}|{x: number, y: number}}
|
|
*/
|
|
const getHalfAngleVector = (line1, line2) => {
|
|
const v1 = { x: Big(line1.x2).minus(Big(line1.x1)).toNumber(), y: Big(line1.y1).minus(Big(line1.y2)).toNumber() }
|
|
const v2 = { x: Big(line2.x2).minus(Big(line2.x1)).toNumber(), y: Big(line2.y1).minus(Big(line2.y2)).toNumber() }
|
|
|
|
/**
|
|
* 벡터 정규화
|
|
* @type {{x: *, y: *}|{x: number, y: number}}
|
|
*/
|
|
const unitV1 = normalizeVector(v1) // 첫 번째 벡터를 정규화
|
|
const unitV2 = normalizeVector(v2) // 두 번째 벡터를 정규화
|
|
|
|
/**
|
|
* 두 벡터를 더합니다
|
|
* @type {{x: *, y: *}}
|
|
*/
|
|
const summedVector = {
|
|
x: Big(unitV1.x).plus(Big(unitV2.x)).toNumber(),
|
|
y: Big(unitV1.y).plus(Big(unitV2.y)).toNumber(),
|
|
}
|
|
|
|
/** 결과 벡터를 정규화하여 사잇각 벡터를 반환합니다 */
|
|
return normalizeVector(summedVector)
|
|
}
|
|
|
|
/**
|
|
* 두 선분이 겹치는지 확인
|
|
* @param line1
|
|
* @param line2
|
|
* @returns {boolean}
|
|
*/
|
|
export 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
|
|
}
|
|
|
|
/**
|
|
* 외벽선 속성에 따라서 모양을 수정한다.
|
|
* @param roof
|
|
* @param canvas
|
|
* @param textMode
|
|
*/
|
|
const modifyRidge = (roof, canvas, textMode) => {
|
|
const ridgeLines = canvas?.getObjects().filter((object) => object.name === LINE_TYPE.SUBLINE.RIDGE && object.attributes.roofId === roof.id)
|
|
const hipLines = canvas?.getObjects().filter((object) => object.name === LINE_TYPE.SUBLINE.HIP && object.attributes.roofId === roof.id)
|
|
|
|
ridgeLines.forEach((ridge) => {
|
|
let ridgeHip1 = hipLines.filter((hip) => hip.x2 === ridge.x1 && hip.y2 === ridge.y1)
|
|
let ridgeHip2 = hipLines.filter((hip) => hip.x2 === ridge.x2 && hip.y2 === ridge.y2)
|
|
if (ridgeHip1.length >= 2) {
|
|
let currentRoof = roof.lines
|
|
.filter((roofLine) => roofLine.attributes !== undefined && roofLine.attributes.ridgeCoordinate !== undefined)
|
|
.find((roofLine) => roofLine.attributes.ridgeCoordinate.x1 === ridge.x1 && roofLine.attributes.ridgeCoordinate.y1 === ridge.y1)
|
|
if (currentRoof === undefined) {
|
|
currentRoof = roof.lines.find(
|
|
(roofLine) =>
|
|
(roofLine.x1 === ridgeHip1[0].x1 &&
|
|
roofLine.y1 === ridgeHip1[0].y1 &&
|
|
roofLine.x2 === ridgeHip1[1].x1 &&
|
|
roofLine.y2 === ridgeHip1[1].y1) ||
|
|
(roofLine.x1 === ridgeHip1[1].x1 &&
|
|
roofLine.y1 === ridgeHip1[1].y1 &&
|
|
roofLine.x2 === ridgeHip1[0].x1 &&
|
|
roofLine.y2 === ridgeHip1[0].y1),
|
|
)
|
|
if (currentRoof !== undefined) {
|
|
currentRoof.attributes.ridgeCoordinate = { x1: ridge.x1, y1: ridge.y1 }
|
|
}
|
|
}
|
|
if (currentRoof !== undefined) {
|
|
switch (currentRoof.attributes.type) {
|
|
case LINE_TYPE.WALLLINE.EAVES:
|
|
changeEavesRoof(currentRoof, canvas, textMode)
|
|
break
|
|
case LINE_TYPE.WALLLINE.GABLE:
|
|
changeGableRoof(currentRoof, canvas, textMode)
|
|
break
|
|
case LINE_TYPE.WALLLINE.HIPANDGABLE:
|
|
changeHipAndGableRoof(currentRoof, canvas, textMode)
|
|
break
|
|
case LINE_TYPE.WALLLINE.JERKINHEAD:
|
|
changeJerkInHeadRoof(currentRoof, canvas, textMode)
|
|
break
|
|
case LINE_TYPE.WALLLINE.WALL:
|
|
changeWallRoof(currentRoof, canvas, textMode)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
if (ridgeHip2.length >= 2) {
|
|
let currentRoof = roof.lines
|
|
.filter((roofLine) => roofLine.attributes !== undefined && roofLine.attributes.ridgeCoordinate !== undefined)
|
|
.find((roofLine) => roofLine.attributes.ridgeCoordinate.x1 === ridge.x2 && roofLine.attributes.ridgeCoordinate.y1 === ridge.y2)
|
|
if (currentRoof === undefined) {
|
|
currentRoof = roof.lines.find(
|
|
(roofLine) =>
|
|
(roofLine.x1 === ridgeHip2[0].x1 &&
|
|
roofLine.y1 === ridgeHip2[0].y1 &&
|
|
roofLine.x2 === ridgeHip2[1].x1 &&
|
|
roofLine.y2 === ridgeHip2[1].y1) ||
|
|
(roofLine.x1 === ridgeHip2[1].x1 &&
|
|
roofLine.y1 === ridgeHip2[1].y1 &&
|
|
roofLine.x2 === ridgeHip2[0].x1 &&
|
|
roofLine.y2 === ridgeHip2[0].y1),
|
|
)
|
|
if (currentRoof !== undefined) {
|
|
currentRoof.attributes.ridgeCoordinate = { x1: ridge.x2, y1: ridge.y2 }
|
|
}
|
|
}
|
|
if (currentRoof !== undefined) {
|
|
switch (currentRoof.attributes.type) {
|
|
case LINE_TYPE.WALLLINE.EAVES:
|
|
changeEavesRoof(currentRoof, canvas, textMode)
|
|
break
|
|
case LINE_TYPE.WALLLINE.GABLE:
|
|
changeGableRoof(currentRoof, canvas, textMode)
|
|
break
|
|
case LINE_TYPE.WALLLINE.HIPANDGABLE:
|
|
changeHipAndGableRoof(currentRoof, canvas, textMode)
|
|
break
|
|
case LINE_TYPE.WALLLINE.JERKINHEAD:
|
|
changeJerkInHeadRoof(currentRoof, canvas, textMode)
|
|
break
|
|
case LINE_TYPE.WALLLINE.WALL:
|
|
changeWallRoof(currentRoof, canvas, textMode)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
/*
|
|
최대 생성 마루 갯수
|
|
*/
|
|
const getMaxRidge = (length) => {
|
|
return (length - 4) / 2 + 1
|
|
}
|
|
|
|
/**
|
|
* 처마지붕으로 변경
|
|
* @param currentRoof
|
|
* @param canvas
|
|
* @param textMode
|
|
*/
|
|
const changeEavesRoof = (currentRoof, canvas, textMode) => {
|
|
if (currentRoof.attributes.type === LINE_TYPE.WALLLINE.EAVES) {
|
|
const roofId = currentRoof.attributes.roofId
|
|
const wall = canvas?.getObjects().find((object) => object.name === POLYGON_TYPE.WALL && object.attributes.roofId === roofId)
|
|
const roof = canvas?.getObjects().find((object) => object.name === POLYGON_TYPE.ROOF && object.id === roofId)
|
|
let hipLines = canvas?.getObjects().filter((object) => object.name === LINE_TYPE.SUBLINE.HIP && object.attributes.roofId === roofId)
|
|
let ridgeLines = canvas?.getObjects().filter((object) => object.name === LINE_TYPE.SUBLINE.RIDGE && object.attributes.roofId === roofId)
|
|
let wallLine = wall.lines.filter((w) => w.id === currentRoof.attributes.wallLine)
|
|
if (wallLine.length > 0) {
|
|
wallLine = wallLine[0]
|
|
}
|
|
let prevRoof, nextRoof
|
|
roof.lines.forEach((r, index) => {
|
|
if (r.id === currentRoof.id) {
|
|
currentRoof = r
|
|
prevRoof = roof.lines[index === 0 ? roof.lines.length - 1 : index - 1]
|
|
nextRoof = roof.lines[index === roof.lines.length - 1 ? 0 : index + 1]
|
|
}
|
|
})
|
|
|
|
const midX = Big(currentRoof.x1).plus(Big(currentRoof.x2)).div(2) // 지붕의 X 중심
|
|
const midY = Big(currentRoof.y1).plus(Big(currentRoof.y2)).div(2) // 지붕의 Y 중심
|
|
const midWallX = Big(wallLine.x1).plus(Big(wallLine.x2)).div(2)
|
|
const midWallY = Big(wallLine.y1).plus(Big(wallLine.y2)).div(2) // 벽의 Y 중심
|
|
const alpha = midX.minus(midWallX) // 벽과 지붕의 X 거리
|
|
const beta = midY.minus(midWallY) // 벽과 지붕의 Y 거리
|
|
const hypotenuse = alpha.pow(2).plus(beta.pow(2)).sqrt() // 벽과 지붕의 거리
|
|
const hipX2 = midX.plus(alpha.div(hypotenuse).times(Big(currentRoof.length).div(2)).neg())
|
|
const hipY2 = midY.plus(beta.div(hypotenuse).times(Big(currentRoof.length).div(2)).neg())
|
|
|
|
const innerLines = canvas
|
|
?.getObjects()
|
|
.filter(
|
|
(object) =>
|
|
object.attributes?.roofId === roofId &&
|
|
object.attributes?.currentRoofId === currentRoof.id &&
|
|
object.x1 !== undefined &&
|
|
object.x2 !== undefined,
|
|
)
|
|
|
|
innerLines
|
|
.filter(
|
|
(line) =>
|
|
line.name !== LINE_TYPE.SUBLINE.RIDGE &&
|
|
line.name !== LINE_TYPE.SUBLINE.HIP &&
|
|
line.name !== LINE_TYPE.SUBLINE.VALLEY &&
|
|
line.name !== OUTER_LINE_TYPE.OUTER_LINE &&
|
|
line.name !== 'wallLine',
|
|
)
|
|
.forEach((line) => {
|
|
roof.innerLines = roof.innerLines.filter((l) => l.id !== line.id)
|
|
canvas?.remove(line)
|
|
})
|
|
|
|
ridgeLines = ridgeLines.filter(
|
|
(ridge) =>
|
|
(ridge.x1 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y1 === currentRoof.attributes.ridgeCoordinate.y1) ||
|
|
(ridge.x2 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y2 === currentRoof.attributes.ridgeCoordinate.y1),
|
|
)
|
|
hipLines = hipLines.filter(
|
|
(hip) => (hip.x1 === currentRoof.x1 && hip.y1 === currentRoof.y1) || (hip.x1 === currentRoof.x2 && hip.y1 === currentRoof.y2),
|
|
)
|
|
|
|
if (hipLines === undefined || hipLines.length === 0) {
|
|
hipLines = innerLines.filter(
|
|
(line) =>
|
|
(line.x1 === currentRoof.x1 && line.y1 === currentRoof.y1) ||
|
|
(line.x2 === currentRoof.x1 && line.y2 === currentRoof.y1) ||
|
|
(line.x1 === currentRoof.x2 && line.y1 === currentRoof.y2) ||
|
|
(line.x2 === currentRoof.x2 && line.y2 === currentRoof.y2),
|
|
)
|
|
}
|
|
|
|
if ((ridgeLines === undefined || ridgeLines.length === 0) && hipLines.length >= 2) {
|
|
let points = []
|
|
hipLines.forEach((hip) => {
|
|
points.push({ x: hip.x1, y: hip.y1 })
|
|
points.push({ x: hip.x2, y: hip.y2 })
|
|
})
|
|
|
|
const pointSet = new Set()
|
|
const duplicatePoints = []
|
|
|
|
points.forEach((point) => {
|
|
const pointKey = `${point.x},${point.y}`
|
|
if (pointSet.has(pointKey)) {
|
|
duplicatePoints.push(point)
|
|
} else {
|
|
pointSet.add(pointKey)
|
|
}
|
|
})
|
|
|
|
ridgeLines = innerLines
|
|
.filter((r) => r !== hipLines[0] && r !== hipLines[1])
|
|
.filter(
|
|
(r) => (r.x1 === duplicatePoints[0].x && r.y1 === duplicatePoints[0].y) || (r.x2 === duplicatePoints[0].x && r.y2 === duplicatePoints[0].y),
|
|
)
|
|
if (ridgeLines.length > 0) {
|
|
currentRoof.attributes.ridgeCoordinate = { x1: duplicatePoints[0].x, y1: duplicatePoints[0].y }
|
|
}
|
|
}
|
|
|
|
if (ridgeLines.length > 0) {
|
|
const ridge = ridgeLines[0]
|
|
if (ridge.x1 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y1 === currentRoof.attributes.ridgeCoordinate.y1) {
|
|
ridge.set({
|
|
x1: hipX2.toNumber(),
|
|
y1: hipY2.toNumber(),
|
|
x2: ridge.x2,
|
|
y2: ridge.y2,
|
|
})
|
|
currentRoof.attributes.ridgeCoordinate = { x1: ridge.x1, y1: ridge.y1 }
|
|
}
|
|
if (ridge.x2 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y2 === currentRoof.attributes.ridgeCoordinate.y1) {
|
|
ridge.set({
|
|
x1: ridge.x1,
|
|
y1: ridge.y1,
|
|
x2: hipX2.toNumber(),
|
|
y2: hipY2.toNumber(),
|
|
})
|
|
currentRoof.attributes.ridgeCoordinate = { x1: ridge.x2, y1: ridge.y2 }
|
|
}
|
|
//Math.round(Math.sqrt(Math.pow(ridge.x1 - ridge.x2, 2) + Math.pow(ridge.y1 - ridge.y2, 2)) * 10)
|
|
ridge.attributes.planeSize = calcLinePlaneSize({ x1: ridge.x1, y1: ridge.y1, x2: ridge.x2, y2: ridge.y2 })
|
|
ridge.attributes.actualSize = calcLinePlaneSize({ x1: ridge.x1, y1: ridge.y1, x2: ridge.x2, y2: ridge.y2 })
|
|
}
|
|
|
|
hipLines.forEach((hip) => {
|
|
roof.innerLines = roof.innerLines.filter((h) => h.id !== hip.id)
|
|
canvas.remove(hip)
|
|
})
|
|
|
|
canvas?.renderAll()
|
|
|
|
const prevDegree = prevRoof.attributes.pitch > 0 ? getDegreeByChon(prevRoof.attributes.pitch) : prevRoof.attributes.degree
|
|
const currentDegree = currentRoof.attributes.pitch > 0 ? getDegreeByChon(currentRoof.attributes.pitch) : currentRoof.attributes.degree
|
|
const nextDegree = nextRoof.attributes.pitch > 0 ? getDegreeByChon(nextRoof.attributes.pitch) : nextRoof.attributes.degree
|
|
|
|
const hip1 = new QLine([currentRoof.x1, currentRoof.y1, hipX2.toNumber(), hipY2.toNumber()], {
|
|
parentId: roof.id,
|
|
fontSize: roof.fontSize,
|
|
stroke: '#1083E3',
|
|
strokeWidth: 2,
|
|
name: LINE_TYPE.SUBLINE.HIP,
|
|
textMode: textMode,
|
|
attributes: {
|
|
roofId: roof.id,
|
|
currentRoofId: currentRoof.id,
|
|
planeSize: calcLinePlaneSize({
|
|
x1: currentRoof.x1,
|
|
y1: currentRoof.y1,
|
|
x2: hipX2.toNumber(),
|
|
y2: hipY2.toNumber(),
|
|
}),
|
|
actualSize:
|
|
prevDegree === currentDegree
|
|
? calcLineActualSize(
|
|
{
|
|
x1: currentRoof.x1,
|
|
y1: currentRoof.y1,
|
|
x2: hipX2.toNumber(),
|
|
y2: hipY2.toNumber(),
|
|
},
|
|
currentDegree,
|
|
)
|
|
: 0,
|
|
},
|
|
})
|
|
const hip1Base = ((Math.abs(hip1.x1 - hip1.x2) + Math.abs(hip1.y1 - hip1.y2)) / 2) * 10
|
|
const hip1Height = Math.round(hip1Base / Math.tan(((90 - currentDegree) * Math.PI) / 180))
|
|
canvas?.add(hip1)
|
|
roof.innerLines.push(hip1)
|
|
|
|
const hip2 = new QLine([currentRoof.x2, currentRoof.y2, hipX2.toNumber(), hipY2.toNumber()], {
|
|
parentId: roof.id,
|
|
fontSize: roof.fontSize,
|
|
stroke: '#1083E3',
|
|
strokeWidth: 2,
|
|
name: LINE_TYPE.SUBLINE.HIP,
|
|
textMode: textMode,
|
|
attributes: {
|
|
roofId: roof.id,
|
|
currentRoofId: currentRoof.id,
|
|
planeSize: calcLinePlaneSize({
|
|
x1: currentRoof.x2,
|
|
y1: currentRoof.y2,
|
|
x2: hipX2.toNumber(),
|
|
y2: hipY2.toNumber(),
|
|
}),
|
|
actualSize:
|
|
currentDegree === nextDegree
|
|
? calcLineActualSize(
|
|
{
|
|
x1: currentRoof.x2,
|
|
y1: currentRoof.y2,
|
|
x2: hipX2.toNumber(),
|
|
y2: hipY2.toNumber(),
|
|
},
|
|
currentDegree,
|
|
)
|
|
: 0,
|
|
},
|
|
})
|
|
canvas?.add(hip2)
|
|
roof.innerLines.push(hip2)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 박공지붕으로 변경
|
|
* @param currentRoof
|
|
* @param canvas
|
|
* @param textMode
|
|
*/
|
|
const changeGableRoof = (currentRoof, canvas, textMode) => {
|
|
if (currentRoof.attributes.type === LINE_TYPE.WALLLINE.GABLE) {
|
|
const roofId = currentRoof.attributes.roofId
|
|
const roof = canvas?.getObjects().find((object) => object.name === POLYGON_TYPE.ROOF && object.id === roofId)
|
|
let hipLines = canvas?.getObjects().filter((object) => object.name === LINE_TYPE.SUBLINE.HIP && object.attributes.roofId === roofId)
|
|
let ridgeLines = canvas?.getObjects().filter((object) => object.name === LINE_TYPE.SUBLINE.RIDGE && object.attributes.roofId === roofId)
|
|
|
|
const midX = Big(currentRoof.x1).plus(Big(currentRoof.x2)).div(2) // 지붕의 X 중심
|
|
const midY = Big(currentRoof.y1).plus(Big(currentRoof.y2)).div(2) // 지붕의 Y 중심
|
|
|
|
const innerLines = canvas
|
|
?.getObjects()
|
|
.filter(
|
|
(object) =>
|
|
object.attributes?.roofId === roofId &&
|
|
object.attributes?.currentRoofId === currentRoof.id &&
|
|
object.x1 !== undefined &&
|
|
object.x2 !== undefined,
|
|
)
|
|
|
|
let prevRoof, nextRoof
|
|
roof.lines.forEach((r, index) => {
|
|
if (r.id === currentRoof.id) {
|
|
currentRoof = r
|
|
prevRoof = roof.lines[index === 0 ? roof.lines.length - 1 : index - 1]
|
|
nextRoof = roof.lines[index === roof.lines.length - 1 ? 0 : index + 1]
|
|
}
|
|
})
|
|
|
|
innerLines
|
|
.filter(
|
|
(line) =>
|
|
line.name !== LINE_TYPE.SUBLINE.RIDGE &&
|
|
line.name !== LINE_TYPE.SUBLINE.HIP &&
|
|
line.name !== LINE_TYPE.SUBLINE.VALLEY &&
|
|
line.name !== OUTER_LINE_TYPE.OUTER_LINE &&
|
|
line.name !== 'wallLine',
|
|
)
|
|
.forEach((line) => {
|
|
roof.innerLines = roof.innerLines.filter((l) => l.id !== line.id)
|
|
canvas?.remove(line)
|
|
})
|
|
canvas.renderAll()
|
|
|
|
ridgeLines = ridgeLines.filter(
|
|
(ridge) =>
|
|
(ridge.x1 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y1 === currentRoof.attributes.ridgeCoordinate.y1) ||
|
|
(ridge.x2 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y2 === currentRoof.attributes.ridgeCoordinate.y1),
|
|
)
|
|
hipLines = hipLines.filter(
|
|
(hip) => (hip.x1 === currentRoof.x1 && hip.y1 === currentRoof.y1) || (hip.x1 === currentRoof.x2 && hip.y1 === currentRoof.y2),
|
|
)
|
|
|
|
if (hipLines === undefined || hipLines.length === 0) {
|
|
hipLines = innerLines.filter(
|
|
(line) =>
|
|
(line.x1 === currentRoof.x1 && line.y1 === currentRoof.y1) ||
|
|
(line.x2 === currentRoof.x1 && line.y2 === currentRoof.y1) ||
|
|
(line.x1 === currentRoof.x2 && line.y1 === currentRoof.y2) ||
|
|
(line.x2 === currentRoof.x2 && line.y2 === currentRoof.y2),
|
|
)
|
|
}
|
|
|
|
hipLines.forEach((hip) => {
|
|
roof.innerLines = roof.innerLines.filter((h) => h.id !== hip.id)
|
|
canvas.remove(hip)
|
|
})
|
|
|
|
if ((ridgeLines === undefined || ridgeLines.length === 0) && hipLines.length >= 2) {
|
|
let points = []
|
|
hipLines.forEach((hip) => {
|
|
points.push({ x: hip.x1, y: hip.y1 })
|
|
points.push({ x: hip.x2, y: hip.y2 })
|
|
})
|
|
|
|
const pointSet = new Set()
|
|
const duplicatePoints = []
|
|
|
|
points.forEach((point) => {
|
|
const pointKey = `${point.x},${point.y}`
|
|
if (pointSet.has(pointKey)) {
|
|
duplicatePoints.push(point)
|
|
} else {
|
|
pointSet.add(pointKey)
|
|
}
|
|
})
|
|
|
|
ridgeLines = innerLines
|
|
.filter((r) => r !== hipLines[0] && r !== hipLines[1])
|
|
.filter(
|
|
(r) => (r.x1 === duplicatePoints[0].x && r.y1 === duplicatePoints[0].y) || (r.x2 === duplicatePoints[0].x && r.y2 === duplicatePoints[0].y),
|
|
)
|
|
if (ridgeLines.length > 0) {
|
|
currentRoof.attributes.ridgeCoordinate = { x1: duplicatePoints[0].x, y1: duplicatePoints[0].y }
|
|
}
|
|
}
|
|
|
|
if (ridgeLines !== undefined && ridgeLines.length > 0) {
|
|
const prevDegree = prevRoof.attributes.pitch > 0 ? getDegreeByChon(prevRoof.attributes.pitch) : prevRoof.attributes.degree
|
|
const nextDegree = nextRoof.attributes.pitch > 0 ? getDegreeByChon(nextRoof.attributes.pitch) : nextRoof.attributes.degree
|
|
|
|
const ridge = ridgeLines[0]
|
|
if (ridge.x1 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y1 === currentRoof.attributes.ridgeCoordinate.y1) {
|
|
ridge.set({
|
|
x1: midX.toNumber(),
|
|
y1: midY.toNumber(),
|
|
x2: ridge.x2,
|
|
y2: ridge.y2,
|
|
})
|
|
currentRoof.attributes.ridgeCoordinate = { x1: ridge.x1, y1: ridge.y1 }
|
|
}
|
|
if (ridge.x2 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y2 === currentRoof.attributes.ridgeCoordinate.y1) {
|
|
ridge.set({
|
|
x1: ridge.x1,
|
|
y1: ridge.y1,
|
|
x2: midX.toNumber(),
|
|
y2: midY.toNumber(),
|
|
})
|
|
currentRoof.attributes.ridgeCoordinate = { x1: ridge.x2, y1: ridge.y2 }
|
|
}
|
|
ridge.attributes.planeSize = calcLinePlaneSize({ x1: ridge.x1, y1: ridge.y1, x2: ridge.x2, y2: ridge.y2 })
|
|
ridge.attributes.actualSize = calcLinePlaneSize({ x1: ridge.x1, y1: ridge.y1, x2: ridge.x2, y2: ridge.y2 })
|
|
|
|
let hip1 = new QLine([currentRoof.x1, currentRoof.y1, midX.toNumber(), midY.toNumber()], {
|
|
parentId: roof.id,
|
|
fontSize: roof.fontSize,
|
|
stroke: '#1083E3',
|
|
strokeWidth: 2,
|
|
name: LINE_TYPE.SUBLINE.HIP,
|
|
textMode: textMode,
|
|
attributes: {
|
|
roofId: roofId,
|
|
currentRoofId: currentRoof.id,
|
|
planeSize: calcLinePlaneSize({
|
|
x1: currentRoof.x1,
|
|
y1: currentRoof.y1,
|
|
x2: midX.toNumber(),
|
|
y2: midY.toNumber(),
|
|
}),
|
|
actualSize: calcLineActualSize(
|
|
{
|
|
x1: currentRoof.x1,
|
|
y1: currentRoof.y1,
|
|
x2: midX.toNumber(),
|
|
y2: midY.toNumber(),
|
|
},
|
|
prevDegree,
|
|
),
|
|
},
|
|
})
|
|
canvas?.add(hip1)
|
|
// const hip1Base = ((Math.abs(hip1.x1 - hip1.x2) + Math.abs(hip1.y1 - hip1.y2)) / 2) * 10
|
|
// const hip1Height = Math.round(hip1Base / Math.tan(((90 - prevDegree) * Math.PI) / 180))
|
|
// hip1.attributes.planeSize = Math.round(Math.sqrt(Math.pow(hip1.x1 - hip1.x2, 2) + Math.pow(hip1.y1 - hip1.y2, 2))) * 10
|
|
// hip1.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip1.attributes.planeSize, 2) + Math.pow(hip1Height, 2)))
|
|
roof.innerLines.push(hip1)
|
|
|
|
let hip2 = new QLine([currentRoof.x2, currentRoof.y2, midX.toNumber(), midY.toNumber()], {
|
|
parentId: roof.id,
|
|
fontSize: roof.fontSize,
|
|
stroke: '#1083E3',
|
|
strokeWidth: 2,
|
|
name: LINE_TYPE.SUBLINE.HIP,
|
|
attributes: {
|
|
roofId: roofId,
|
|
currentRoofId: currentRoof.id,
|
|
planeSize: calcLinePlaneSize({
|
|
x1: currentRoof.x2,
|
|
y1: currentRoof.y2,
|
|
x2: midX.toNumber(),
|
|
y2: midY.toNumber(),
|
|
}),
|
|
actualSize: calcLineActualSize(
|
|
{
|
|
x1: currentRoof.x2,
|
|
y1: currentRoof.y2,
|
|
x2: midX.toNumber(),
|
|
y2: midY.toNumber(),
|
|
},
|
|
nextDegree,
|
|
),
|
|
},
|
|
})
|
|
canvas?.add(hip2)
|
|
// const hip2Base = ((Math.abs(hip2.x1 - hip2.x2) + Math.abs(hip2.y1 - hip2.y2)) / 2) * 10
|
|
// const hip2Height = Math.round(hip2Base / Math.tan(((90 - nextDegree) * Math.PI) / 180))
|
|
// hip2.attributes.planeSize = Math.round(Math.sqrt(Math.pow(hip2.x1 - hip2.x2, 2) + Math.pow(hip2.y1 - hip2.y2, 2))) * 10
|
|
// hip2.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip2.attributes.planeSize, 2) + Math.pow(hip2Height, 2)))
|
|
roof.innerLines.push(hip2)
|
|
canvas?.renderAll()
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 팔작지붕으로 변경
|
|
* @param currentRoof
|
|
* @param canvas
|
|
* @param textMode
|
|
*/
|
|
const changeHipAndGableRoof = (currentRoof, canvas, textMode) => {
|
|
if (
|
|
currentRoof.attributes.type === LINE_TYPE.WALLLINE.HIPANDGABLE &&
|
|
currentRoof.attributes.width !== undefined &&
|
|
currentRoof.attributes.width > 0
|
|
) {
|
|
const roofId = currentRoof.attributes.roofId
|
|
const wall = canvas?.getObjects().find((object) => object.name === POLYGON_TYPE.WALL && object.attributes.roofId === roofId)
|
|
const roof = canvas?.getObjects().find((object) => object.name === POLYGON_TYPE.ROOF && object.id === roofId)
|
|
let hipLines = canvas?.getObjects().filter((object) => object.name === LINE_TYPE.SUBLINE.HIP && object.attributes.roofId === roofId)
|
|
let ridgeLines = canvas?.getObjects().filter((object) => object.name === LINE_TYPE.SUBLINE.RIDGE && object.attributes.roofId === roofId)
|
|
let wallLine = wall.lines.filter((w) => w.id === currentRoof.attributes.wallLine)
|
|
if (wallLine.length > 0) {
|
|
wallLine = wallLine[0]
|
|
}
|
|
let prevRoof, nextRoof
|
|
roof.lines.forEach((r, index) => {
|
|
if (r.id === currentRoof.id) {
|
|
currentRoof = r
|
|
prevRoof = roof.lines[index === 0 ? roof.lines.length - 1 : index - 1]
|
|
nextRoof = roof.lines[index === roof.lines.length - 1 ? 0 : index + 1]
|
|
}
|
|
})
|
|
|
|
const midX = Big(currentRoof.x1).plus(Big(currentRoof.x2)).div(2) // 지붕의 X 중심
|
|
const midY = Big(currentRoof.y1).plus(Big(currentRoof.y2)).div(2) // 지붕의 Y 중심
|
|
const midWallX = Big(wallLine.x1).plus(Big(wallLine.x2)).div(2) // 벽의 X 중심
|
|
const midWallY = Big(wallLine.y1).plus(Big(wallLine.y2)).div(2) // 벽의 Y 중심
|
|
const alpha = midX.minus(midWallX) // 벽과 지붕의 X 거리
|
|
const beta = midY.minus(midWallY) // 벽과 지붕의 Y 거리
|
|
// Math.sqrt(Math.pow(alpha, 2) + Math.pow(beta, 2))
|
|
const hypotenuse = alpha.pow(2).plus(beta.pow(2)).sqrt() // 벽과 지붕의 거리
|
|
const xWidth = Big(Math.sign(midX.minus(midWallX).toNumber()))
|
|
.times(alpha.div(hypotenuse))
|
|
.times(currentRoof.attributes.width) // 지붕의 X 너비
|
|
const yWidth = Big(Math.sign(midY.minus(midWallY)))
|
|
.times(beta.div(hypotenuse))
|
|
.times(currentRoof.attributes.width) // 지붕의 Y 너비
|
|
const hipX2 = Big(Math.sign(midX.minus(midWallX).toNumber()))
|
|
.times(alpha.div(hypotenuse))
|
|
.times(currentRoof.length / 2) // 추녀마루의 X 너비
|
|
const hipY2 = Big(Math.sign(midY.minus(midWallY)))
|
|
.times(beta.div(hypotenuse))
|
|
.times(currentRoof.length / 2) // 추녀마루의 Y 너비
|
|
|
|
// if (Math.sqrt(Math.pow(xWidth, 2) + Math.pow(yWidth, 2)) < Math.sqrt(Math.pow(hipX2, 2) + Math.pow(hipY2, 2))) {
|
|
if (
|
|
xWidth
|
|
.pow(2)
|
|
.plus(yWidth.pow(2))
|
|
.sqrt()
|
|
.lt(hipX2.pow(2).plus(hipY2.pow(2)).sqrt())
|
|
) {
|
|
const innerLines = canvas
|
|
?.getObjects()
|
|
.filter(
|
|
(object) =>
|
|
object.attributes?.roofId === roofId &&
|
|
object.attributes?.currentRoofId === currentRoof.id &&
|
|
object.x1 !== undefined &&
|
|
object.x2 !== undefined,
|
|
)
|
|
|
|
innerLines
|
|
.filter(
|
|
(line) =>
|
|
line.name !== LINE_TYPE.SUBLINE.RIDGE &&
|
|
line.name !== LINE_TYPE.SUBLINE.HIP &&
|
|
line.name !== LINE_TYPE.SUBLINE.VALLEY &&
|
|
line.name !== OUTER_LINE_TYPE.OUTER_LINE &&
|
|
line.name !== 'wallLine',
|
|
)
|
|
.forEach((line) => {
|
|
roof.innerLines = roof.innerLines.filter((l) => l.id !== line.id)
|
|
canvas?.remove(line)
|
|
})
|
|
|
|
ridgeLines = ridgeLines.filter(
|
|
(ridge) =>
|
|
(ridge.x1 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y1 === currentRoof.attributes.ridgeCoordinate.y1) ||
|
|
(ridge.x2 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y2 === currentRoof.attributes.ridgeCoordinate.y1),
|
|
)
|
|
hipLines = hipLines.filter(
|
|
(hip) => (hip.x1 === currentRoof.x1 && hip.y1 === currentRoof.y1) || (hip.x1 === currentRoof.x2 && hip.y1 === currentRoof.y2),
|
|
)
|
|
|
|
if (hipLines === undefined || hipLines.length === 0) {
|
|
hipLines = innerLines.filter(
|
|
(line) =>
|
|
(line.x1 === currentRoof.x1 && line.y1 === currentRoof.y1) ||
|
|
(line.x2 === currentRoof.x1 && line.y2 === currentRoof.y1) ||
|
|
(line.x1 === currentRoof.x2 && line.y1 === currentRoof.y2) ||
|
|
(line.x2 === currentRoof.x2 && line.y2 === currentRoof.y2),
|
|
)
|
|
}
|
|
|
|
if ((ridgeLines === undefined || ridgeLines.length === 0) && hipLines.length >= 2) {
|
|
let points = []
|
|
hipLines.forEach((hip) => {
|
|
points.push({ x: hip.x1, y: hip.y1 })
|
|
points.push({ x: hip.x2, y: hip.y2 })
|
|
})
|
|
|
|
const pointSet = new Set()
|
|
const duplicatePoints = []
|
|
|
|
points.forEach((point) => {
|
|
const pointKey = `${point.x},${point.y}`
|
|
if (pointSet.has(pointKey)) {
|
|
duplicatePoints.push(point)
|
|
} else {
|
|
pointSet.add(pointKey)
|
|
}
|
|
})
|
|
|
|
ridgeLines = innerLines
|
|
.filter((r) => r !== hipLines[0] && r !== hipLines[1])
|
|
.filter(
|
|
(r) =>
|
|
(r.x1 === duplicatePoints[0].x && r.y1 === duplicatePoints[0].y) || (r.x2 === duplicatePoints[0].x && r.y2 === duplicatePoints[0].y),
|
|
)
|
|
if (ridgeLines.length > 0) {
|
|
currentRoof.attributes.ridgeCoordinate = { x1: duplicatePoints[0].x, y1: duplicatePoints[0].y }
|
|
}
|
|
}
|
|
|
|
hipLines.forEach((hip) => {
|
|
roof.innerLines = roof.innerLines.filter((h) => h.id !== hip.id)
|
|
canvas.remove(hip)
|
|
})
|
|
hipLines = []
|
|
|
|
if (ridgeLines.length > 0) {
|
|
const ridge = ridgeLines[0]
|
|
if (ridge.x1 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y1 === currentRoof.attributes.ridgeCoordinate.y1) {
|
|
const signX = Math.sign(midX.minus(ridge.x1).toNumber())
|
|
const signY = Math.sign(midY.minus(ridge.y1).toNumber())
|
|
ridge.set({
|
|
x1: midX.minus(xWidth.abs().times(signX)).toNumber(),
|
|
y1: midY.minus(yWidth.abs().times(signY)).toNumber(),
|
|
x2: ridge.x2,
|
|
y2: ridge.y2,
|
|
})
|
|
currentRoof.attributes.ridgeCoordinate = { x1: ridge.x1, y1: ridge.y1 }
|
|
}
|
|
if (ridge.x2 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y2 === currentRoof.attributes.ridgeCoordinate.y1) {
|
|
const signX = Math.sign(midX.minus(ridge.x2).toNumber())
|
|
const signY = Math.sign(midY.minus(ridge.y2).toNumber())
|
|
ridge.set({
|
|
x1: ridge.x1,
|
|
y1: ridge.y1,
|
|
x2: midX.minus(xWidth.abs().times(signX)).toNumber(),
|
|
y2: midY.minus(yWidth.abs().times(signY)).toNumber(),
|
|
})
|
|
currentRoof.attributes.ridgeCoordinate = { x1: ridge.x2, y1: ridge.y2 }
|
|
}
|
|
ridge.attributes.planeSize = calcLinePlaneSize({ x1: ridge.x1, y1: ridge.y1, x2: ridge.x2, y2: ridge.y2 })
|
|
ridge.attributes.actualSize = calcLinePlaneSize({ x1: ridge.x1, y1: ridge.y1, x2: ridge.x2, y2: ridge.y2 })
|
|
}
|
|
|
|
const currentDegree = currentRoof.attributes.pitch > 0 ? getDegreeByChon(currentRoof.attributes.pitch) : currentRoof.attributes.degree
|
|
|
|
const hip1 = new QLine([currentRoof.x1, currentRoof.y1, midX.plus(hipX2).toNumber(), midY.plus(hipY2).toNumber()], {
|
|
parentId: roof.id,
|
|
fontSize: roof.fontSize,
|
|
stroke: '#1083E3',
|
|
strokeWidth: 2,
|
|
name: LINE_TYPE.SUBLINE.HIP,
|
|
textMode: textMode,
|
|
attributes: {
|
|
roofId: roof.id,
|
|
currentRoofId: currentRoof.id,
|
|
planeSize: calcLinePlaneSize({
|
|
x1: currentRoof.x1,
|
|
y1: currentRoof.y1,
|
|
x2: midX.plus(hipX2).toNumber(),
|
|
y2: midY.plus(hipY2).toNumber(),
|
|
}),
|
|
actualSize: calcLineActualSize(
|
|
{
|
|
x1: currentRoof.x1,
|
|
y1: currentRoof.y1,
|
|
x2: midX.plus(hipX2).toNumber(),
|
|
y2: midY.plus(hipY2).toNumber(),
|
|
},
|
|
currentDegree,
|
|
),
|
|
},
|
|
})
|
|
|
|
// const hip1Base = ((Math.abs(hip1.x1 - hip1.x2) + Math.abs(hip1.y1 - hip1.y2)) / 2) * 10
|
|
// const hip1Height = Math.round(hip1Base / Math.tan(((90 - prevDegree) * Math.PI) / 180))
|
|
// hip1.attributes.planeSize = Math.round(Math.sqrt(Math.pow(hip1.x1 - hip1.x2, 2) + Math.pow(hip1.y1 - hip1.y2, 2))) * 10
|
|
// hip1.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip1.attributes.planeSize, 2) + Math.pow(hip1Height, 2)))
|
|
canvas?.add(hip1)
|
|
roof.innerLines.push(hip1)
|
|
|
|
const hip2 = new QLine([currentRoof.x2, currentRoof.y2, midX.plus(hipX2).toNumber(), midY.plus(hipY2).toNumber()], {
|
|
parentId: roof.id,
|
|
fontSize: roof.fontSize,
|
|
stroke: '#1083E3',
|
|
strokeWidth: 2,
|
|
name: LINE_TYPE.SUBLINE.HIP,
|
|
textMode: textMode,
|
|
attributes: {
|
|
roofId: roof.id,
|
|
currentRoofId: currentRoof.id,
|
|
planeSize: calcLinePlaneSize({
|
|
x1: currentRoof.x2,
|
|
y1: currentRoof.y2,
|
|
x2: midX.plus(hipX2).toNumber(),
|
|
y2: midY.plus(hipY2).toNumber(),
|
|
}),
|
|
actualSize: calcLineActualSize(
|
|
{
|
|
x1: currentRoof.x2,
|
|
y1: currentRoof.y2,
|
|
x2: midX.plus(hipX2).toNumber(),
|
|
y2: midY.plus(hipY2).toNumber(),
|
|
},
|
|
currentDegree,
|
|
),
|
|
},
|
|
})
|
|
// const hip2Base = ((Math.abs(hip2.x1 - hip2.x2) + Math.abs(hip2.y1 - hip2.y2)) / 2) * 10
|
|
// const hip2Height = Math.round(hip2Base / Math.tan(((90 - nextDegree) * Math.PI) / 180))
|
|
// hip2.attributes.planeSize = Math.round(Math.sqrt(Math.pow(hip2.x1 - hip2.x2, 2) + Math.pow(hip2.y1 - hip2.y2, 2))) * 10
|
|
// hip2.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip2.attributes.planeSize, 2) + Math.pow(hip2Height, 2)))
|
|
canvas?.add(hip2)
|
|
roof.innerLines.push(hip2)
|
|
|
|
hipLines.push(hip1)
|
|
hipLines.push(hip2)
|
|
|
|
hipLines.forEach((hip) => {
|
|
const singHipX = Math.sign(hip.x1 - midWallX.toNumber())
|
|
const singHipY = Math.sign(hip.y1 - midWallY.toNumber())
|
|
|
|
hip.set({
|
|
x1: hip.x1,
|
|
y1: hip.y1,
|
|
x2: hip.x1 - singHipX * currentRoof.attributes.width,
|
|
y2: hip.y1 - singHipY * currentRoof.attributes.width,
|
|
})
|
|
})
|
|
|
|
hipLines.forEach((hip, i) => {
|
|
const gableLine = new QLine([hip.x2, hip.y2, currentRoof.attributes.ridgeCoordinate.x1, currentRoof.attributes.ridgeCoordinate.y1], {
|
|
parentId: roof.id,
|
|
fontSize: roof.fontSize,
|
|
stroke: '#1083E3',
|
|
strokeWidth: 2,
|
|
name: LINE_TYPE.SUBLINE.GABLE,
|
|
attributes: {
|
|
roofId: roof.id,
|
|
currentRoofId: currentRoof.id,
|
|
textMode: textMode,
|
|
planeSize: calcLinePlaneSize({
|
|
x1: hip.x2,
|
|
y1: hip.y2,
|
|
x2: currentRoof.attributes.ridgeCoordinate.x1,
|
|
y2: currentRoof.attributes.ridgeCoordinate.y1,
|
|
}),
|
|
actualSize: calcLineActualSize(
|
|
{
|
|
x1: hip.x2,
|
|
y1: hip.y2,
|
|
x2: currentRoof.attributes.ridgeCoordinate.x1,
|
|
y2: currentRoof.attributes.ridgeCoordinate.y1,
|
|
},
|
|
currentDegree,
|
|
),
|
|
},
|
|
})
|
|
|
|
// const gableBase = ((Math.abs(gableLine.x1 - gableLine.x2) + Math.abs(gableLine.y1 - gableLine.y2)) / 2) * 10
|
|
// const gableHeight = Math.round(gableBase / Math.tan(((90 - gableDegree) * Math.PI) / 180))
|
|
// gableLine.attributes.planeSize =
|
|
// Math.round(Math.sqrt(Math.pow(gableLine.x1 - gableLine.x2, 2) + Math.pow(gableLine.y1 - gableLine.y2, 2))) * 10
|
|
// gableLine.attributes.actualSize = Math.round(Math.sqrt(Math.pow(gableLine.attributes.planeSize, 2) + Math.pow(gableHeight, 2)))
|
|
canvas?.add(gableLine)
|
|
roof.innerLines.push(gableLine)
|
|
})
|
|
}
|
|
}
|
|
canvas?.renderAll()
|
|
}
|
|
|
|
/**
|
|
* 반절처 지붕으로 변경
|
|
* @param currentRoof
|
|
* @param canvas
|
|
* @param textMode
|
|
*/
|
|
const changeJerkInHeadRoof = (currentRoof, canvas, textMode) => {
|
|
if (
|
|
currentRoof.attributes.type === LINE_TYPE.WALLLINE.JERKINHEAD &&
|
|
currentRoof.attributes.width !== undefined &&
|
|
currentRoof.attributes.width > 0
|
|
) {
|
|
const roofId = currentRoof.attributes.roofId
|
|
const wall = canvas?.getObjects().find((object) => object.name === POLYGON_TYPE.WALL && object.attributes.roofId === roofId)
|
|
const roof = canvas?.getObjects().find((object) => object.name === POLYGON_TYPE.ROOF && object.id === roofId)
|
|
let hipLines = canvas?.getObjects().filter((object) => object.name === LINE_TYPE.SUBLINE.HIP && object.attributes.roofId === roofId)
|
|
let ridgeLines = canvas?.getObjects().filter((object) => object.name === LINE_TYPE.SUBLINE.RIDGE && object.attributes.roofId === roofId)
|
|
let wallLine = wall.lines.filter((w) => w.id === currentRoof.attributes.wallLine)
|
|
if (wallLine.length > 0) {
|
|
wallLine = wallLine[0]
|
|
}
|
|
|
|
let prevRoof, nextRoof
|
|
roof.lines.forEach((r, index) => {
|
|
if (r.id === currentRoof.id) {
|
|
currentRoof = r
|
|
prevRoof = roof.lines[index === 0 ? roof.lines.length - 1 : index - 1]
|
|
nextRoof = roof.lines[index === roof.lines.length - 1 ? 0 : index + 1]
|
|
}
|
|
})
|
|
|
|
const prevDegree = prevRoof.attributes.pitch > 0 ? getDegreeByChon(prevRoof.attributes.pitch) : prevRoof.attributes.degree
|
|
const nextDegree = nextRoof.attributes.pitch > 0 ? getDegreeByChon(nextRoof.attributes.pitch) : nextRoof.attributes.degree
|
|
|
|
const midX = Big(currentRoof.x1).plus(Big(currentRoof.x2)).div(2) // 지붕의 X 중심
|
|
const midY = Big(currentRoof.y1).plus(Big(currentRoof.y2)).div(2) // 지붕의 Y 중심
|
|
const midWallX = Big(wallLine.x1).plus(Big(wallLine.x2)).div(2) // 벽의 X 중심
|
|
const midWallY = Big(wallLine.y1).plus(Big(wallLine.y2)).div(2) // 벽의 Y 중심
|
|
const alpha = midX.minus(midWallX) // 벽과 지붕의 X 거리
|
|
const beta = midY.minus(midWallY) // 벽과 지붕의 Y 거리
|
|
// Math.sqrt(Math.pow(alpha, 2) + Math.pow(beta, 2))
|
|
const hypotenuse = alpha.pow(2).plus(beta.pow(2)).sqrt() // 벽과 지붕의 거리
|
|
const xWidth = Big(Math.sign(midX.minus(midWallX).toNumber()))
|
|
.times(alpha.div(hypotenuse))
|
|
.times(currentRoof.attributes.width / 2) // 지붕의 X 너비
|
|
const yWidth = Big(Math.sign(midY.minus(midWallY)))
|
|
.times(beta.div(hypotenuse))
|
|
.times(currentRoof.attributes.width / 2) // 지붕의 Y 너비
|
|
const addHipX2 = Big(Math.sign(midX.minus(midWallX).toNumber()))
|
|
.times(alpha.div(hypotenuse))
|
|
.times(currentRoof.length / 2) // 추녀마루의 X 너비
|
|
const addHipY2 = Big(Math.sign(midY.minus(midWallY)))
|
|
.times(beta.div(hypotenuse))
|
|
.times(currentRoof.length / 2) // 추녀마루의 Y 너비
|
|
let hipX2 = 0
|
|
let hipY2 = 0
|
|
|
|
// if (Math.sqrt(Math.pow(xWidth, 2) + Math.pow(yWidth, 2)) < Math.sqrt(Math.pow(addHipX2, 2) + Math.pow(addHipY2, 2))) {
|
|
if (
|
|
xWidth
|
|
.pow(2)
|
|
.plus(yWidth.pow(2))
|
|
.lt(addHipX2.pow(2).plus(addHipY2.pow(2)))
|
|
) {
|
|
// reDrawPolygon(roof, canvas)
|
|
|
|
const innerLines = canvas
|
|
?.getObjects()
|
|
.filter(
|
|
(object) =>
|
|
object.attributes !== undefined &&
|
|
object.attributes.roofId === roofId &&
|
|
object.attributes.currentRoofId === currentRoof.id &&
|
|
object.x1 !== undefined &&
|
|
object.x2 !== undefined,
|
|
)
|
|
|
|
innerLines
|
|
.filter(
|
|
(line) =>
|
|
line.name !== LINE_TYPE.SUBLINE.RIDGE &&
|
|
line.name !== LINE_TYPE.SUBLINE.HIP &&
|
|
line.name !== LINE_TYPE.SUBLINE.VALLEY &&
|
|
line.name !== OUTER_LINE_TYPE.OUTER_LINE &&
|
|
line.name !== 'wallLine',
|
|
)
|
|
.forEach((line) => {
|
|
roof.innerLines = roof.innerLines.filter((l) => l.id !== line.id)
|
|
canvas?.remove(line)
|
|
})
|
|
|
|
ridgeLines = ridgeLines.filter(
|
|
(ridge) =>
|
|
(ridge.x1 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y1 === currentRoof.attributes.ridgeCoordinate.y1) ||
|
|
(ridge.x2 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y2 === currentRoof.attributes.ridgeCoordinate.y1),
|
|
)
|
|
hipLines = hipLines.filter(
|
|
(hip) => (hip.x1 === currentRoof.x1 && hip.y1 === currentRoof.y1) || (hip.x1 === currentRoof.x2 && hip.y1 === currentRoof.y2),
|
|
)
|
|
|
|
if (hipLines === undefined || hipLines.length === 0) {
|
|
hipLines = innerLines.filter(
|
|
(line) =>
|
|
(line.x1 === currentRoof.x1 && line.y1 === currentRoof.y1) ||
|
|
(line.x2 === currentRoof.x1 && line.y2 === currentRoof.y1) ||
|
|
(line.x1 === currentRoof.x2 && line.y1 === currentRoof.y2) ||
|
|
(line.x2 === currentRoof.x2 && line.y2 === currentRoof.y2),
|
|
)
|
|
}
|
|
|
|
if ((ridgeLines === undefined || ridgeLines.length === 0) && hipLines.length >= 2) {
|
|
let points = []
|
|
hipLines.forEach((hip) => {
|
|
points.push({ x: hip.x1, y: hip.y1 })
|
|
points.push({ x: hip.x2, y: hip.y2 })
|
|
})
|
|
|
|
const pointSet = new Set()
|
|
const duplicatePoints = []
|
|
|
|
points.forEach((point) => {
|
|
const pointKey = `${point.x},${point.y}`
|
|
if (pointSet.has(pointKey)) {
|
|
duplicatePoints.push(point)
|
|
} else {
|
|
pointSet.add(pointKey)
|
|
}
|
|
})
|
|
|
|
ridgeLines = innerLines
|
|
.filter((r) => r !== hipLines[0] && r !== hipLines[1])
|
|
.filter(
|
|
(r) =>
|
|
(r.x1 === duplicatePoints[0].x && r.y1 === duplicatePoints[0].y) || (r.x2 === duplicatePoints[0].x && r.y2 === duplicatePoints[0].y),
|
|
)
|
|
if (ridgeLines.length > 0) {
|
|
currentRoof.attributes.ridgeCoordinate = { x1: duplicatePoints[0].x, y1: duplicatePoints[0].y }
|
|
}
|
|
}
|
|
|
|
hipLines.forEach((hip) => {
|
|
roof.innerLines = roof.innerLines.filter((h) => h.id !== hip.id)
|
|
canvas.remove(hip)
|
|
})
|
|
|
|
if (ridgeLines.length > 0) {
|
|
const ridge = ridgeLines[0]
|
|
if (ridge.x1 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y1 === currentRoof.attributes.ridgeCoordinate.y1) {
|
|
const signX = Math.sign(midX.minus(ridge.x1).toNumber())
|
|
const signY = Math.sign(midY.minus(ridge.y1).toNumber())
|
|
ridge.set({
|
|
x1: midX.minus(xWidth.abs().times(signX)).toNumber(),
|
|
y1: midY.minus(yWidth.abs().times(signY)).toNumber(),
|
|
x2: ridge.x2,
|
|
y2: ridge.y2,
|
|
})
|
|
|
|
currentRoof.attributes.ridgeCoordinate = { x1: ridge.x1, y1: ridge.y1 }
|
|
hipX2 = ridge.x1
|
|
hipY2 = ridge.y1
|
|
}
|
|
if (ridge.x2 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y2 === currentRoof.attributes.ridgeCoordinate.y1) {
|
|
const signX = Math.sign(midX - ridge.x2)
|
|
const signY = Math.sign(midY - ridge.y2)
|
|
ridge.set({
|
|
x1: ridge.x1,
|
|
y1: ridge.y1,
|
|
x2: midX.minus(xWidth.abs().times(signX)).toNumber(),
|
|
y2: midY.minus(yWidth.abs().times(signY)).toNumber(),
|
|
})
|
|
|
|
currentRoof.attributes.ridgeCoordinate = { x1: ridge.x2, y1: ridge.y2 }
|
|
hipX2 = ridge.x2
|
|
hipY2 = ridge.y2
|
|
}
|
|
ridge.attributes.planeSize = calcLinePlaneSize({ x1: ridge.x1, y1: ridge.y1, x2: ridge.x2, y2: ridge.y2 })
|
|
ridge.attributes.actualSize = calcLinePlaneSize({ x1: ridge.x1, y1: ridge.y1, x2: ridge.x2, y2: ridge.y2 })
|
|
}
|
|
|
|
let hipX1 = (Math.sign(currentRoof.x1 - midX) * currentRoof.attributes.width) / 2
|
|
let hipY1 = (Math.sign(currentRoof.y1 - midY) * currentRoof.attributes.width) / 2
|
|
|
|
const gableDegree = currentRoof.attributes.degree > 0 ? currentRoof.attributes.degree : getDegreeByChon(currentRoof.attributes.pitch)
|
|
const gable1 = new QLine([midX.plus(hipX1).toNumber(), midY.plus(hipY1).toNumber(), hipX2, hipY2], {
|
|
parentId: roof.id,
|
|
fontSize: roof.fontSize,
|
|
stroke: '#1083E3',
|
|
strokeWidth: 2,
|
|
name: LINE_TYPE.SUBLINE.GABLE,
|
|
textMode: textMode,
|
|
attributes: {
|
|
roofId: roof.id,
|
|
currentRoofId: currentRoof.id,
|
|
planeSize: calcLinePlaneSize({
|
|
x1: midX.plus(hipX1).toNumber(),
|
|
y1: midY.plus(hipY1).toNumber(),
|
|
x2: hipX2,
|
|
y2: hipY2,
|
|
}),
|
|
actualSize: calcLineActualSize(
|
|
{
|
|
x1: midX.plus(hipX1).toNumber(),
|
|
y1: midY.plus(hipY1).toNumber(),
|
|
x2: hipX2,
|
|
y2: hipY2,
|
|
},
|
|
gableDegree,
|
|
),
|
|
},
|
|
})
|
|
// const gable1Base = ((Math.abs(gable1.x1 - gable1.x2) + Math.abs(gable1.y1 - gable1.y2)) / 2) * 10
|
|
// const gable1Height = Math.round(gable1Base / Math.tan(((90 - gableDegree) * Math.PI) / 180))
|
|
// gable1.attributes.planeSize = Math.round(Math.sqrt(Math.pow(gable1.x1 - gable1.x2, 2) + Math.pow(gable1.y1 - gable1.y2, 2))) * 10
|
|
// gable1.attributes.actualSize = Math.round(Math.sqrt(Math.pow(gable1.attributes.planeSize, 2) + Math.pow(gable1Height, 2)))
|
|
canvas?.add(gable1)
|
|
roof.innerLines.push(gable1)
|
|
|
|
hipX1 = (Math.sign(currentRoof.x2 - midX) * currentRoof.attributes.width) / 2
|
|
hipY1 = (Math.sign(currentRoof.y2 - midY) * currentRoof.attributes.width) / 2
|
|
|
|
const gable2 = new QLine([midX.plus(hipX1).toNumber(), midY.plus(hipY1).toNumber(), hipX2, hipY2], {
|
|
parentId: roof.id,
|
|
fontSize: roof.fontSize,
|
|
stroke: '#1083E3',
|
|
strokeWidth: 2,
|
|
name: LINE_TYPE.SUBLINE.GABLE,
|
|
textMode: textMode,
|
|
attributes: {
|
|
roofId: roof.id,
|
|
currentRoofId: currentRoof.id,
|
|
planeSize: calcLinePlaneSize({
|
|
x1: midX.plus(hipX1).toNumber(),
|
|
y1: midY.plus(hipY1).toNumber(),
|
|
x2: hipX2,
|
|
y2: hipY2,
|
|
}),
|
|
actualSize: calcLineActualSize(
|
|
{
|
|
x1: midX.plus(hipX1).toNumber(),
|
|
y1: midY.plus(hipY1).toNumber(),
|
|
x2: hipX2,
|
|
y2: hipY2,
|
|
},
|
|
gableDegree,
|
|
),
|
|
},
|
|
})
|
|
// const gable2Base = ((Math.abs(gable2.x1 - gable2.x2) + Math.abs(gable2.y1 - gable2.y2)) / 2) * 10
|
|
// const gable2Height = Math.round(gable2Base / Math.tan(((90 - gableDegree) * Math.PI) / 180))
|
|
// gable2.attributes.planeSize = Math.round(Math.sqrt(Math.pow(gable2.x1 - gable2.x2, 2) + Math.pow(gable2.y1 - gable2.y2, 2))) * 10
|
|
// gable2.attributes.actualSize = Math.round(Math.sqrt(Math.pow(gable2.attributes.planeSize, 2) + Math.pow(gable2Height, 2)))
|
|
canvas?.add(gable2)
|
|
roof.innerLines.push(gable2)
|
|
|
|
const gable3 = new QLine([gable1.x1, gable1.y1, gable2.x1, gable2.y1], {
|
|
parentId: roof.id,
|
|
fontSize: roof.fontSize,
|
|
stroke: '#1083E3',
|
|
strokeWidth: 2,
|
|
name: LINE_TYPE.SUBLINE.GABLE,
|
|
textMode: textMode,
|
|
visible: false,
|
|
attributes: {
|
|
roofId: roof.id,
|
|
currentRoofId: currentRoof.id,
|
|
planeSize: calcLinePlaneSize({ x1: gable1.x1, y1: gable1.y1, x2: gable2.x1, y2: gable2.y1 }),
|
|
actualSize: calcLinePlaneSize({ x1: gable1.x1, y1: gable1.y1, x2: gable2.x1, y2: gable2.y1 }),
|
|
},
|
|
})
|
|
// gable3.attributes.planeSize = Math.round(Math.sqrt(Math.pow(gable3.x1 - gable3.x2, 2) + Math.pow(gable3.y1 - gable3.y2, 2))) * 10
|
|
// gable3.attributes.actualSize = Math.round(Math.sqrt(Math.pow(gable3.x1 - gable3.x2, 2) + Math.pow(gable3.y1 - gable3.y2, 2))) * 10
|
|
canvas?.add(gable3)
|
|
// roof.innerLines.push(gable3)
|
|
|
|
const hip1 = new QLine([currentRoof.x1, currentRoof.y1, gable1.x1, gable1.y1], {
|
|
parentId: roof.id,
|
|
fontSize: roof.fontSize,
|
|
stroke: '#1083E3',
|
|
strokeWidth: 2,
|
|
name: LINE_TYPE.SUBLINE.HIP,
|
|
textMode: textMode,
|
|
visible: false,
|
|
attributes: {
|
|
roofId: roof.id,
|
|
currentRoofId: currentRoof.id,
|
|
planeSize: calcLinePlaneSize({ x1: currentRoof.x1, y1: currentRoof.y1, x2: gable1.x1, y2: gable1.y1 }),
|
|
actualSize: calcLineActualSize(
|
|
{
|
|
x1: currentRoof.x1,
|
|
y1: currentRoof.y1,
|
|
x2: gable1.x1,
|
|
y2: gable1.y1,
|
|
},
|
|
prevDegree,
|
|
),
|
|
},
|
|
})
|
|
// const hip1Base = ((Math.abs(hip1.x1 - hip1.x2) + Math.abs(hip1.y1 - hip1.y2)) / 2) * 10
|
|
// const hip1Height = Math.round(hip1Base / Math.tan(((90 - prevDegree) * Math.PI) / 180))
|
|
// hip1.attributes.planeSize = Math.round(Math.sqrt(Math.pow(hip1.x1 - hip1.x2, 2) + Math.pow(hip1.y1 - hip1.y2, 2))) * 10
|
|
// hip1.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip1.attributes.planeSize, 2) + Math.pow(hip1Height, 2)))
|
|
canvas?.add(hip1)
|
|
// roof.innerLines.push(hip1)
|
|
|
|
const hip2 = new QLine([currentRoof.x2, currentRoof.y2, gable2.x1, gable2.y1], {
|
|
parentId: roof.id,
|
|
fontSize: roof.fontSize,
|
|
stroke: '#1083E3',
|
|
strokeWidth: 2,
|
|
name: LINE_TYPE.SUBLINE.HIP,
|
|
textMode: textMode,
|
|
visible: false,
|
|
attributes: {
|
|
roofId: roof.id,
|
|
currentRoofId: currentRoof.id,
|
|
planeSize: calcLinePlaneSize({ x1: currentRoof.x2, y1: currentRoof.y2, x2: gable2.x1, y2: gable2.y1 }),
|
|
actualSize: calcLineActualSize(
|
|
{
|
|
x1: currentRoof.x2,
|
|
y1: currentRoof.y2,
|
|
x2: gable2.x1,
|
|
y2: gable2.y1,
|
|
},
|
|
nextDegree,
|
|
),
|
|
},
|
|
})
|
|
// const hip2Base = ((Math.abs(hip2.x1 - hip2.x2) + Math.abs(hip2.y1 - hip2.y2)) / 2) * 10
|
|
// const hip2Height = Math.round(hip2Base / Math.tan(((90 - nextDegree) * Math.PI) / 180))
|
|
// hip2.attributes.planeSize = Math.round(Math.sqrt(Math.pow(hip2.x1 - hip2.x2, 2) + Math.pow(hip2.y1 - hip2.y2, 2))) * 10
|
|
// hip2.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip2.attributes.planeSize, 2) + Math.pow(hip2Height, 2)))
|
|
canvas?.add(hip2)
|
|
|
|
// hip1.set({ visible: false })
|
|
hip1.setViewLengthText(false)
|
|
// gable3.set({ visible: false })
|
|
gable3.setViewLengthText(false)
|
|
// hip2.set({ visible: false })
|
|
hip2.setViewLengthText(false)
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 벽지붕으로 변경
|
|
* @param currentRoof
|
|
* @param canvas
|
|
* @param textMode
|
|
*/
|
|
const changeWallRoof = (currentRoof, canvas, textMode) => {
|
|
const roofId = currentRoof.attributes.roofId
|
|
const roof = canvas?.getObjects().find((object) => object.name === 'roof' && object.id === roofId)
|
|
const roofLines = roof.lines
|
|
let prevRoof, nextRoof
|
|
roofLines.forEach((r, index) => {
|
|
if (r.id === currentRoof.id) {
|
|
currentRoof = r
|
|
prevRoof = roofLines[index === 0 ? roofLines.length - 1 : index - 1]
|
|
nextRoof = roofLines[index === roofLines.length - 1 ? 0 : index + 1]
|
|
}
|
|
})
|
|
|
|
const wall = canvas?.getObjects().find((object) => object.name === 'wall' && object.attributes.roofId === roofId)
|
|
let wallLine = wall.lines.filter((w) => w.id === currentRoof.attributes.wallLine)
|
|
let hipLines = canvas?.getObjects().filter((object) => object.name === LINE_TYPE.SUBLINE.HIP && object.attributes.roofId === roofId)
|
|
let ridgeLines = canvas?.getObjects().filter((object) => object.name === LINE_TYPE.SUBLINE.RIDGE && object.attributes.roofId === roofId)
|
|
|
|
if (wallLine.length > 0) {
|
|
wallLine = wallLine[0]
|
|
}
|
|
|
|
ridgeLines = ridgeLines.filter(
|
|
(ridge) =>
|
|
(ridge.x1 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y1 === currentRoof.attributes.ridgeCoordinate.y1) ||
|
|
(ridge.x2 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y2 === currentRoof.attributes.ridgeCoordinate.y1),
|
|
)
|
|
hipLines = hipLines.filter(
|
|
(hip) => (hip.x1 === currentRoof.x1 && hip.y1 === currentRoof.y1) || (hip.x1 === currentRoof.x2 && hip.y1 === currentRoof.y2),
|
|
)
|
|
|
|
const wallMidX = Big(wallLine.x1).plus(Big(wallLine.x2)).div(2)
|
|
const wallMidY = Big(wallLine.y1).plus(Big(wallLine.y2)).div(2)
|
|
const roofMidX = Big(currentRoof.x1).plus(Big(currentRoof.x2)).div(2)
|
|
const roofMidY = Big(currentRoof.y1).plus(Big(currentRoof.y2)).div(2)
|
|
|
|
const alpha = wallMidX.minus(roofMidX)
|
|
const beta = wallMidY.minus(roofMidY)
|
|
|
|
currentRoof.set({
|
|
x1: alpha.plus(currentRoof.x1).toNumber(),
|
|
y1: beta.plus(currentRoof.y1).toNumber(),
|
|
x2: alpha.plus(currentRoof.x2).toNumber(),
|
|
y2: beta.plus(currentRoof.y2).toNumber(),
|
|
})
|
|
|
|
prevRoof.set({
|
|
x1: prevRoof.x1,
|
|
y1: prevRoof.y1,
|
|
x2: alpha.plus(prevRoof.x2),
|
|
y2: beta.plus(prevRoof.y2),
|
|
})
|
|
|
|
nextRoof.set({
|
|
x1: alpha.plus(nextRoof.x1),
|
|
y1: beta.plus(nextRoof.y1),
|
|
x2: nextRoof.x2,
|
|
y2: nextRoof.y2,
|
|
})
|
|
|
|
const innerLines = canvas
|
|
?.getObjects()
|
|
.filter(
|
|
(object) =>
|
|
object.attributes?.roofId === roofId &&
|
|
object.attributes?.currentRoofId === currentRoof.id &&
|
|
object.x1 !== undefined &&
|
|
object.x2 !== undefined,
|
|
)
|
|
|
|
innerLines
|
|
.filter(
|
|
(line) =>
|
|
line.name !== LINE_TYPE.SUBLINE.RIDGE &&
|
|
line.name !== LINE_TYPE.SUBLINE.HIP &&
|
|
line.name !== LINE_TYPE.SUBLINE.VALLEY &&
|
|
line.name !== OUTER_LINE_TYPE.OUTER_LINE &&
|
|
line.name !== 'wallLine',
|
|
)
|
|
.forEach((line) => {
|
|
roof.innerLines = roof.innerLines.filter((l) => l.id !== line.id)
|
|
canvas?.remove(line)
|
|
})
|
|
|
|
const prevDegree = prevRoof.attributes.pitch > 0 ? getDegreeByChon(prevRoof.attributes.pitch) : prevRoof.attributes.degree
|
|
const nextDegree = nextRoof.attributes.pitch > 0 ? getDegreeByChon(nextRoof.attributes.pitch) : nextRoof.attributes.degree
|
|
|
|
if (currentRoof.attributes.sleeve && currentRoof.attributes.width > 0 && prevRoof.attributes.offset > 0 && nextRoof.attributes.offset > 0) {
|
|
const prevSignX = Math.sign(prevRoof.x1 - prevRoof.x2)
|
|
const prevSignY = Math.sign(prevRoof.y1 - prevRoof.y2)
|
|
const nextSignX = Math.sign(nextRoof.x1 - nextRoof.x2)
|
|
const nextSignY = Math.sign(nextRoof.y1 - nextRoof.y2)
|
|
|
|
const prevWidthX = prevSignX === 0 ? 0 : prevSignX * currentRoof.attributes.width
|
|
const prevWidthY = prevSignY === 0 ? 0 : prevSignY * currentRoof.attributes.width
|
|
const nextWidthX = nextSignX === 0 ? 0 : nextSignX * currentRoof.attributes.width
|
|
const nextWidthY = nextSignY === 0 ? 0 : nextSignY * currentRoof.attributes.width
|
|
const prevX2 = Big(prevRoof.x2).minus(prevWidthX)
|
|
const prevY2 = Big(prevRoof.y2).minus(prevWidthY)
|
|
const nextX1 = Big(nextRoof.x1).plus(nextWidthX)
|
|
const nextY1 = Big(nextRoof.y1).plus(nextWidthY)
|
|
|
|
currentRoof.set({
|
|
x1: wallLine.x1,
|
|
y1: wallLine.y1,
|
|
x2: wallLine.x2,
|
|
y2: wallLine.y2,
|
|
})
|
|
|
|
prevRoof.set({
|
|
x1: prevRoof.x1,
|
|
y1: prevRoof.y1,
|
|
x2: prevX2.toNumber(),
|
|
y2: prevY2.toNumber(),
|
|
})
|
|
|
|
nextRoof.set({
|
|
x1: nextX1.toNumber(),
|
|
y1: nextY1.toNumber(),
|
|
x2: nextRoof.x2,
|
|
y2: nextRoof.y2,
|
|
})
|
|
|
|
const addPrevWallLine1 = new QLine(
|
|
[prevX2.toNumber(), prevY2.toNumber(), Big(wallLine.x1).minus(prevWidthX).toNumber(), Big(wallLine.y1).minus(prevWidthY).toNumber()],
|
|
{
|
|
parentId: roof.id,
|
|
fontSize: roof.fontSize,
|
|
stroke: '#1083E3',
|
|
strokeWidth: 2,
|
|
name: 'roofLine',
|
|
textMode: textMode,
|
|
attributes: {
|
|
roofId: roofId,
|
|
type: LINE_TYPE.WALLLINE.ETC,
|
|
planeSize: calcLinePlaneSize({
|
|
x1: prevX2.toNumber(),
|
|
y1: prevY2.toNumber(),
|
|
x2: Big(wallLine.x1).minus(prevWidthX).toNumber(),
|
|
y2: Big(wallLine.y1).minus(prevWidthY).toNumber(),
|
|
}),
|
|
actualSize: calcLinePlaneSize({
|
|
x1: prevX2.toNumber(),
|
|
y1: prevY2.toNumber(),
|
|
x2: Big(wallLine.x1).minus(prevWidthX).toNumber(),
|
|
y2: Big(wallLine.y1).minus(prevWidthY).toNumber(),
|
|
}),
|
|
},
|
|
},
|
|
)
|
|
|
|
const addPrevWallLine2 = new QLine(
|
|
[
|
|
addPrevWallLine1.x2,
|
|
addPrevWallLine1.y2,
|
|
Big(addPrevWallLine1.x2).plus(prevWidthX).toNumber(),
|
|
Big(addPrevWallLine1.y2).plus(prevWidthY).toNumber(),
|
|
],
|
|
{
|
|
parentId: roof.id,
|
|
fontSize: roof.fontSize,
|
|
stroke: '#1083E3',
|
|
strokeWidth: 2,
|
|
name: 'roofLine',
|
|
textMode: textMode,
|
|
attributes: {
|
|
roofId: roofId,
|
|
type: LINE_TYPE.WALLLINE.ETC,
|
|
planeSize: calcLinePlaneSize({
|
|
x1: addPrevWallLine1.x2,
|
|
y1: addPrevWallLine1.y2,
|
|
x2: Big(addPrevWallLine1.x2).plus(prevWidthX).toNumber(),
|
|
y2: Big(addPrevWallLine1.y2).plus(prevWidthY).toNumber(),
|
|
}),
|
|
actualSize: calcLinePlaneSize({
|
|
x1: addPrevWallLine1.x2,
|
|
y1: addPrevWallLine1.y2,
|
|
x2: Big(addPrevWallLine1.x2).plus(prevWidthX).toNumber(),
|
|
y2: Big(addPrevWallLine1.y2).plus(prevWidthY).toNumber(),
|
|
}),
|
|
},
|
|
},
|
|
)
|
|
|
|
const addNextWallLine1 = new QLine(
|
|
[wallLine.x2, wallLine.y2, Big(wallLine.x2).plus(nextWidthX).toNumber(), Big(wallLine.y2).plus(nextWidthY).toNumber()],
|
|
{
|
|
parentId: roof.id,
|
|
fontSize: roof.fontSize,
|
|
stroke: '#1083E3',
|
|
strokeWidth: 2,
|
|
name: 'roofLine',
|
|
textMode: textMode,
|
|
attributes: {
|
|
roofId: roofId,
|
|
type: LINE_TYPE.WALLLINE.ETC,
|
|
planeSize: calcLinePlaneSize({
|
|
x1: wallLine.x2,
|
|
y1: wallLine.y2,
|
|
x2: Big(wallLine.x2).plus(nextWidthX).toNumber(),
|
|
y2: Big(wallLine.y2).plus(nextWidthY).toNumber(),
|
|
}),
|
|
actualSize: calcLinePlaneSize({
|
|
x1: wallLine.x2,
|
|
y1: wallLine.y2,
|
|
}),
|
|
},
|
|
},
|
|
)
|
|
|
|
const addNextWallLine2 = new QLine([addNextWallLine1.x2, addNextWallLine1.y2, nextX1.toNumber(), nextY1.toNumber()], {
|
|
parentId: roof.id,
|
|
fontSize: roof.fontSize,
|
|
stroke: '#1083E3',
|
|
strokeWidth: 2,
|
|
name: 'roofLine',
|
|
attributes: {
|
|
roofId: roofId,
|
|
type: LINE_TYPE.WALLLINE.ETC,
|
|
planeSize: calcLinePlaneSize({
|
|
x1: addNextWallLine1.x2,
|
|
y1: addNextWallLine1.y2,
|
|
x2: nextX1.toNumber(),
|
|
y2: nextY1.toNumber(),
|
|
}),
|
|
actualSize: calcLinePlaneSize({
|
|
x1: addNextWallLine1.x2,
|
|
y1: addNextWallLine1.y2,
|
|
x2: nextX1.toNumber(),
|
|
y2: nextY1.toNumber(),
|
|
}),
|
|
},
|
|
})
|
|
|
|
canvas?.renderAll()
|
|
const prevIndex = roof.lines.indexOf(prevRoof) + 1
|
|
roof.lines.splice(prevIndex, 0, addPrevWallLine1, addPrevWallLine2)
|
|
|
|
const nextIndex = roof.lines.indexOf(currentRoof) + 1
|
|
roof.lines.splice(nextIndex, 0, addNextWallLine1, addNextWallLine2)
|
|
}
|
|
|
|
reDrawPolygon(roof, canvas)
|
|
|
|
if (ridgeLines.length > 0) {
|
|
const ridge = ridgeLines[0]
|
|
if (ridge.x1 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y1 === currentRoof.attributes.ridgeCoordinate.y1) {
|
|
const diffX = Big(ridge.x1).minus(wallMidX)
|
|
const diffY = Big(ridge.y1).minus(wallMidY)
|
|
|
|
ridge.set({
|
|
x1: Big(ridge.x1).minus(diffX).toNumber(),
|
|
y1: Big(ridge.y1).minus(diffY).toNumber(),
|
|
x2: ridge.x2,
|
|
y2: ridge.y2,
|
|
})
|
|
}
|
|
if (ridge.x2 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y2 === currentRoof.attributes.ridgeCoordinate.y1) {
|
|
const diffX = Big(ridge.x2).minus(wallMidX)
|
|
const diffY = Big(ridge.y2).minus(wallMidY)
|
|
|
|
ridge.set({
|
|
x1: ridge.x1,
|
|
y1: ridge.y1,
|
|
x2: Big(ridge.x2).minus(diffX).toNumber(),
|
|
y2: Big(ridge.y2).minus(diffY).toNumber(),
|
|
})
|
|
}
|
|
// ridge.attributes.planeSize = Math.round(Math.sqrt(Math.pow(ridge.x1 - ridge.x2, 2) + Math.pow(ridge.y1 - ridge.y2, 2)) * 10)
|
|
ridge.attributes.planeSize = calcLinePlaneSize({ x1: ridge.x1, y1: ridge.y1, x2: ridge.x2, y2: ridge.y2 })
|
|
ridge.attributes.actualSize = calcLinePlaneSize({ x1: ridge.x1, y1: ridge.y1, x2: ridge.x2, y2: ridge.y2 })
|
|
|
|
let hip1 = new QLine([currentRoof.x1, currentRoof.y1, wallMidX.toNumber(), wallMidY.toNumber()], {
|
|
parentId: roof.id,
|
|
fontSize: roof.fontSize,
|
|
stroke: '#1083E3',
|
|
strokeWidth: 2,
|
|
name: LINE_TYPE.SUBLINE.HIP,
|
|
textMode: textMode,
|
|
attributes: {
|
|
roofId: roof.id,
|
|
currentRoofId: currentRoof.id,
|
|
planeSize: calcLinePlaneSize({
|
|
x1: currentRoof.x1,
|
|
y1: currentRoof.y1,
|
|
x2: wallMidX.toNumber(),
|
|
y2: wallMidY.toNumber(),
|
|
}),
|
|
actualSize: calcLineActualSize(
|
|
{
|
|
x1: currentRoof.x1,
|
|
y1: currentRoof.y1,
|
|
x2: wallMidX.toNumber(),
|
|
y2: wallMidY.toNumber(),
|
|
},
|
|
prevDegree,
|
|
),
|
|
},
|
|
})
|
|
// const hip1Base = ((Math.abs(hip1.x1 - hip1.x2) + Math.abs(hip1.y1 - hip1.y2)) / 2) * 10
|
|
// const hip1Height = Math.round(hip1Base / Math.tan(((90 - prevDegree) * Math.PI) / 180))
|
|
// hip1.attributes.planeSize = Math.round(Math.sqrt(Math.pow(hip1.x1 - hip1.x2, 2) + Math.pow(hip1.y1 - hip1.y2, 2))) * 10
|
|
// hip1.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip1.attributes.planeSize, 2) + Math.pow(hip1Height, 2)))
|
|
|
|
let hip2 = new QLine([currentRoof.x2, currentRoof.y2, wallMidX.toNumber(), wallMidY.toNumber()], {
|
|
parentId: roof.id,
|
|
fontSize: roof.fontSize,
|
|
stroke: '#1083E3',
|
|
strokeWidth: 2,
|
|
name: LINE_TYPE.SUBLINE.HIP,
|
|
attributes: {
|
|
roofId: roof.id,
|
|
currentRoofId: currentRoof.id,
|
|
planeSize: calcLinePlaneSize({
|
|
x1: currentRoof.x2,
|
|
y1: currentRoof.y2,
|
|
x2: wallMidX.toNumber(),
|
|
y2: wallMidY.toNumber(),
|
|
}),
|
|
actualSize: calcLineActualSize(
|
|
{
|
|
x1: currentRoof.x2,
|
|
y1: currentRoof.y2,
|
|
x2: wallMidX.toNumber(),
|
|
y2: wallMidY.toNumber(),
|
|
},
|
|
nextDegree,
|
|
),
|
|
},
|
|
})
|
|
// const hip2Base = ((Math.abs(hip2.x1 - hip2.x2) + Math.abs(hip2.y1 - hip2.y2)) / 2) * 10
|
|
// const hip2Height = Math.round(hip2Base / Math.tan(((90 - nextDegree) * Math.PI) / 180))
|
|
// hip2.attributes.planeSize = Math.round(Math.sqrt(Math.pow(hip2.x1 - hip2.x2, 2) + Math.pow(hip2.y1 - hip2.y2, 2))) * 10
|
|
// hip2.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip2.attributes.planeSize, 2) + Math.pow(hip2Height, 2)))
|
|
canvas?.add(hip1)
|
|
canvas?.add(hip2)
|
|
roof.innerLines.push(hip1)
|
|
roof.innerLines.push(hip2)
|
|
}
|
|
if (hipLines.length > 0) {
|
|
hipLines.forEach((hip) => {
|
|
roof.innerLines = roof.innerLines.filter((h) => h.id !== hip.id)
|
|
canvas?.remove(hip)
|
|
})
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 지붕을 변경한다.
|
|
* @param currentRoof
|
|
* @param canvas
|
|
*/
|
|
export const changeCurrentRoof = (currentRoof, canvas) => {
|
|
const roofId = currentRoof.attributes.roofId
|
|
const originRoof = canvas?.getObjects().find((object) => object.name === 'roof' && object.id === roofId)
|
|
const wall = canvas?.getObjects().find((object) => object.name === 'wall' && object.attributes.roofId === roofId)
|
|
const wallLine = wall.lines.filter((w) => w.id === currentRoof.attributes.wallLine)[0]
|
|
const innerLines = canvas
|
|
?.getObjects()
|
|
.filter((object) => object.attributes !== undefined && object.attributes.roofId === roofId && object.x1 !== undefined && object.x2 !== undefined)
|
|
|
|
wallLine.attributes.type = currentRoof.attributes.type
|
|
wallLine.attributes.offset = currentRoof.attributes.offset
|
|
wallLine.attributes.width = currentRoof.attributes.width
|
|
wallLine.attributes.pitch = currentRoof.attributes.pitch
|
|
wallLine.attributes.sleeve = currentRoof.attributes.sleeve
|
|
|
|
canvas?.remove(originRoof)
|
|
|
|
innerLines.filter((line) => line.name !== OUTER_LINE_TYPE.OUTER_LINE).forEach((line) => canvas?.remove(line))
|
|
|
|
const polygon = createPolygon(wall.points)
|
|
const originPolygon = new QPolygon(wall.points, { fontSize: 0 })
|
|
originPolygon.setViewLengthText(false)
|
|
let offsetPolygon
|
|
|
|
let result = createRoofMarginPolygon(polygon, wall.lines).vertices
|
|
const allPointsOutside = result.every((point) => !originPolygon.inPolygon(point))
|
|
|
|
if (allPointsOutside) {
|
|
offsetPolygon = createRoofMarginPolygon(polygon, wall.lines).vertices
|
|
} else {
|
|
offsetPolygon = createRoofPaddingPolygon(polygon, wall.lines).vertices
|
|
}
|
|
|
|
const newRoof = new QPolygon(offsetPolygon, {
|
|
fill: originRoof.fill,
|
|
stroke: originRoof.stroke,
|
|
strokeWidth: originRoof.strokeWidth,
|
|
selectable: originRoof.selectable,
|
|
fontSize: originRoof.fontSize,
|
|
})
|
|
|
|
newRoof.name = POLYGON_TYPE.ROOF
|
|
newRoof.setWall(wall)
|
|
|
|
newRoof.lines.forEach((line, index) => {
|
|
const lineLength = Math.sqrt(
|
|
Math.pow(Math.round(Math.abs(line.x1 - line.x2) * 10), 2) + Math.pow(Math.round(Math.abs(line.y1 - line.y2) * 10), 2),
|
|
)
|
|
line.attributes = {
|
|
roofId: newRoof.id,
|
|
planeSize: lineLength,
|
|
actualSize: lineLength,
|
|
wallLine: wall.lines[index].id,
|
|
type: wall.lines[index].attributes.type,
|
|
offset: wall.lines[index].attributes.offset,
|
|
width: wall.lines[index].attributes.width,
|
|
pitch: wall.lines[index].attributes.pitch,
|
|
sleeve: wall.lines[index].attributes.sleeve || false,
|
|
}
|
|
})
|
|
wall.attributes = {
|
|
roofId: newRoof.id,
|
|
}
|
|
|
|
wall.lines.forEach((line, index) => {
|
|
line.attributes.roofId = newRoof.id
|
|
line.attributes.currentRoofId = newRoof.lines[index].id
|
|
})
|
|
canvas?.add(newRoof)
|
|
canvas?.renderAll()
|
|
|
|
newRoof.drawHelpLine()
|
|
}
|
|
|
|
/**
|
|
* 지붕을 변경한다.
|
|
* @param polygon
|
|
* @param canvas
|
|
*/
|
|
const reDrawPolygon = (polygon, canvas) => {
|
|
const lines = polygon.lines
|
|
let point = []
|
|
lines.forEach((line) => point.push({ x: line.x1, y: line.y1 }))
|
|
|
|
canvas?.remove(polygon)
|
|
|
|
const newPolygon = new QPolygon(point, {
|
|
id: polygon.id,
|
|
name: polygon.name,
|
|
fill: polygon.fill,
|
|
stroke: polygon.stroke,
|
|
strokeWidth: polygon.strokeWidth,
|
|
selectable: polygon.selectable,
|
|
fontSize: polygon.fontSize,
|
|
wall: polygon.wall !== undefined ? polygon.wall : null,
|
|
})
|
|
|
|
const newLines = newPolygon.lines
|
|
|
|
newLines.forEach((line) => {
|
|
lines.forEach((l) => {
|
|
if (line.x1 === l.x1 && line.y1 === l.y1) {
|
|
line.id = l.id
|
|
line.attributes = l.attributes
|
|
}
|
|
})
|
|
const lineLength = calcLinePlaneSize({ x1: line.x1, y1: line.y1, x2: line.x2, y2: line.y2 })
|
|
if (line.attributes !== undefined) {
|
|
line.attributes.planeSize = lineLength
|
|
line.attributes.actualSize = lineLength
|
|
} else {
|
|
line.attributes = {
|
|
roofId: newPolygon.id,
|
|
planeSize: lineLength,
|
|
actualSize: lineLength,
|
|
}
|
|
}
|
|
})
|
|
|
|
canvas?.add(newPolygon)
|
|
canvas?.renderAll()
|
|
|
|
return newPolygon
|
|
}
|
|
|
|
/**
|
|
* 지붕의 centerLine을 그린다.
|
|
* @param roof
|
|
* @param canvas
|
|
* @param textMode
|
|
*/
|
|
const drawCenterLine = (roof, canvas, textMode) => {
|
|
//현재 지붕의 centerLine을 다 지운다.
|
|
canvas
|
|
.getObjects()
|
|
.filter((object) => object.attributes?.roofId === roof.id)
|
|
.filter((line) => line.attributes?.type === 'pitchSizeLine')
|
|
.forEach((line) => canvas.remove(line))
|
|
|
|
const roofLines = roof.lines
|
|
roofLines.forEach((currentRoof) => {
|
|
const hips = roof.innerLines.filter(
|
|
(line) => (currentRoof.x1 === line.x1 && currentRoof.y1 === line.y1) || (currentRoof.x2 === line.x1 && currentRoof.y2 === line.y1),
|
|
)
|
|
|
|
const ridge = roof.innerLines
|
|
.filter((line) => line.name === LINE_TYPE.SUBLINE.RIDGE)
|
|
.filter((line) => {
|
|
if (currentRoof.x1 === currentRoof.x2) {
|
|
if (line.x1 === line.x2) {
|
|
return line
|
|
}
|
|
} else {
|
|
if (line.y1 === line.y2) {
|
|
return line
|
|
}
|
|
}
|
|
})
|
|
.reduce((prev, current) => {
|
|
let currentDistance, prevDistance
|
|
if (Math.sign(currentRoof.x1 - currentRoof.x2) === 0) {
|
|
currentDistance = Math.abs(currentRoof.y1 - current.y1)
|
|
prevDistance = prev ? Math.abs(currentRoof.y1 - prev.y1) : Infinity
|
|
} else {
|
|
currentDistance = Math.abs(currentRoof.y1 - current.y2)
|
|
prevDistance = prev ? Math.abs(currentRoof.y1 - current.y2) : Infinity
|
|
}
|
|
return prevDistance < currentDistance ? prev : current
|
|
}, null)
|
|
|
|
let points = []
|
|
if (hips.length === 2 && Math.abs(hips[0].x2 - hips[1].x2) < 1 && Math.abs(hips[0].y2 - hips[1].y2) < 1) {
|
|
const x1 = (currentRoof.x1 + currentRoof.x2) / 2
|
|
const y1 = (currentRoof.y1 + currentRoof.y2) / 2
|
|
points.push(x1, y1, hips[0].x2, hips[0].y2)
|
|
} else if (hips.length > 1) {
|
|
if (
|
|
((ridge?.x1 === hips[0].x2 && ridge?.y1 === hips[0].y2) || (ridge?.x2 === hips[0].x2 && ridge?.y2 === hips[0].y2)) &&
|
|
((ridge?.x1 === hips[1].x2 && ridge?.y1 === hips[1].y2) || (ridge?.x2 === hips[1].x2 && ridge?.y2 === hips[1].y2))
|
|
) {
|
|
//사각이면 마루와 현재 라인 사이에 길이를 구한다
|
|
if (Math.sign(currentRoof.x1 - currentRoof.x2) === 0) {
|
|
const yPoints = [currentRoof.y1, currentRoof.y2, ridge.y1, ridge.y2].sort((a, b) => a - b)
|
|
const x1 = (currentRoof.x1 + currentRoof.x2) / 2
|
|
const x2 = (ridge.x1 + ridge.x2) / 2
|
|
const y = (yPoints[1] + yPoints[2]) / 2
|
|
if (
|
|
((currentRoof.y1 <= y && y <= currentRoof.y2) || (currentRoof.y2 <= y && y <= currentRoof.y1)) &&
|
|
((ridge.y1 <= y && y <= ridge.y2) || (ridge.y2 <= y && y <= ridge.y1))
|
|
) {
|
|
points.push(x1, y, x2, y)
|
|
}
|
|
} else {
|
|
const xPoints = [currentRoof.x1, currentRoof.x2, ridge.x1, ridge.x2].sort((a, b) => a - b)
|
|
const y1 = (currentRoof.y1 + currentRoof.y2) / 2
|
|
const y2 = (ridge.y1 + ridge.y2) / 2
|
|
const x = (xPoints[1] + xPoints[2]) / 2
|
|
if (
|
|
((currentRoof.x1 <= x && x <= currentRoof.x2) || (currentRoof.x2 <= x && x <= currentRoof.x1)) &&
|
|
((ridge.x1 <= x && x <= ridge.x2) || (ridge.x2 <= x && x <= ridge.x1))
|
|
) {
|
|
points.push(x, y1, x, y2)
|
|
}
|
|
}
|
|
} else {
|
|
if (Math.sign(currentRoof.x1 - currentRoof.x2) === 0) {
|
|
let xPoints = []
|
|
xPoints.push(ridge?.x1, ridge?.x2)
|
|
hips.forEach((hip) => {
|
|
xPoints.push(hip.x2)
|
|
})
|
|
let maxPoint = xPoints.reduce((prev, current) => {
|
|
const currentDistance = Math.abs(currentRoof.x1 - current)
|
|
const prevDistance = prev ? Math.abs(currentRoof.x1 - prev) : 0
|
|
return prevDistance > currentDistance ? prev : current
|
|
}, null)
|
|
|
|
xPoints = xPoints.filter((point) => point === maxPoint)
|
|
|
|
let oppositeLine
|
|
if (xPoints.length === 1) {
|
|
if (ridge?.x1 === xPoints[0] || ridge?.x2 === xPoints[0]) {
|
|
oppositeLine = ridge
|
|
}
|
|
if (oppositeLine === undefined) {
|
|
oppositeLine = hips.find((hip) => hip.x2 === xPoints[0])
|
|
}
|
|
points.push(currentRoof.x1, oppositeLine.y2, oppositeLine.x2, oppositeLine.y2)
|
|
} else if (xPoints.length > 1) {
|
|
xPoints = [...new Set(xPoints)] // 중복제거
|
|
if (ridge?.length > 0) {
|
|
let boolX1 = xPoints.some((x) => x === ridge.x1)
|
|
let boolX2 = xPoints.some((x) => x === ridge.x2)
|
|
if (boolX1 && boolX2) {
|
|
oppositeLine = ridge
|
|
}
|
|
if (oppositeLine) {
|
|
const sortPoints = [currentRoof.y1, currentRoof.y2, oppositeLine.y1, oppositeLine.y2].sort((a, b) => a - b)
|
|
const y = (sortPoints[1] + sortPoints[2]) / 2
|
|
if (
|
|
((currentRoof.y1 <= y && y <= currentRoof.y2) || (currentRoof.y2 <= y && y <= currentRoof.y1)) &&
|
|
((oppositeLine.y1 <= y && y <= oppositeLine.y2) || (oppositeLine.y2 <= y && y <= oppositeLine.y1))
|
|
) {
|
|
points.push(currentRoof.x1, y, oppositeLine.x1, y)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
let yPoints = []
|
|
yPoints.push(ridge?.y1, ridge?.y2)
|
|
hips.forEach((hip) => {
|
|
yPoints.push(hip.y2)
|
|
})
|
|
const maxPoint = yPoints.reduce((prev, current) => {
|
|
const currentDistance = Math.abs(currentRoof.y1 - current)
|
|
const prevDistance = prev ? Math.abs(currentRoof.y1 - prev) : 0
|
|
return prevDistance > currentDistance ? prev : current
|
|
})
|
|
yPoints = yPoints.filter((y) => y === maxPoint)
|
|
|
|
let oppositeLine
|
|
if (yPoints.length === 1) {
|
|
if (ridge?.y1 === yPoints[0] || ridge?.y2 === yPoints[0]) {
|
|
oppositeLine = ridge
|
|
}
|
|
if (oppositeLine === undefined) {
|
|
oppositeLine = hips.find((hip) => hip.y2 === yPoints[0])
|
|
}
|
|
points.push(oppositeLine.x2, currentRoof.y1, oppositeLine.x2, oppositeLine.y2)
|
|
} else if (yPoints.length > 1) {
|
|
let boolY1 = yPoints.some((y) => y === ridge?.y1)
|
|
let boolY2 = yPoints.some((y) => y === ridge?.y2)
|
|
if (boolY1 && boolY2) {
|
|
oppositeLine = ridge
|
|
}
|
|
if (oppositeLine) {
|
|
const sortPoints = [currentRoof.x1, currentRoof.x2, oppositeLine.x1, oppositeLine.x2].sort((a, b) => a - b)
|
|
const x = (sortPoints[1] + sortPoints[2]) / 2
|
|
if (
|
|
((currentRoof.x1 <= x && x <= currentRoof.x2) || (currentRoof.x2 <= x && x <= currentRoof.x1)) &&
|
|
((oppositeLine.x1 <= x && x <= oppositeLine.x2) || (oppositeLine.x2 <= x && x <= oppositeLine.x1))
|
|
) {
|
|
points.push(x, currentRoof.y1, x, oppositeLine.y1)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (currentRoof.attributes.type === LINE_TYPE.WALLLINE.JERKINHEAD) {
|
|
const gables = canvas
|
|
.getObjects()
|
|
.filter((object) => object.attributes?.currentRoofId === currentRoof.id && object.name === LINE_TYPE.SUBLINE.GABLE)
|
|
|
|
let x1, y1, x2, y2
|
|
x1 = (currentRoof.x1 + currentRoof.x2) / 2
|
|
y1 = (currentRoof.y1 + currentRoof.y2) / 2
|
|
|
|
if (currentRoof.x1 === currentRoof.x2) {
|
|
const xPoints = []
|
|
gables.forEach((gable) => {
|
|
if (gable.x1 !== x1) {
|
|
xPoints.push(gable.x1)
|
|
} else if (gable.x2 !== x1) {
|
|
xPoints.push(gable.x2)
|
|
}
|
|
})
|
|
x2 = xPoints.reduce((sum, current) => sum + current, 0) / xPoints.length
|
|
y2 = y1
|
|
} else {
|
|
const yPoints = []
|
|
gables.forEach((gable) => {
|
|
if (gable.y1 !== y1) {
|
|
yPoints.push(gable.y1)
|
|
} else if (gable.y2 !== y1) {
|
|
yPoints.push(gable.y2)
|
|
}
|
|
})
|
|
x2 = x1
|
|
y2 = yPoints.reduce((sum, current) => sum + current, 0) / yPoints.length
|
|
}
|
|
points.push(x1, y1, x2, y2)
|
|
}
|
|
}
|
|
if (points?.length > 0) {
|
|
const currentDegree = currentRoof.attributes.pitch > 0 ? getDegreeByChon(currentRoof.attributes.pitch) : currentRoof.attributes.degree
|
|
const length =
|
|
currentDegree !== undefined && currentDegree > 0
|
|
? calcLineActualSize({ x1: points[0], y1: points[1], x2: points[2], y2: points[3] }, currentDegree)
|
|
: calcLinePlaneSize({ x1: points[0], y1: points[1], x2: points[2], y2: points[3] })
|
|
const pitchSizeLine = new QLine(points, {
|
|
parentId: roof.id,
|
|
stroke: '#000000',
|
|
strokeWidth: 2,
|
|
strokeDashArray: [5, 5],
|
|
selectable: false,
|
|
fontSize: roof.fontSize,
|
|
textMode: textMode,
|
|
attributes: {
|
|
roofId: roof.id,
|
|
type: 'pitchSizeLine',
|
|
planeSize: length,
|
|
actualSize: length,
|
|
},
|
|
})
|
|
if (length > 0) {
|
|
canvas.add(pitchSizeLine)
|
|
canvas.renderAll()
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
function createRoofMarginPolygon(polygon, lines, arcSegments = 0) {
|
|
const offsetEdges = []
|
|
|
|
polygon.edges.forEach((edge, i) => {
|
|
const offset =
|
|
lines[i % lines.length].attributes.offset === undefined || lines[i % lines.length].attributes.offset === 0
|
|
? 0.1
|
|
: lines[i % lines.length].attributes.offset
|
|
const dx = edge.outwardNormal.x * offset
|
|
const dy = edge.outwardNormal.y * offset
|
|
offsetEdges.push(createOffsetEdge(edge, dx, dy))
|
|
})
|
|
|
|
const vertices = []
|
|
|
|
offsetEdges.forEach((thisEdge, 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,
|
|
})
|
|
}
|
|
})
|
|
|
|
const marginPolygon = createPolygon(vertices)
|
|
marginPolygon.offsetEdges = offsetEdges
|
|
return marginPolygon
|
|
}
|
|
|
|
function createRoofPaddingPolygon(polygon, lines, arcSegments = 0) {
|
|
const offsetEdges = []
|
|
|
|
polygon.edges.forEach((edge, i) => {
|
|
const offset =
|
|
lines[i % lines.length].attributes.offset === undefined || lines[i % lines.length].attributes.offset === 0
|
|
? 0.1
|
|
: lines[i % lines.length].attributes.offset
|
|
const dx = edge.inwardNormal.x * offset
|
|
const dy = edge.inwardNormal.y * offset
|
|
offsetEdges.push(createOffsetEdge(edge, dx, dy))
|
|
})
|
|
|
|
const vertices = []
|
|
|
|
offsetEdges.forEach((thisEdge, 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,
|
|
})
|
|
}
|
|
})
|
|
|
|
const paddingPolygon = createPolygon(vertices)
|
|
paddingPolygon.offsetEdges = offsetEdges
|
|
return paddingPolygon
|
|
}
|
|
|
|
function arePointsEqual(point1, point2) {
|
|
return Math.abs(point1.x - point2.x) <= 1 && Math.abs(point1.y - point2.y) <= 1
|
|
}
|
|
|
|
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((coordinate) => {
|
|
const point = turf.point(coordinate)
|
|
return turf.booleanPointInPolygon(point, polygonFeature)
|
|
})
|
|
|
|
// 다각형의 모든 점이 사각형 내부에 있지 않은지 확인
|
|
const noPolygonPointsInsideRect = polygonCoordinates.every((coordinate) => {
|
|
const point = turf.point(coordinate)
|
|
return !turf.booleanPointInPolygon(point, rectFeature)
|
|
})
|
|
|
|
return allPointsInsidePolygon && noPolygonPointsInsideRect
|
|
}
|
|
|
|
/**
|
|
* 포인트를 기준으로 선의 길이를 구한다. 선의 길이는 10을 곱하여 사용한다.
|
|
* @param points
|
|
* @returns number
|
|
*/
|
|
export const calcLinePlaneSize = (points) => {
|
|
const { x1, y1, x2, y2 } = points
|
|
return Big(x1).minus(x2).abs().pow(2).plus(Big(y1).minus(y2).abs().pow(2)).sqrt().times(10).round().toNumber()
|
|
}
|
|
|
|
/**
|
|
* 포인트와 기울기를 기준으로 선의 길이를 구한다.
|
|
* @param points
|
|
* @param degree
|
|
* @returns number
|
|
*/
|
|
export const calcLineActualSize = (points, degree = 0) => {
|
|
const { x1, y1, x2, y2 } = points
|
|
const planeSize = calcLinePlaneSize(points)
|
|
const theta = Big(Math.cos(Big(degree).times(Math.PI).div(180)))
|
|
return Big(planeSize).div(theta).round().toNumber()
|
|
}
|
|
|
|
export const createLinesFromPolygon = (points) => {
|
|
const lines = []
|
|
for (let i = 0; i < points.length; i++) {
|
|
const nextIndex = (i + 1) % points.length
|
|
const line = new fabric.Line([points[i].x, points[i].y, points[nextIndex].x, points[nextIndex].y], {
|
|
stroke: 'red',
|
|
strokeWidth: 2,
|
|
selectable: false,
|
|
evented: false,
|
|
})
|
|
lines.push(line)
|
|
}
|
|
return lines
|
|
}
|
|
|
|
/** 포인트 정렬 가장왼쪽, 가장위 부터 */
|
|
const getSortedPoint = (points) => {
|
|
const startPoint = points
|
|
.filter((point) => point.x === Math.min(...points.map((point) => point.x)))
|
|
.reduce((prev, curr) => {
|
|
return prev.y < curr.y ? prev : curr
|
|
})
|
|
const sortedPoints = []
|
|
sortedPoints.push(startPoint)
|
|
|
|
let prevPoint = startPoint
|
|
|
|
for (let i = 0; i < points.length - 1; i++) {
|
|
const samePoints = []
|
|
points
|
|
.filter((point) => !sortedPoints.includes(point))
|
|
.forEach((point) => {
|
|
if (i % 2 === 1 && prevPoint.y === point.y) {
|
|
samePoints.push({ point, size: Math.abs(point.x - prevPoint.x) })
|
|
}
|
|
if (i % 2 === 0 && prevPoint.x === point.x) {
|
|
samePoints.push({ point, size: Math.abs(point.y - prevPoint.y) })
|
|
}
|
|
})
|
|
const samePoint = samePoints.sort((a, b) => a.size - b.size)[0].point
|
|
sortedPoints.push(samePoint)
|
|
prevPoint = samePoint
|
|
}
|
|
return sortedPoints
|
|
}
|
|
|
|
const reCalculateSize = (line) => {
|
|
const oldPlaneSize = line.attributes.planeSize
|
|
const oldActualSize = line.attributes.actualSize
|
|
const theta = Big(Math.acos(Big(oldPlaneSize).div(oldActualSize)))
|
|
.times(180)
|
|
.div(Math.PI)
|
|
console.log('theta : ', theta.toNumber())
|
|
const planeSize = calcLinePlaneSize({
|
|
x1: line.x1,
|
|
y1: line.y1,
|
|
x2: line.x2,
|
|
y2: line.y2,
|
|
})
|
|
const actualSize =
|
|
planeSize === oldActualSize
|
|
? 0
|
|
: calcLineActualSize(
|
|
{
|
|
x1: line.x1,
|
|
y1: line.y1,
|
|
x2: line.x2,
|
|
y2: line.y2,
|
|
},
|
|
theta,
|
|
)
|
|
return { planeSize, actualSize }
|
|
}
|