This commit is contained in:
hyojun.choi 2025-02-13 17:14:58 +09:00
commit 11fb26e589
8 changed files with 67 additions and 131 deletions

View File

@ -2,7 +2,7 @@ import { fabric } from 'fabric'
import { v4 as uuidv4 } from 'uuid'
import { QLine } from '@/components/fabric/QLine'
import { distanceBetweenPoints, findTopTwoIndexesByDistance, getDirectionByPoint, sortedPointLessEightPoint, sortedPoints } from '@/util/canvas-util'
import { calculateAngle, drawGabledRoof, drawRidgeRoof, drawShedRoof, inPolygon, toGeoJSON } from '@/util/qpolygon-utils'
import { calculateAngle, drawGabledRoof, drawRidgeRoof, drawShedRoof, toGeoJSON } from '@/util/qpolygon-utils'
import * as turf from '@turf/turf'
import { LINE_TYPE, POLYGON_TYPE } from '@/common/common'
import Big from 'big.js'
@ -188,8 +188,28 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
})
},
// 보조선 그리기
/**
* 보조선 그리기
* @param settingModalFirstOptions
*/
drawHelpLine(settingModalFirstOptions) {
let textMode = 'plane'
const dimensionDisplay = settingModalFirstOptions?.dimensionDisplay.find((opt) => opt.selected).id
? settingModalFirstOptions?.dimensionDisplay.find((opt) => opt.selected).id
: 1
switch (dimensionDisplay) {
case 1:
textMode = 'plane'
break
case 2:
textMode = 'actual'
break
case 3:
textMode = 'none'
break
}
const types = []
this.lines.forEach((line) => types.push(line.attributes.type))
@ -206,7 +226,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
(gableOdd.every((type) => type === LINE_TYPE.WALLLINE.EAVES) && gableEven.every((type) => gableType.includes(type))) ||
(gableEven.every((type) => type === LINE_TYPE.WALLLINE.EAVES) && gableOdd.every((type) => gableType.includes(type)))
) {
drawGabledRoof(this.id, this.canvas, settingModalFirstOptions)
drawGabledRoof(this.id, this.canvas, textMode)
} else if (hasShed) {
const sheds = this.lines.filter((line) => line.attributes !== undefined && line.attributes.type === LINE_TYPE.WALLLINE.SHED)
const areLinesParallel = function (line1, line2) {
@ -233,18 +253,18 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
const gables = this.lines.filter((line) => sheds.includes(line) === false && eaves.includes(line) === false)
const isGable = gables.every((line) => gableType.includes(line.attributes.type))
if (isGable) {
drawShedRoof(this.id, this.canvas, settingModalFirstOptions)
drawShedRoof(this.id, this.canvas, textMode)
} else {
drawRidgeRoof(this.id, this.canvas, settingModalFirstOptions)
drawRidgeRoof(this.id, this.canvas, textMode)
}
} else {
drawRidgeRoof(this.id, this.canvas, settingModalFirstOptions)
drawRidgeRoof(this.id, this.canvas, textMode)
}
} else {
drawRidgeRoof(this.id, this.canvas, settingModalFirstOptions)
drawRidgeRoof(this.id, this.canvas, textMode)
}
} else {
drawRidgeRoof(this.id, this.canvas, settingModalFirstOptions)
drawRidgeRoof(this.id, this.canvas, textMode)
}
},
@ -264,11 +284,8 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
this.texts = []
points.forEach((start, i) => {
const end = points[(i + 1) % points.length]
// planeSize: Math.round(Math.sqrt(Math.pow(newLine.x1 - newLine.x2, 2) + Math.pow(newLine.y1 - newLine.y2, 2))) * 10,
// actualSize: Math.round(Math.sqrt(Math.pow(newLine.x1 - newLine.x2, 2) + Math.pow(newLine.y1 - newLine.y2, 2))) * 10,
const dx = Big(end.x).minus(Big(start.x))
const dy = Big(end.y).minus(Big(start.y))
// const length = Math.round(Math.sqrt(Math.pow(end.x - start.x, 2) + Math.pow(end.y - start.y, 2))) * 10
const length = dx.pow(2).plus(dy.pow(2)).sqrt().times(10).round().toNumber()
let midPoint
@ -323,75 +340,6 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
setCanvas(canvas) {
this.canvas = canvas
},
fillCell(cell = { width: 50, height: 100, padding: 10 }) {
const points = this.points
const minX = Math.min(...points.map((p) => p.x))
const maxX = Math.max(...points.map((p) => p.x))
const minY = Math.min(...points.map((p) => p.y))
const maxY = Math.max(...points.map((p) => p.y))
const boundingBoxWidth = maxX - minX
const boundingBoxHeight = maxY - minY
const rectWidth = cell.width
const rectHeight = cell.height
const cols = Math.floor((boundingBoxWidth + cell.padding) / (rectWidth + cell.padding))
const rows = Math.floor((boundingBoxHeight + cell.padding) / (rectHeight + cell.padding))
//전체 높이에서 패딩을 포함하고 rows를 곱해서 여백길이를 계산 후에 2로 나누면 반높이를 넣어서 중간으로 정렬
const tmpHeight = (boundingBoxHeight - (rectHeight + cell.padding) * rows) / 2
//센터 정렬시에 쓴다 체크박스가 존재함 TODO: if문 추가해서 정렬해야함
let tmpWidth = (boundingBoxWidth - (rectWidth + cell.padding) * cols) / 2
const drawCellsArray = [] //그려진 셀의 배열
let idx = 1
for (let i = 0; i < cols; i++) {
for (let j = 0; j < rows; j++) {
const rectLeft = minX + i * (rectWidth + cell.padding)
const rectTop = minY + j * (rectHeight + cell.padding)
const rectPoints = [
{ x: rectLeft, y: rectTop },
{ x: rectLeft, y: rectTop + rectHeight },
{ x: rectLeft + rectWidth, y: rectTop + rectHeight },
{ x: rectLeft + rectWidth, y: rectTop },
]
if (inPolygon(this.points, rectPoints)) {
const rect = new fabric.Rect({
left: rectLeft,
top: rectTop,
width: rectWidth,
height: rectHeight,
fill: '#BFFD9F',
stroke: 'black',
selectable: true, // 선택 가능하게 설정
lockMovementX: true, // X 축 이동 잠금
lockMovementY: true, // Y 축 이동 잠금
lockRotation: true, // 회전 잠금
lockScalingX: true, // X 축 크기 조정 잠금
lockScalingY: true, // Y 축 크기 조정 잠금
opacity: 0.8,
name: 'cell',
idx: idx,
parentId: this.id,
parent: this,
})
idx++
drawCellsArray.push(rect) //배열에 넣어서 반환한다
this.canvas.add(rect)
}
}
}
this.canvas?.renderAll()
this.cells = drawCellsArray
return drawCellsArray
},
fillCellABType(
cell = { width: 50, height: 100, padding: 5, wallDirection: 'left', referenceDirection: 'none', startIndex: -1, isCellCenter: false },
) {

View File

@ -227,8 +227,10 @@ export default function StuffDetail() {
},
},
{
field: 'supportMethodIdMulti',
headerName: getMessage('stuff.detail.planGridHeader.supportMethodIdMulti'),
//
// field: 'supportMethodIdMulti',
field: 'standTypeNo',
headerName: getMessage('stuff.detail.planGridHeader.standTypeNo'),
wrapText: true,
autoHeight: true,
cellStyle: { alignItems: 'flex-start' /* 좌측정렬*/, cursor: 'pointer' },

View File

@ -329,7 +329,10 @@ export function useRoofFn() {
const allRoofObject = canvas
.getObjects()
.filter((obj) => /*obj !== roof && obj !== wall &&*/ obj.attributes?.roofId === roof.id || obj.parentId === roof.id || obj.parentId === wall.id)
.filter(
(obj) => /*obj !== roof && obj !== wall &&*/ obj.attributes?.roofId === roof.id || obj.parentId === roof.id || obj.parentId === wall?.id,
)
const auxilaryObject = canvas.getObjects().filter((obj) => obj.name === 'auxiliaryLine' && !obj.isAuxiliaryFixed)
allRoofObject.forEach((obj) => {

View File

@ -28,8 +28,8 @@ export function useImgLoader() {
const minX = objects.reduce((acc, cur) => (cur.left < acc ? cur.left : acc), objects[0].left)
const minY = objects.reduce((acc, cur) => (cur.top < acc ? cur.top : acc), objects[0].top)
const maxX = objects.reduce((acc, cur) => (cur.left + cur.width > acc ? cur.left + cur.width : acc), 0)
const maxY = objects.reduce((acc, cur) => (cur.top + cur.height > acc ? cur.top + cur.height : acc), 0)
const maxX = objects.reduce((acc, cur) => (cur.left + cur.width > acc ? cur.left : acc), 0)
const maxY = objects.reduce((acc, cur) => (cur.top + cur.height > acc ? cur.top : acc), 0)
return [
{ x: minX - margin, y: minY - margin },
{ x: maxX + margin, y: maxY + margin },
@ -70,8 +70,8 @@ export function useImgLoader() {
// formData.append('coordinates', getImageCoordinates())
const positionObj = getImageCoordinates()
console.log('🚀 ~ handleCanvasToPng ~ positionObj:', positionObj)
formData.append('width', Math.round(positionObj[1].x - positionObj[0].x - 350))
formData.append('height', Math.round(positionObj[1].y - positionObj[0].y - 100))
formData.append('width', Math.round(positionObj[1].x - positionObj[0].x + 100))
formData.append('height', Math.round(positionObj[1].y - positionObj[0].y + 100))
formData.append('left', Math.round(positionObj[0].x))
formData.append('top', Math.round(positionObj[0].y))
console.log('🚀 ~ handleCanvasToPng ~ formData:', formData)

View File

@ -1797,6 +1797,10 @@ export function useMode() {
roof.direction = wall.direction
}
if (wall.attributes?.roofId) {
canvas
.getObjects()
.filter((obj) => obj.parentId === roof.id)
.forEach((obj) => obj.set('parentId', wall.attributes.roofId))
roof.id = wall.attributes.roofId
}
roof.name = POLYGON_TYPE.ROOF

View File

@ -782,7 +782,7 @@
"stuff.detail.planGridHeader.capacity": "システム容量",
"stuff.detail.planGridHeader.roofMaterialIdMulti": "屋根材",
"stuff.detail.planGridHeader.constructSpecificationMulti": "施工方法",
"stuff.detail.planGridHeader.supportMethodIdMulti": "架台",
"stuff.detail.planGridHeader.standTypeNo": "架台",
"stuff.detail.planGridHeader.pcTypeNo": "パワーコンディショナー",
"stuff.detail.planGridHeader.management": "管理",
"stuff.detail.planGrid.btn1": "見積書の照会",

View File

@ -782,7 +782,7 @@
"stuff.detail.planGridHeader.capacity": "시스템용량",
"stuff.detail.planGridHeader.roofMaterialIdMulti": "지붕재",
"stuff.detail.planGridHeader.constructSpecificationMulti": "시공방법",
"stuff.detail.planGridHeader.supportMethodIdMulti": "가대",
"stuff.detail.planGridHeader.standTypeNo": "가대",
"stuff.detail.planGridHeader.pcTypeNo": "파워컨디셔너",
"stuff.detail.planGridHeader.management": "관리",
"stuff.detail.planGrid.btn1": "견적서 조회",

View File

@ -312,8 +312,9 @@ export const isSamePoint = (a, b) => {
* 박공지붕(templateA, templateB) 그린다.
* @param roofId
* @param canvas
* @param textMode
*/
export const drawGabledRoof = (roofId, canvas, settingModalFirstOptions) => {
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
@ -323,15 +324,6 @@ export const drawGabledRoof = (roofId, canvas, settingModalFirstOptions) => {
return
}
// const roofPoints = roof.points
// const minX = Math.min(...roofPoints.map((point) => point.x))
// const maxX = Math.max(...roofPoints.map((point) => point.x))
// const minY = Math.min(...roofPoints.map((point) => point.y))
// const maxY = Math.max(...roofPoints.map((point) => point.y))
// 맞은편 라인을 찾기 위해 현재 polygon 으로 만들수 있는 최대한의 길이를 구한다.
// const checkLength = Math.abs(Math.sqrt(Math.pow(maxX - minX, 2) + Math.pow(maxY - minY, 2)))
// 처마라인의 기본속성 입력
const eaves = []
roofLines.forEach((currentRoof, index) => {
@ -650,8 +642,9 @@ export const drawGabledRoof = (roofId, canvas, settingModalFirstOptions) => {
* 한쪽흐름 지붕
* @param roofId
* @param canvas
* @param textMode
*/
export const drawShedRoof = (roofId, canvas, settingModalFirstOptions) => {
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) {
@ -673,11 +666,9 @@ export const drawShedRoof = (roofId, canvas, settingModalFirstOptions) => {
return Math.tan(degree * (Math.PI / 180)) * adjust
}
gables.forEach((gable) => {
const adjust = gable.attributes.planeSize
const height = getHeight(adjust, shedDegree)
gable.attributes.actualSize = Math.round(Math.sqrt(Math.pow(adjust, 2) + Math.pow(height, 2)))
})
gables.forEach(
(gable) => (gable.attributes.actualSize = calcLineActualSize({ x1: gable.x1, y1: gable.y1, x2: gable.x2, y2: gable.y2 }, shedDegree)),
)
const pitchSizeLines = []
@ -707,8 +698,11 @@ export const drawShedRoof = (roofId, canvas, settingModalFirstOptions) => {
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 actualSize = Math.round(Math.sqrt(Math.pow(planeSize, 2) + Math.pow(getHeight(planeSize, shedDegree), 2)) * 10)
// 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',
@ -716,10 +710,11 @@ export const drawShedRoof = (roofId, canvas, settingModalFirstOptions) => {
strokeDashArray: [5, 5],
selectable: false,
fontSize: roof.fontSize,
textMode: textMode,
attributes: {
roofId: roof.id,
type: 'pitchSizeLine',
planeSize: planeSize,
planeSize: actualSize,
actualSize: actualSize,
},
})
@ -730,33 +725,17 @@ export const drawShedRoof = (roofId, canvas, settingModalFirstOptions) => {
const maxLine = pitchSizeLines.reduce((prev, current) => (prev.length > current.length ? prev : current), pitchSizeLines[0])
canvas.add(maxLine)
canvas.renderAll()
const adjust = Math.sqrt(
Math.pow(Math.round(Math.abs(maxLine.x1 - maxLine.x2) * 10), 2) + Math.pow(Math.round(Math.abs(maxLine.y1 - maxLine.y2) * 10), 2),
)
const height = getHeight(adjust, shedDegree)
const lengthText = Math.round(Math.sqrt(Math.pow(adjust, 2) + Math.pow(height, 2)))
maxLine.setLengthText(lengthText)
}
export const drawRidgeRoof = (roofId, canvas, settingModalFirstOptions) => {
/**
* 마루가 있는 지붕을 그린다.
* @param roofId
* @param canvas
* @param textMode
*/
export const drawRidgeRoof = (roofId, canvas, textMode) => {
const roof = canvas?.getObjects().find((object) => object.id === roofId)
let textMode = 'plane'
const dimensionDisplay = settingModalFirstOptions?.dimensionDisplay.find((opt) => opt.selected).id
? settingModalFirstOptions?.dimensionDisplay.find((opt) => opt.selected).id
: 1
switch (dimensionDisplay) {
case 1:
textMode = 'plane'
break
case 2:
textMode = 'actual'
break
case 3:
textMode = 'none'
break
}
//Math.abs(line.x1 - line.x2) > 1 && Math.abs(line.y1 - line.y2) > 1
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) {