From 34816b6e16584de302301227cf07ca03781bdbba Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Thu, 1 Aug 2024 18:05:59 +0900 Subject: [PATCH 1/7] =?UTF-8?q?option=EC=9D=98=20sort=EB=A5=BC=20=ED=99=95?= =?UTF-8?q?=EC=9D=B8=ED=95=B4=20=EC=A0=95=EB=A0=AC=20=EB=B0=A9=EC=8B=9D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/fabric/QPolygon.js | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js index 1817862a..93e7ff65 100644 --- a/src/components/fabric/QPolygon.js +++ b/src/components/fabric/QPolygon.js @@ -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 { drawHelpLineInHexagon } from '@/util/qpolygon-utils' +import { calculateAngle, dividePolygon, drawHelpLineInHexagon } from '@/util/qpolygon-utils' export const QPolygon = fabric.util.createClass(fabric.Polygon, { type: 'QPolygon', @@ -19,10 +19,25 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { point.x = Math.round(point.x) point.y = Math.round(point.y) }) - if (points.length <= 8) { + options.sort = options.sort ?? true + if (!options.sort && points.length <= 8) { points = sortedPointLessEightPoint(points) } else { - points = sortedPoints(points) + let isDiagonal = false + points.forEach((point, i) => { + if (isDiagonal) { + return + } + const nextPoint = points[(i + 1) % points.length] + const angle = calculateAngle(point, nextPoint) + if (!(Math.abs(angle) === 0 || Math.abs(angle) === 180)) { + isDiagonal = true + } + }) + + if (!isDiagonal) { + points = sortedPoints(points) + } } this.callSuper('initialize', points, options) @@ -77,6 +92,9 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { return fabric.util.object.extend(this.callSuper('toObject', propertiesToInclude), { type: this.type, text: this.text, + hips: this.hips, + ridges: this.ridges, + connectRidges: this.connectRidges, }) }, init: function () { @@ -119,6 +137,8 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { direction: getDirectionByPoint(point, nextPoint), idx: i, }) + line.startPoint = point + line.endPoint = nextPoint this.lines.push(line) }) }, @@ -326,5 +346,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { text.set({ visible: isView }) }) }, - divideLine() {}, + divideLine() { + dividePolygon(this) + }, }) From 94a7502e6ee72a845b4fa56cc1fa0ca69519f7c5 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Thu, 1 Aug 2024 18:07:43 +0900 Subject: [PATCH 2/7] =?UTF-8?q?polygon=20divide=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Roof2.jsx | 50 ++++- src/components/fabric/QLine.js | 40 ++-- src/hooks/useCanvas.js | 20 +- src/hooks/useMode.js | 8 +- src/util/qline-utils.js | 14 +- src/util/qpolygon-utils.js | 353 ++++++++++++++++++++++++++++++--- 6 files changed, 418 insertions(+), 67 deletions(-) diff --git a/src/components/Roof2.jsx b/src/components/Roof2.jsx index e3009432..8d2fc35b 100644 --- a/src/components/Roof2.jsx +++ b/src/components/Roof2.jsx @@ -257,20 +257,64 @@ export default function Roof2() { ] const types = [type1, type2, type3, type4, type1A, type1B, eightPoint, eightPoint2, eightPoint3, eightPoint4, twelvePoint] - - const polygon = new QPolygon(type1B, { + const newP = [ + { x: 450, y: 450 }, + { x: 650, y: 250 }, + { x: 675, y: 275 }, + { x: 450, y: 850 }, + ] + const polygon = new QPolygon(type4, { fill: 'transparent', stroke: 'black', strokeWidth: 1, - selectable: true, + selectable: false, fontSize: fontSize, name: 'QPolygon1', }) canvas?.add(polygon) + polygon.set('strokeDashArray', [10, 5, 2, 5]) + polygon.set('stroke', 'blue') + polygon.set('strokeWidth', 1) + + // const newPolygon = fabric.util.clone(polygon) + handleOuterlinesTest2(polygon) + const roofs = canvas?.getObjects().filter((obj) => obj.name === 'roof') + + roofs.forEach((roof) => { + let maxLengthLine = roof.lines.reduce((acc, cur) => { + return acc.length > cur.length ? acc : cur + }) + + // 패턴 소스를 위한 임시 캔버스 생성 + const patternSourceCanvas = document.createElement('canvas') + if (maxLengthLine.direction === 'right' || maxLengthLine.direction === 'left') { + patternSourceCanvas.width = 20 * window.devicePixelRatio || 1 + patternSourceCanvas.height = 10 * window.devicePixelRatio || 1 + } else { + patternSourceCanvas.width = 10 * window.devicePixelRatio || 1 + patternSourceCanvas.height = 20 * window.devicePixelRatio || 1 + } + + const ctx = patternSourceCanvas.getContext('2d') + + // 벽돌 패턴 그리기 + ctx.scale(window.devicePixelRatio || 1, window.devicePixelRatio || 1) + ctx.strokeStyle = 'green' + ctx.lineWidth = 0.4 + ctx.strokeRect(0, 0, 100, 100) + // 패턴 생성 + const pattern = new fabric.Pattern({ + source: patternSourceCanvas, + repeat: 'repeat', + }) + + roof.set('fill', pattern) + }) + // const lines = togglePolygonLine(polygon) // togglePolygonLine(lines[0]) } diff --git a/src/components/fabric/QLine.js b/src/components/fabric/QLine.js index 2d1e87df..e6a2bc65 100644 --- a/src/components/fabric/QLine.js +++ b/src/components/fabric/QLine.js @@ -10,8 +10,9 @@ export const QLine = fabric.util.createClass(fabric.Line, { length: 0, direction: null, idx: 0, + area: 0, initialize: function (points, options, canvas) { - this.callSuper('initialize', points, options) + this.callSuper('initialize', points, { ...options, selectable: options.selectable ?? false }) if (options.id) { this.id = options.id } else { @@ -23,16 +24,9 @@ export const QLine = fabric.util.createClass(fabric.Line, { point = Math.round(point) }) - const scaleX = this.scaleX - const scaleY = this.scaleY - const x1 = this.left - const y1 = this.top - const x2 = this.left + this.width * scaleX - const y2 = this.top + this.height * scaleY this.idx = options.idx ?? 0 - const dx = x2 - x1 - const dy = y2 - y1 - this.length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(0)) + + this.setLength() this.direction = options.direction ?? getDirection({ x: this.x1, y: this.y1 }, { x: this.x2, y: this.y2 }) @@ -67,9 +61,22 @@ export const QLine = fabric.util.createClass(fabric.Line, { }) }, + setLength() { + const scaleX = this.scaleX + const scaleY = this.scaleY + const x1 = this.left + const y1 = this.top + const x2 = this.left + this.width * scaleX + const y2 = this.top + this.height * scaleY + const dx = x2 - x1 + const dy = y2 - y1 + this.length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(0)) + }, + addLengthText() { const thisText = this.canvas.getObjects().find((obj) => obj.name === 'lengthText' && obj.parentId === this.id) + this.setLength() const scaleX = this.scaleX const scaleY = this.scaleY const x1 = this.left @@ -79,13 +86,10 @@ export const QLine = fabric.util.createClass(fabric.Line, { if (thisText) { thisText.set({ text: this.length.toFixed(0).toString(), left: (x1 + x2) / 2, top: (y1 + y2) / 2 }) + this.text = thisText return } - const dx = x2 - x1 - const dy = y2 - y1 - this.length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(0)) - const text = new fabric.Textbox(this.length.toFixed(0).toString(), { left: (x1 + x2) / 2, top: (y1 + y2) / 2, @@ -113,4 +117,12 @@ export const QLine = fabric.util.createClass(fabric.Line, { setCanvas(canvas) { this.canvas = canvas }, + + setViewLengthText(bool) { + const thisText = this.canvas.getObjects().find((obj) => obj.name === 'lengthText' && obj.parentId === this.id) + if (thisText) { + thisText.set({ visible: bool }) + } + return this + }, }) diff --git a/src/hooks/useCanvas.js b/src/hooks/useCanvas.js index c1ba723a..31b223bf 100644 --- a/src/hooks/useCanvas.js +++ b/src/hooks/useCanvas.js @@ -7,6 +7,8 @@ import { canvasSizeState, fontSizeState } from '@/store/canvasAtom' import { QLine } from '@/components/fabric/QLine' import QRect from '@/components/fabric/QRect' import { QPolygon } from '@/components/fabric/QPolygon' +import { defineQLine } from '@/util/qline-utils' +import { defineQPloygon } from '@/util/qpolygon-utils' export function useCanvas(id) { const [canvas, setCanvas] = useState() @@ -115,22 +117,8 @@ export function useCanvas(id) { QPolygon.prototype.canvas = canvas QLine.prototype.canvas = canvas QRect.prototype.canvas = canvas - - fabric.QLine.fromObject = function (object, callback) { - function _callback(instance) { - delete instance.points - callback && callback(instance) - } - - const options = fabric.util.object.clone(object, true) - options.points = [object.x1, object.y1, object.x2, object.y2] - - fabric.Object._fromObject('QLine', options, _callback, 'points') - } - - fabric.QPolygon.fromObject = function (object, callback) { - fabric.Object._fromObject('QPolygon', object, callback, 'points') - } + defineQLine() + defineQPloygon() } /** diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index 4533d7a2..563dd65a 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -18,7 +18,7 @@ import { fabric } from 'fabric' import { QPolygon } from '@/components/fabric/QPolygon' export const Mode = { - DRAW_LINE: 'drawLine', // 기준선 긋기모드 + DRAW_LINE: 'drawLine', // 기준선 긋기모드` EDIT: 'edit', TEMPLATE: 'template', PATTERNA: 'patterna', @@ -641,6 +641,9 @@ export function useMode() { * a : 시작점, b : 끝점 */ const drawLineWithLength = (a, b) => { + if (!a || !b) { + return + } const line = new QLine([a.left, a.top, b.left, b.top], { stroke: 'black', strokeWidth: 2, @@ -700,7 +703,6 @@ export function useMode() { stroke: 'black', fill: 'transparent', viewLengthText: true, - selectable: true, fontSize: fontSize, }, canvas, @@ -1174,6 +1176,7 @@ export function useMode() { setRoof(roof) roof.drawHelpLine() + roof.divideLine() } const togglePolygonLine = (obj) => { @@ -3237,6 +3240,7 @@ export function useMode() { fill: pattern, selectable: false, fontSize: 15, // fontSize는 필요에 따라 조정 + sort: false, lockMovementX: true, lockMovementY: true, lockRotation: true, diff --git a/src/util/qline-utils.js b/src/util/qline-utils.js index 77821886..6443efb2 100644 --- a/src/util/qline-utils.js +++ b/src/util/qline-utils.js @@ -2,8 +2,14 @@ import { fabric } from 'fabric' import { QLine } from '@/components/fabric/QLine' export const defineQLine = () => { - /*fabric.QLine = QLine - fabric.QLine.fromObject = (object, callback) => { - return new fabric.QLine([object.x1, object.y1, object.x2, object.y2], object) - }*/ + fabric.QLine.fromObject = function (object, callback) { + function _callback(instance) { + delete instance.points + callback && callback(instance) + } + const options = fabric.util.object.clone(object, true) + options.points = [object.x1, object.y1, object.x2, object.y2] + + fabric.Object._fromObject('QLine', options, _callback, 'points') + } } diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index af5d676e..078303d0 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -1,6 +1,7 @@ import { fabric } from 'fabric' import { QLine } from '@/components/fabric/QLine' -import { calculateIntersection, distanceBetweenPoints, findClosestPoint } from '@/util/canvas-util' +import { calculateIntersection, distanceBetweenPoints, findClosestPoint, getDirectionByPoint } from '@/util/canvas-util' +import { QPolygon } from '@/components/fabric/QPolygon' export const defineQPloygon = () => { fabric.QPolygon.fromObject = function (object, callback) { @@ -19,7 +20,7 @@ export const drawHelpLineInHexagon = (polygon, chon) => { const ridgeStartPoints = [] const ridgeEndPoints = [] - const centerInterSectionPoints = [] + let centerInterSectionPoints = [] // polygon.lines = polygon.lines.sort((a, b) => a.length - b.length) polygon.wall.lines = getOneSideLines(polygon.wall) @@ -101,9 +102,9 @@ export const drawHelpLineInHexagon = (polygon, chon) => { : nextLine.startPoint line.connectedPoint = { interSectionPoint, area, startPoint, endPoint } - line.connectedPoints.push(interSectionPoint) + line.connectedPoints.push({ interSectionPoint, area, startPoint, endPoint }) nextLine.connectedPoint = { interSectionPoint, area, startPoint, endPoint } - nextLine.connectedPoints.push(interSectionPoint) + nextLine.connectedPoints.push({ interSectionPoint, area, startPoint, endPoint }) } } }) @@ -150,6 +151,12 @@ export const drawHelpLineInHexagon = (polygon, chon) => { name: 'hip', }) + line.startPoint = point.startPoint + line.endPoint = point.interSectionPoint + + line2.startPoint = point.endPoint + line2.endPoint = point.interSectionPoint + polygon.hips.push(line) polygon.hips.push(line2) @@ -165,20 +172,26 @@ export const drawHelpLineInHexagon = (polygon, chon) => { uniqueInterSectionPoints.forEach((point) => { const interSectionPoint = point.interSectionPoint - if (connectedPoint.x === interSectionPoint.x && connectedPoint.y === interSectionPoint.y) { + if (connectedPoint.interSectionPoint.x === interSectionPoint.x && connectedPoint.interSectionPoint.y === interSectionPoint.y) { removedIdx.push(line.idx) } }) }) }) - const notIntersectedLines = helpLines.filter((line) => !removedIdx.includes(line.idx)) + let notIntersectedLines = helpLines.filter((line) => !removedIdx.includes(line.idx)) + + notIntersectedLines = notIntersectedLines.map((line) => { + return { ...line, centerInterSectionPoints: [] } + }) notIntersectedLines.forEach((line) => { centerLines.forEach((centerLine) => { const interSectionPoint = calculateIntersection(line, centerLine) if (interSectionPoint && polygon.inPolygon(interSectionPoint) && polygon.wall.inPolygon(interSectionPoint)) { + line.centerInterSectionPoints.push(interSectionPoint) + interSectionPoint.lineIdx = line.idx centerInterSectionPoints.push(interSectionPoint) } }) @@ -187,11 +200,12 @@ export const drawHelpLineInHexagon = (polygon, chon) => { // centerInterSectionPoints에서 ridgeStartPoints와 x가 같거나 y가 같은것중 가장 가까운 점들을 찾는다. ridgeStartPoints.forEach((point) => { const xPoints = centerInterSectionPoints.filter((centerPoint) => Math.abs(centerPoint.x - point.x) < 2) + const yPoints = centerInterSectionPoints.filter((centerPoint) => Math.abs(centerPoint.y - point.y) < 2) let closestPoint if (xPoints.length === 0) { closestPoint = findClosestPoint(point, yPoints) - } else { + } else if (yPoints.length === 0) { closestPoint = findClosestPoint(point, xPoints) } @@ -200,44 +214,119 @@ export const drawHelpLineInHexagon = (polygon, chon) => { stroke: 'purple', fontSize: polygon.fontSize, name: 'ridge', + direction: getDirectionByPoint(point, closestPoint), }) + + line.startPoint = point + line.endPoint = closestPoint + polygon.ridges.push(line) polygon.canvas.add(line) ridgeEndPoints.push(closestPoint) + + notIntersectedLines = notIntersectedLines.filter((line) => line.idx !== closestPoint.lineIdx) } }) + centerInterSectionPoints = [] + notIntersectedLines.forEach((line) => { + centerInterSectionPoints.push(...line.centerInterSectionPoints) + }) + // ridgeEndPoints끼리 이어준다. - const remainingPoints = ridgeEndPoints + const remainingPoints = [...ridgeEndPoints] + + // ridgeEndPoint에서 centerInterSectionPoints와 45도인 점을 찾아 이어준다. + + ridgeEndPoints.forEach((ridgePoint) => { + const filteredCenterInterSectionPoints = centerInterSectionPoints.filter((centerPoint) => { + const degree = calculateAngle(ridgePoint, centerPoint) + return Math.abs(degree) === 45 || Math.abs(degree) === 135 + })[0] + + if (filteredCenterInterSectionPoints) { + const line = new QLine([ridgePoint.x, ridgePoint.y, filteredCenterInterSectionPoints.x, filteredCenterInterSectionPoints.y], { + stroke: 'purple', + fontSize: polygon.fontSize, + name: 'hip', + }) + + line.startPoint = ridgePoint + line.endPoint = filteredCenterInterSectionPoints + + polygon.hips.push(line) + polygon.canvas.add(line) + + ridgeStartPoints.push(filteredCenterInterSectionPoints) + + polygon.points.forEach((point) => { + const degree = calculateAngle(ridgePoint, point) + + if (Math.abs(degree) % 45 < 1) { + const line = new QLine([ridgePoint.x, ridgePoint.y, point.x, point.y], { + stroke: 'purple', + fontSize: polygon.fontSize, + name: 'hip', + }) + + polygon.hips.push(line) + polygon.canvas.add(line) + } + }) + } + }) + + // ridgeEndPoint끼리 연결한다. + while (remainingPoints.length > 1) { + const startPoint = remainingPoints.shift() + const endPoint = remainingPoints.shift() + + const line = new QLine([startPoint.x, startPoint.y, endPoint.x, endPoint.y], { + stroke: 'purple', + fontSize: polygon.fontSize, + name: 'connectRidge', + }) + + line.startPoint = startPoint + line.endPoint = endPoint + + polygon.connectRidges.push(line) - remainingPoints.forEach((ridgePoint) => { polygon.points.forEach((point) => { - const degree = calculateAngle(ridgePoint, point) + const degree = calculateAngle(startPoint, point) - if (Math.abs(degree) % 45 < 1) { - const line = new QLine([ridgePoint.x, ridgePoint.y, point.x, point.y], { + if (Math.abs(degree) === 45 || Math.abs(degree) === 135) { + const line = new QLine([startPoint.x, startPoint.y, point.x, point.y], { stroke: 'purple', fontSize: polygon.fontSize, name: 'hip', }) + line.startPoint = startPoint + line.endPoint = point + polygon.hips.push(line) polygon.canvas.add(line) } }) - }) - while (remainingPoints.length > 0) { - const point = remainingPoints.shift() - const closestPoint = findClosestPoint(point, remainingPoints) - if (!closestPoint) continue - // 마루끼리 연결 - const line = new QLine([point.x, point.y, closestPoint.x, closestPoint.y], { - stroke: 'purple', - fontSize: polygon.fontSize, - name: 'connectRidge', + polygon.points.forEach((point) => { + const degree = calculateAngle(endPoint, point) + + if (Math.abs(degree) === 45 || Math.abs(degree) === 135) { + const line = new QLine([endPoint.x, endPoint.y, point.x, point.y], { + stroke: 'purple', + fontSize: polygon.fontSize, + name: 'hip', + }) + + line.startPoint = endPoint + line.endPoint = point + + polygon.hips.push(line) + polygon.canvas.add(line) + } }) - polygon.connectRidges.push(line) polygon.canvas.add(line) } @@ -246,7 +335,7 @@ export const drawHelpLineInHexagon = (polygon, chon) => { export const drawCenterLines = (polygon) => { const centerLines = [] - const oneSideLines = getOneSideLines(polygon) + const oneSideLines = polygon.lines.map((line) => getOneSideLine(line)) const horizontalLines = oneSideLines.filter((line) => line.direction === 'right') const verticalLines = oneSideLines.filter((line) => line.direction === 'bottom') @@ -261,9 +350,6 @@ export const drawCenterLines = (polygon) => { horizontalLines.forEach((line, index) => { const nextLine = horizontalLines[(index + 1) % horizontalLines.length] - line.set({ strokeWidth: 5 }) - nextLine.set({ strokeWidth: 5 }) - polygon.canvas.renderAll() const startCenterX = Math.min(line.x1, nextLine.x1) @@ -319,6 +405,8 @@ const getOneSideLines = (polygon) => { line.x2 = newX2 line.y2 = newY2 line.direction = 'bottom' + line.startPoint = { x: newX1, y: newY1 } + line.endPoint = { x: newX2, y: newY2 } } else if (line.direction === 'left') { newX1 = line.x2 newY1 = line.y2 @@ -330,11 +418,13 @@ const getOneSideLines = (polygon) => { line.x2 = newX2 line.y2 = newY2 line.direction = 'right' + line.startPoint = { x: newX1, y: newY1 } + line.endPoint = { x: newX2, y: newY2 } } return line }) } -const calculateAngle = (point1, point2) => { +export const calculateAngle = (point1, point2) => { const deltaX = point2.x - point1.x const deltaY = point2.y - point1.y const angleInRadians = Math.atan2(deltaY, deltaX) @@ -375,3 +465,210 @@ const calculateTriangleArea = (point1, point2, point3) => { return Math.abs(x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2)) / 2 } + +// polygon을 나눈다. +export const dividePolygon = (polygon) => { + let hips = polygon.hips + const ridges = polygon.ridges.map((ridge) => getOneSideLine(ridge)) + const connectRidges = polygon.connectRidges + const polygonLines = polygon.lines + + hips.forEach((hip) => { + // hips의 startPoint와 endPoint를 polygon의 points와 비교하여 같은 점이 endPoint일 경우 startPoint로 변경한다. + const startPoint = polygon.points.find((point) => point.x === hip.endPoint.x && point.y === hip.endPoint.y) + if (startPoint) { + const temp = hip.startPoint + hip.startPoint = hip.endPoint + hip.endPoint = temp + } + }) + hips = [...hips, ...connectRidges] + polygon.setViewLengthText(false) + + polygonLines.forEach((line) => { + let ridge + + const startPoint = line.startPoint + const endPoint = line.endPoint + let polygonPoints = [] + + polygonPoints.push(startPoint) + + polygonPoints.push(endPoint) + + const startHip = hips.find((hip) => hip.startPoint.x === startPoint.x && hip.startPoint.y === startPoint.y) + const endHip = hips.find((hip) => hip.startPoint.x === endPoint.x && hip.startPoint.y === endPoint.y) + + if (startHip && endHip && startHip.endPoint.x === endHip.endPoint.x && startHip.endPoint.y === endHip.endPoint.y) { + polygonPoints.push(startHip.endPoint) + + const newPolygon = new QPolygon(polygonPoints, { + fontSize: polygon.fontSize, + id: polygon.id, + name: 'roof', + selectable: true, + stroke: 'black', + fill: 'transparent', + strokeWidth: 3, + }) + + polygon.canvas.add(newPolygon) + return + } + + let connectedRidge = ridges.find( + (ridge) => + (ridge.startPoint.x === startHip.endPoint.x && ridge.startPoint.y === startHip.endPoint.y) || + (ridge.endPoint.x === startHip.endPoint.x && ridge.endPoint.y === startHip.endPoint.y), + ) + + const hipStartPoint = startHip.endPoint + const hipEndPoint = endHip.endPoint + const restRidgeConnection = connectRidges[0] + + if (connectedRidge.startPoint.x === hipStartPoint.x && connectedRidge.startPoint.y === hipStartPoint.y) { + if (connectedRidge.endPoint.x === hipEndPoint.x && connectedRidge.endPoint.y === hipEndPoint.y) { + polygonPoints.push(connectedRidge.endPoint) + polygonPoints.push(connectedRidge.startPoint) + + const newPolygon = new QPolygon(polygonPoints, { + fontSize: polygon.fontSize, + id: polygon.id, + name: 'roof', + selectable: true, + stroke: 'black', + fill: 'transparent', + strokeWidth: 3, + }) + + polygon.canvas.add(newPolygon) + return + } + } else if (connectedRidge.endPoint.x === hipStartPoint.x && connectedRidge.endPoint.y === hipStartPoint.y) { + if (connectedRidge.startPoint.x === hipEndPoint.x && connectedRidge.startPoint.y === hipEndPoint.y) { + polygonPoints.push(connectedRidge.startPoint) + polygonPoints.push(connectedRidge.endPoint) + + const newPolygon = new QPolygon(polygonPoints, { + fontSize: polygon.fontSize, + id: polygon.id, + name: 'roof', + selectable: true, + stroke: 'black', + fill: 'transparent', + strokeWidth: 3, + sort: true, + }) + + polygon.canvas.add(newPolygon) + return + } + } + + // 지붕이 꺾여있는 경우 + + if ( + (restRidgeConnection.startPoint.x === startHip.endPoint.x && restRidgeConnection.startPoint.y === startHip.endPoint.y) || + (restRidgeConnection.endPoint.x === startHip.endPoint.x && restRidgeConnection.endPoint.y === startHip.endPoint.y) + ) { + polygonPoints = [startPoint, startHip.endPoint] + let lastPoint + + if (restRidgeConnection.startPoint.x === startHip.endPoint.x && restRidgeConnection.startPoint.y === startHip.endPoint.y) { + lastPoint = restRidgeConnection.endPoint + polygonPoints.push(restRidgeConnection.endPoint) + } else { + lastPoint = restRidgeConnection.startPoint + polygonPoints.push(restRidgeConnection.startPoint) + } + + connectedRidge = ridges.find( + (ridge) => + (ridge.startPoint.x === lastPoint.x && ridge.startPoint.y === lastPoint.y) || + (ridge.endPoint.x === lastPoint.x && ridge.endPoint.y === lastPoint.y), + ) + + if (connectedRidge.startPoint.x === lastPoint.x && connectedRidge.startPoint.y === lastPoint.y) { + polygonPoints.push(connectedRidge.endPoint) + } else { + polygonPoints.push(connectedRidge.startPoint) + } + + polygonPoints.push(endPoint) + } else { + polygonPoints = [endPoint, endHip.endPoint] + let lastPoint + + if (restRidgeConnection.startPoint.x === endHip.endPoint.x && restRidgeConnection.startPoint.y === endHip.endPoint.y) { + lastPoint = restRidgeConnection.endPoint + polygonPoints.push(restRidgeConnection.endPoint) + } else { + lastPoint = restRidgeConnection.startPoint + polygonPoints.push(restRidgeConnection.startPoint) + } + + connectedRidge = ridges.find( + (ridge) => + (ridge.startPoint.x === lastPoint.x && ridge.startPoint.y === lastPoint.y) || + (ridge.endPoint.x === lastPoint.x && ridge.endPoint.y === lastPoint.y), + ) + + if (connectedRidge.startPoint.x === startHip.endPoint.x && connectedRidge.startPoint.y === startHip.endPoint.y) { + lastPoint = connectedRidge.startPoint + polygonPoints.push(connectedRidge.startPoint) + } else { + lastPoint = connectedRidge.endPoint + polygonPoints.push(connectedRidge.endPoint) + } + + polygonPoints.push(startPoint) + } + + const newPolygon = new QPolygon(polygonPoints, { + fontSize: polygon.fontSize, + id: polygon.id, + name: 'roof', + selectable: true, + stroke: 'black', + fill: 'transparent', + strokeWidth: 3, + }) + + polygon.canvas.add(newPolygon) + }) +} + +const getOneSideLine = (line) => { + // left, top 방향의 line은 right, bottom 방향의 line으로 변경한다. + const newLine = { ...line } + let newX1, newY1, newX2, newY2 + if (newLine.direction === 'top') { + newX1 = newLine.x2 + newY1 = newLine.y2 + newX2 = newLine.x1 + newY2 = newLine.y1 + + newLine.x1 = newX1 + newLine.y1 = newY1 + newLine.x2 = newX2 + newLine.y2 = newY2 + newLine.direction = 'bottom' + newLine.startPoint = { x: newX1, y: newY1 } + newLine.endPoint = { x: newX2, y: newY2 } + } else if (line.direction === 'left') { + newX1 = newLine.x2 + newY1 = newLine.y2 + newX2 = newLine.x1 + newY2 = newLine.y1 + + newLine.x1 = newX1 + newLine.y1 = newY1 + newLine.x2 = newX2 + newLine.y2 = newY2 + newLine.direction = 'right' + newLine.startPoint = { x: newX1, y: newY1 } + newLine.endPoint = { x: newX2, y: newY2 } + } + + return newLine +} From 497325f4ae2ce19f8ad88d39b309d1b9f5895102 Mon Sep 17 00:00:00 2001 From: yjnoh Date: Thu, 1 Aug 2024 18:09:17 +0900 Subject: [PATCH 3/7] =?UTF-8?q?=EC=85=80=20=EC=83=9D=EC=84=B1=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EC=9E=91=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/fabric/QPolygon.js | 7 +++- src/hooks/useMode.js | 65 +++++++++++++++++++++++++------ src/store/canvasAtom.js | 7 ++++ 3 files changed, 65 insertions(+), 14 deletions(-) diff --git a/src/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js index 1817862a..37835724 100644 --- a/src/components/fabric/QPolygon.js +++ b/src/components/fabric/QPolygon.js @@ -201,6 +201,8 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { //센터 정렬시에 쓴다 체크박스가 존재함 TODO: if문 추가해서 정렬해야함 let tmpWidth = (boundingBoxWidth - (rectWidth + cell.padding) * cols) / 2 + const drawCellsArray = [] //그려진 셀의 배열 + for (let i = 0; i < cols; i++) { for (let j = 0; j < rows; j++) { const rectLeft = minX + i * (rectWidth + cell.padding) + tmpWidth @@ -228,14 +230,15 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { lockRotation: true, // 회전 잠금 lockScalingX: true, // X 축 크기 조정 잠금 lockScalingY: true, // Y 축 크기 조정 잠금 - opacity: 0.6, + opacity: 0.8, }) - + drawCellsArray.push(rect) //배열에 넣어서 반환한다 this.canvas.add(rect) } } } this.canvas?.renderAll() + return drawCellsArray }, inPolygon(point) { const vertices = this.getCurrentPoints() diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index 4533d7a2..5d8ac85b 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -5,6 +5,7 @@ import { useRecoilState } from 'recoil' import { canvasSizeState, + drewRoofCellsState, fontSizeState, roofPolygonArrayState, roofPolygonPatternArrayState, @@ -25,10 +26,9 @@ export const Mode = { PATTERNB: 'patternb', TEXTBOX: 'textbox', DRAW_RECT: 'drawRect', - ROOF_PATTERN: 'roofPattern', - MODULE: 'module', - ROOF_TRESTLE: 'roofTrestle', - FILL_CELLS: 'fillCells', + ROOF_PATTERN: 'roofPattern', //지붕패턴 모드 + ROOF_TRESTLE: 'roofTrestle', //지붕가대 모드 + FILL_CELLS: 'fillCells', //태양광셀 모드 DEFAULT: 'default', } @@ -57,6 +57,7 @@ export function useMode() { const [canvasSize] = useRecoilState(canvasSizeState) const [selectedCellRoofArray, setSelectedCellRoofArray] = useState([]) + const [drewRoofCells, setDrewRoofCells] = useRecoilState(drewRoofCellsState) useEffect(() => { // 이벤트 리스너 추가 @@ -3175,23 +3176,27 @@ export function useMode() { canvas?.renderAll() } + /** + * 지붕 패턴 생성 로직 + * @param roofStyle + */ const makeRoofPatternPolygon = (roofStyle) => { if (Object.keys(roofPolygonPattern).length === 0 && roofPolygonPattern.constructor === Object) { alert('객체가 비어있습니다.') return } - //내부 선 점선으로 변경 + //내부 선 점선으로 변경 추후에 다시 되돌리는 로직 필요 roofPolygonPattern.lines.forEach((line, index) => { line.line.set('strokeDashArray', [10, 5, 2, 5]) line.line.set('stroke', 'blue') line.line.set('strokeWidth', 1) }) - var ratio = window.devicePixelRatio || 1 + const ratio = window.devicePixelRatio || 1 - let inputPatternSize = { width: 30, height: 20 } //임시 사이즈 - let patternSize = { ...inputPatternSize } // 입력된 값을 뒤집기 위해 + const inputPatternSize = { width: 30, height: 20 } //임시 사이즈 + const patternSize = { ...inputPatternSize } // 입력된 값을 뒤집기 위해 if (templateType === 2) { //세로형이면 width height를 바꿈 @@ -3246,6 +3251,7 @@ export function useMode() { let polygonArray = [] + //패턴 폴리곤을 생성 후 배열에 담음 roofPolygonPattern.roofPatternPolygonArray.forEach((patternPolygon, index) => { const drawPolygon = new QPolygon(patternPolygon, commonOption) canvas.add(drawPolygon) @@ -3255,9 +3261,13 @@ export function useMode() { }) canvas?.renderAll() + //지붕 폴리곤 recoil에 담음 setRoofPolygonArray(polygonArray) } + /** + * 가대 생성 로직 + */ const makeRoofTrestle = () => { if (Object.keys(roofPolygonPattern).length === 0 && roofPolygonPattern.constructor === Object) { alert('객체가 비어있습니다.') @@ -3278,8 +3288,13 @@ export function useMode() { strokeWidth: 3, } + /** + * 지붕가대 생성 후 가대 선택 이벤트를 추가하는 로직 + * @param polygon + */ function toggleSelection(polygon) { if (polygon.strokeWidth === defualtStrokeStyle.strokeWidth) { + //기본 선택이랑 스트로크 굵기가 같으면 선택 안됨으로 봄 polygon.set({ stroke: selectedStrokeStyle.stroke, strokeWidth: selectedStrokeStyle.strokeWidth, @@ -3288,6 +3303,7 @@ export function useMode() { canvas.discardActiveObject() // 객체의 활성 상태 해제 selectedAreaArray.push(polygon) } else { + //선택후 재선택하면 선택안됨으로 변경 polygon.set({ stroke: defualtStrokeStyle.stroke, strokeWidth: defualtStrokeStyle.strokeWidth, @@ -3295,6 +3311,7 @@ export function useMode() { }) canvas.discardActiveObject() // 객체의 활성 상태 해제 + //폴리곤에 커스텀 인덱스를 가지고 해당 배열 인덱스를 찾아 삭제함 const removeIndex = polygon.customIndex const removeArrayIndex = selectedAreaArray.findIndex((x) => x.customIndex === removeIndex) selectedAreaArray.splice(removeArrayIndex, 1) @@ -3302,6 +3319,7 @@ export function useMode() { canvas?.renderAll() } + //외각선을 안쪽으로 그려 가대선을 그린다. polygons.forEach((polygon, index) => { const trestlePolygon = handleOuterlinesTest(polygon, -12) trestlePolygon.setViewLengthText(false) //얘는 set으로 안먹는다... @@ -3315,25 +3333,45 @@ export function useMode() { lockScalingX: true, lockScalingY: true, bringToFront: true, - customIndex: polygon.customIndex, + customIndex: polygon.customIndex, //가대 폴리곤의 임시 인덱스를 넣어줌 }) + /** + * 가대 선택 이벤트 + */ trestlePolygon.on('mousedown', function () { - const customIndex = polygon.get('customIndex') toggleSelection(trestlePolygon) }) }) setSelectedCellRoofArray(selectedAreaArray) } + /** + * 가대 선택 후 셀 채우기 + */ const makeRoofFillCells = () => { - // const selectedCellRoofs = selectedCellRoofArray + const drawCellsArray = [] + if (selectedCellRoofArray.length === 0) { + //배열에 선택된 가대 셀이 없으면 리턴 alert('선택된 영역이 없습니다.') setMode(Mode.DEFAULT) //default 모드로 변경 return } + if (drewRoofCells.length > 0) { + //리코일에 + if (confirm('패널이 초기화 됩니다.')) { + drewRoofCells.forEach((cells, index) => { + cells.drawCells.forEach((cell) => { + canvas?.remove(cell) + }) + }) + setDrewRoofCells([]) + canvas?.renderAll() + } + } + const inputCellSize = { width: 172, height: 113 } const cellSize = { ...inputCellSize } //기본으로 가로형으로 넣고 @@ -3342,8 +3380,11 @@ export function useMode() { } selectedCellRoofArray.forEach((polygon, index) => { - polygon.fillCell({ width: cellSize.width, height: cellSize.height, padding: 10 }) + const drawCells = polygon.fillCell({ width: cellSize.width, height: cellSize.height, padding: 10 }) + drawCellsArray.push({ roofIndex: polygon.customIndex, drawCells: drawCells }) }) + + setDrewRoofCells(drawCellsArray) setMode(Mode.DEFAULT) //default 모드로 변경 } diff --git a/src/store/canvasAtom.js b/src/store/canvasAtom.js index c51a7061..6491dc2d 100644 --- a/src/store/canvasAtom.js +++ b/src/store/canvasAtom.js @@ -53,3 +53,10 @@ export const templateTypeState = atom({ default: 1, //1:모임지붕, 2:A타입, 3:B타입 dangerouslyAllowMutability: true, }) + +//셀 그린 이후에 생성하는 state +export const drewRoofCellsState = atom({ + key: 'drewRoofCells', + default: [], + dangerouslyAllowMutability: true, +}) From bc71c7df9aa868410032d9de2e90c57da82a3f11 Mon Sep 17 00:00:00 2001 From: yoosangwook Date: Fri, 2 Aug 2024 10:09:40 +0900 Subject: [PATCH 4/7] =?UTF-8?q?feature:=20css-module=20=EC=98=88=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- postcss.config.mjs | 4 ++-- src/app/changelog/changelog.module.css | 4 ++++ src/app/changelog/page.jsx | 15 ++++++------ src/app/globals.css | 3 ++- src/components/ui/QSelect.jsx | 33 ++++++++++++++++++++++++++ src/components/ui/QSelect.module.css | 3 +++ 6 files changed, 51 insertions(+), 11 deletions(-) create mode 100644 src/app/changelog/changelog.module.css create mode 100644 src/components/ui/QSelect.jsx create mode 100644 src/components/ui/QSelect.module.css diff --git a/postcss.config.mjs b/postcss.config.mjs index 1a69fd2a..0dc456ad 100644 --- a/postcss.config.mjs +++ b/postcss.config.mjs @@ -3,6 +3,6 @@ const config = { plugins: { tailwindcss: {}, }, -}; +} -export default config; +export default config diff --git a/src/app/changelog/changelog.module.css b/src/app/changelog/changelog.module.css new file mode 100644 index 00000000..273a4a34 --- /dev/null +++ b/src/app/changelog/changelog.module.css @@ -0,0 +1,4 @@ +.test { + @apply bg-red-500; + @apply text-2xl; +} diff --git a/src/app/changelog/page.jsx b/src/app/changelog/page.jsx index 821254ec..79f8df19 100644 --- a/src/app/changelog/page.jsx +++ b/src/app/changelog/page.jsx @@ -1,19 +1,15 @@ 'use client' import Hero from '@/components/Hero' -import { - Table, - TableBody, - TableCell, - TableColumn, - TableHeader, - TableRow, -} from '@nextui-org/react' +import { Table, TableBody, TableCell, TableColumn, TableHeader, TableRow } from '@nextui-org/react' +import styles from './changelog.module.css' +import QSelect from '@/components/ui/QSelect' export default function changelogPage() { return ( <> +
이 영역은 테스트입니다.
@@ -35,6 +31,9 @@ export default function changelogPage() {
+
+ +
) } diff --git a/src/app/globals.css b/src/app/globals.css index 1c95a542..33e09403 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -30,10 +30,11 @@ body { .text-balance { text-wrap: balance; } -} */ +} .archivo-black-regular { font-family: 'Archivo Black', sans-serif; font-weight: 400; font-style: normal; } +*/ diff --git a/src/components/ui/QSelect.jsx b/src/components/ui/QSelect.jsx new file mode 100644 index 00000000..6ebf2bd6 --- /dev/null +++ b/src/components/ui/QSelect.jsx @@ -0,0 +1,33 @@ +import { Select, SelectItem } from '@nextui-org/react' +import styles from './QSelect.module.css' + +const animals = [ + { key: 'cat', label: 'Cat' }, + { key: 'dog', label: 'Dog' }, + { key: 'elephant', label: 'Elephant' }, + { key: 'lion', label: 'Lion' }, + { key: 'tiger', label: 'Tiger' }, + { key: 'giraffe', label: 'Giraffe' }, + { key: 'dolphin', label: 'Dolphin' }, + { key: 'penguin', label: 'Penguin' }, + { key: 'zebra', label: 'Zebra' }, + { key: 'shark', label: 'Shark' }, + { key: 'whale', label: 'Whale' }, + { key: 'otter', label: 'Otter' }, + { key: 'crocodile', label: 'Crocodile' }, +] + +export default function QSelect() { + return ( + <> +
+ +
+
test
+ + ) +} diff --git a/src/components/ui/QSelect.module.css b/src/components/ui/QSelect.module.css new file mode 100644 index 00000000..c0802771 --- /dev/null +++ b/src/components/ui/QSelect.module.css @@ -0,0 +1,3 @@ +.test { + @apply bg-blue-500; +} From 69e1077c2ce7b965fbadcd9660d8c1b84bf08611 Mon Sep 17 00:00:00 2001 From: yoosangwook Date: Fri, 2 Aug 2024 11:34:22 +0900 Subject: [PATCH 5/7] =?UTF-8?q?chore:=20env=20=EA=B8=B0=EC=A4=80=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.development | 1 + .env.production | 1 + next.config.mjs | 16 ++++++++-------- src/app/changelog/page.jsx | 16 ++++++++-------- 4 files changed, 18 insertions(+), 16 deletions(-) create mode 100644 .env.development create mode 100644 .env.production diff --git a/.env.development b/.env.development new file mode 100644 index 00000000..87e51c2b --- /dev/null +++ b/.env.development @@ -0,0 +1 @@ +NEXT_PUBLIC_TEST="테스트변수입니다. development" \ No newline at end of file diff --git a/.env.production b/.env.production new file mode 100644 index 00000000..e810ba94 --- /dev/null +++ b/.env.production @@ -0,0 +1 @@ +NEXT_PUBLIC_TEST="테스트변수입니다. production" \ No newline at end of file diff --git a/next.config.mjs b/next.config.mjs index 8cc4c040..15466bd2 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,15 +1,15 @@ /** @type {import('next').NextConfig} */ const nextConfig = { - reactStrictMode: false, + reactStrictMode: true, webpack: (config) => { config.externals.push({ - "utf-8-validate": "commonjs utf-8-validate", - bufferutil: "commonjs bufferutil", - canvas: "commonjs canvas", - }); + 'utf-8-validate': 'commonjs utf-8-validate', + bufferutil: 'commonjs bufferutil', + canvas: 'commonjs canvas', + }) // config.infrastructureLogging = { debug: /PackFileCache/ }; - return config; + return config }, -}; +} -export default nextConfig; +export default nextConfig diff --git a/src/app/changelog/page.jsx b/src/app/changelog/page.jsx index 821254ec..1b45590f 100644 --- a/src/app/changelog/page.jsx +++ b/src/app/changelog/page.jsx @@ -1,16 +1,12 @@ 'use client' +import { Table, TableBody, TableCell, TableColumn, TableHeader, TableRow } from '@nextui-org/react' import Hero from '@/components/Hero' -import { - Table, - TableBody, - TableCell, - TableColumn, - TableHeader, - TableRow, -} from '@nextui-org/react' +import QSelect from '@/components/ui/QSelect' +import styles from './changelog.module.css' export default function changelogPage() { + const testVar = process.env.NEXT_PUBLIC_TEST return ( <> @@ -35,6 +31,10 @@ export default function changelogPage() { +
+ +
+
{testVar}
) } From 8f051d541bbf541f57ed8b3d74fa1788f273a098 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Fri, 2 Aug 2024 12:40:07 +0900 Subject: [PATCH 6/7] =?UTF-8?q?=EC=A7=80=EB=B6=95=ED=83=80=EC=9E=85=20?= =?UTF-8?q?=EC=A7=80=EB=B6=95=EC=9E=AC,=20=EC=A7=80=EB=B6=95=20=EB=B0=B0?= =?UTF-8?q?=ED=84=B0=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Roof2.jsx | 147 +++++++++++++++++++----------- src/components/fabric/QPolygon.js | 4 +- src/store/canvasAtom.js | 7 ++ src/util/qpolygon-utils.js | 12 ++- 4 files changed, 113 insertions(+), 57 deletions(-) diff --git a/src/components/Roof2.jsx b/src/components/Roof2.jsx index 8d2fc35b..da41280b 100644 --- a/src/components/Roof2.jsx +++ b/src/components/Roof2.jsx @@ -6,7 +6,7 @@ import QRect from '@/components/fabric/QRect' import RangeSlider from './ui/RangeSlider' import { useRecoilState, useRecoilValue } from 'recoil' -import { canvasSizeState, fontSizeState, roofState, sortedPolygonArray } from '@/store/canvasAtom' +import { canvasSizeState, fontSizeState, roofMaterialState, roofState, sortedPolygonArray } from '@/store/canvasAtom' import { QLine } from '@/components/fabric/QLine' import { getCanvasState, insertCanvasState } from '@/lib/canvas' import { calculateIntersection } from '@/util/canvas-util' @@ -31,7 +31,8 @@ export default function Roof2() { const [showControl, setShowControl] = useState(false) - const roof = useRecoilValue(roofState) + //지붕재 + const roofMaterial = useRecoilValue(roofMaterialState) const { mode, @@ -138,12 +139,12 @@ export default function Roof2() { { x: 100, y: 400 }, ] const type2 = [ - { x: 100, y: 100 }, - { x: 100, y: 1000 }, - { x: 1000, y: 1000 }, - { x: 1000, y: 600 }, - { x: 550, y: 600 }, - { x: 550, y: 100 }, + { x: 200, y: 100 }, + { x: 200, y: 1000 }, + { x: 1100, y: 1000 }, + { x: 1100, y: 600 }, + { x: 650, y: 600 }, + { x: 650, y: 100 }, ] const type3 = [ @@ -263,58 +264,17 @@ export default function Roof2() { { x: 675, y: 275 }, { x: 450, y: 850 }, ] - const polygon = new QPolygon(type4, { + const polygon = new QPolygon(type2, { fill: 'transparent', stroke: 'black', strokeWidth: 1, selectable: false, fontSize: fontSize, - name: 'QPolygon1', + name: 'wall', }) canvas?.add(polygon) - - polygon.set('strokeDashArray', [10, 5, 2, 5]) - polygon.set('stroke', 'blue') - polygon.set('strokeWidth', 1) - - // const newPolygon = fabric.util.clone(polygon) - handleOuterlinesTest2(polygon) - - const roofs = canvas?.getObjects().filter((obj) => obj.name === 'roof') - - roofs.forEach((roof) => { - let maxLengthLine = roof.lines.reduce((acc, cur) => { - return acc.length > cur.length ? acc : cur - }) - - // 패턴 소스를 위한 임시 캔버스 생성 - const patternSourceCanvas = document.createElement('canvas') - if (maxLengthLine.direction === 'right' || maxLengthLine.direction === 'left') { - patternSourceCanvas.width = 20 * window.devicePixelRatio || 1 - patternSourceCanvas.height = 10 * window.devicePixelRatio || 1 - } else { - patternSourceCanvas.width = 10 * window.devicePixelRatio || 1 - patternSourceCanvas.height = 20 * window.devicePixelRatio || 1 - } - - const ctx = patternSourceCanvas.getContext('2d') - - // 벽돌 패턴 그리기 - ctx.scale(window.devicePixelRatio || 1, window.devicePixelRatio || 1) - ctx.strokeStyle = 'green' - ctx.lineWidth = 0.4 - ctx.strokeRect(0, 0, 100, 100) - // 패턴 생성 - const pattern = new fabric.Pattern({ - source: patternSourceCanvas, - repeat: 'repeat', - }) - - roof.set('fill', pattern) - }) - // const lines = togglePolygonLine(polygon) // togglePolygonLine(lines[0]) } @@ -404,6 +364,70 @@ export default function Roof2() { handleClear() } + const drawRoofMaterial = () => { + const { width, height, roofStyle } = roofMaterial + + const wallPolygon = canvas?.getObjects().find((obj) => obj.name === 'wall') + + wallPolygon.set('strokeDashArray', [10, 5, 2, 5]) + wallPolygon.set('stroke', 'blue') + wallPolygon.set('strokeWidth', 1) + + const roofs = canvas?.getObjects().filter((obj) => obj.name === 'roof') + + roofs.forEach((roof) => { + let maxLengthLine = roof.lines.reduce((acc, cur) => { + return acc.length > cur.length ? acc : cur + }) + + const roofRatio = window.devicePixelRatio || 1 + + // 패턴 소스를 위한 임시 캔버스 생성 + const patternSourceCanvas = document.createElement('canvas') + if (roofStyle === 1) { + if (maxLengthLine.direction === 'right' || maxLengthLine.direction === 'left') { + patternSourceCanvas.width = width * roofRatio + patternSourceCanvas.height = height * roofRatio + } else { + patternSourceCanvas.width = height * roofRatio + patternSourceCanvas.height = width * roofRatio + } + } else if (roofStyle === 2) { + if (maxLengthLine.direction === 'right' || maxLengthLine.direction === 'left') { + patternSourceCanvas.width = width * 2 + patternSourceCanvas.height = height * 2 + } else { + patternSourceCanvas.width = height * 2 + patternSourceCanvas.height = width * 2 + } + } + + const ctx = patternSourceCanvas.getContext('2d') + + ctx.scale(roofRatio, roofRatio) + ctx.strokeStyle = 'green' + ctx.lineWidth = 0.4 + // 벽돌 패턴 그리기 + if (roofStyle === 1) { + ctx.strokeRect(0, 0, 50, 30) + } else if (roofStyle === 2) { + // 지그재그 + ctx.strokeRect(0, 0, 200, 100) + ctx.strokeRect(100, 100, 200, 100) + } + + // 패턴 생성 + const pattern = new fabric.Pattern({ + source: patternSourceCanvas, + repeat: 'repeat', + }) + roof.set('fill', null) + + roof.set('fill', pattern) + canvas?.renderAll() + }) + } + /** * canvas 내용 불러오기 */ @@ -424,6 +448,21 @@ export default function Roof2() { makeRoofPatternPolygon(roofStyle) } + const createRoofRack = () => { + const roofs = canvas?.getObjects().filter((obj) => obj.name === 'roof') + roofs.forEach((roof) => { + let maxLengthLine = roof.lines.reduce((acc, cur) => { + return acc.length > cur.length ? acc : cur + }) + + if (maxLengthLine.direction === 'right' || maxLengthLine.direction === 'left') { + roof.fillCell({ width: 50, height: 100, padding: 0 }) + } else { + roof.fillCell({ width: 100, height: 50, padding: 0 }) + } + }) + } + return ( <> {canvas && ( @@ -530,6 +569,12 @@ export default function Roof2() { + + diff --git a/src/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js index f67c0e68..685d83ee 100644 --- a/src/components/fabric/QPolygon.js +++ b/src/components/fabric/QPolygon.js @@ -234,7 +234,6 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { { x: rectLeft, y: rectTop + rectHeight }, { x: rectLeft + rectWidth, y: rectTop + rectHeight }, ] - const allPointsInside = rectPoints.every((point) => this.inPolygon(point)) if (allPointsInside) { @@ -244,6 +243,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { width: rectWidth, height: rectHeight, fill: '#BFFD9F', + stroke: 'black', selectable: true, // 선택 가능하게 설정 lockMovementX: true, // X 축 이동 잠금 lockMovementY: true, // Y 축 이동 잠금 @@ -261,7 +261,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { return drawCellsArray }, inPolygon(point) { - const vertices = this.getCurrentPoints() + const vertices = this.points let intersects = 0 for (let i = 0; i < vertices.length; i++) { diff --git a/src/store/canvasAtom.js b/src/store/canvasAtom.js index 6491dc2d..48eb0281 100644 --- a/src/store/canvasAtom.js +++ b/src/store/canvasAtom.js @@ -60,3 +60,10 @@ export const drewRoofCellsState = atom({ default: [], dangerouslyAllowMutability: true, }) + +// 지붕재 width, height, rafter(서까래), roofStyle을 갖고있고 roofStyle 1은 정방향, 2는 지그재그 +export const roofMaterialState = atom({ + key: 'roofMaterial', + default: { width: 20, height: 10, rafter: 0, roofStyle: 2 }, + dangerouslyAllowMutability: true, +}) diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index 078303d0..36eadd4d 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -499,6 +499,10 @@ export const dividePolygon = (polygon) => { const startHip = hips.find((hip) => hip.startPoint.x === startPoint.x && hip.startPoint.y === startPoint.y) const endHip = hips.find((hip) => hip.startPoint.x === endPoint.x && hip.startPoint.y === endPoint.y) + if (!startHip || !endHip) { + return + } + if (startHip && endHip && startHip.endPoint.x === endHip.endPoint.x && startHip.endPoint.y === endHip.endPoint.y) { polygonPoints.push(startHip.endPoint) @@ -506,7 +510,7 @@ export const dividePolygon = (polygon) => { fontSize: polygon.fontSize, id: polygon.id, name: 'roof', - selectable: true, + selectable: false, stroke: 'black', fill: 'transparent', strokeWidth: 3, @@ -535,7 +539,7 @@ export const dividePolygon = (polygon) => { fontSize: polygon.fontSize, id: polygon.id, name: 'roof', - selectable: true, + selectable: false, stroke: 'black', fill: 'transparent', strokeWidth: 3, @@ -553,7 +557,7 @@ export const dividePolygon = (polygon) => { fontSize: polygon.fontSize, id: polygon.id, name: 'roof', - selectable: true, + selectable: false, stroke: 'black', fill: 'transparent', strokeWidth: 3, @@ -628,7 +632,7 @@ export const dividePolygon = (polygon) => { fontSize: polygon.fontSize, id: polygon.id, name: 'roof', - selectable: true, + selectable: false, stroke: 'black', fill: 'transparent', strokeWidth: 3, From b1e5821ff786e5e57a54c39f4204e10a07be0f7e Mon Sep 17 00:00:00 2001 From: yoosangwook Date: Fri, 2 Aug 2024 15:11:15 +0900 Subject: [PATCH 7/7] =?UTF-8?q?feat:=20axios=20=EC=84=B8=ED=8C=85=20?= =?UTF-8?q?=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 래핑도 완료 --- .env.development | 4 ++- .env.production | 4 ++- package.json | 1 + src/app/changelog/page.jsx | 17 ++++++++++-- src/lib/Axios.js | 56 ++++++++++++++++++++++++++++++++++++++ yarn.lock | 19 +++++++++++++ 6 files changed, 96 insertions(+), 5 deletions(-) create mode 100644 src/lib/Axios.js diff --git a/.env.development b/.env.development index 87e51c2b..161467af 100644 --- a/.env.development +++ b/.env.development @@ -1 +1,3 @@ -NEXT_PUBLIC_TEST="테스트변수입니다. development" \ No newline at end of file +NEXT_PUBLIC_TEST="테스트변수입니다. development" + +NEXT_PUBLIC_API_SERVER_PATH="http://localhost:8080" \ No newline at end of file diff --git a/.env.production b/.env.production index e810ba94..efb12105 100644 --- a/.env.production +++ b/.env.production @@ -1 +1,3 @@ -NEXT_PUBLIC_TEST="테스트변수입니다. production" \ No newline at end of file +NEXT_PUBLIC_TEST="테스트변수입니다. production" + +NEXT_PUBLIC_API_SERVER_PATH="http://localhost:8080" \ No newline at end of file diff --git a/package.json b/package.json index a5c429a7..d4cdd9bb 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "dependencies": { "@nextui-org/react": "^2.4.2", "@prisma/client": "^5.17.0", + "axios": "^1.7.3", "fabric": "^5.3.0", "framer-motion": "^11.2.13", "mathjs": "^13.0.2", diff --git a/src/app/changelog/page.jsx b/src/app/changelog/page.jsx index bf392304..1dcc8d0e 100644 --- a/src/app/changelog/page.jsx +++ b/src/app/changelog/page.jsx @@ -1,12 +1,18 @@ 'use client' -import { Table, TableBody, TableCell, TableColumn, TableHeader, TableRow } from '@nextui-org/react' +import { Button, Table, TableBody, TableCell, TableColumn, TableHeader, TableRow } from '@nextui-org/react' import Hero from '@/components/Hero' import QSelect from '@/components/ui/QSelect' import styles from './changelog.module.css' +import { get } from '@/lib/Axios' export default function changelogPage() { const testVar = process.env.NEXT_PUBLIC_TEST + + const handleUsers = async () => { + const users = await get('/api/user/find-all') + console.log(users) + } return ( <> @@ -32,10 +38,15 @@ export default function changelogPage() { -
+
-
{testVar}
+
{testVar}
+
+
+ +
+
) } diff --git a/src/lib/Axios.js b/src/lib/Axios.js new file mode 100644 index 00000000..cae33f3e --- /dev/null +++ b/src/lib/Axios.js @@ -0,0 +1,56 @@ +'use client' + +import axios from 'axios' + +axios.defaults.baseURL = process.env.NEXT_PUBLIC_API_SERVER_PATH + +const axiosInstance = axios.create({ + // baseURL: process.env.API_SERVER_URL, + headers: { + Accept: 'application/json', + }, +}) + +axiosInstance.interceptors.request.use((config) => { + // config['Authorization'] = localStorage.getItem('token') + //TODO: 인터셉터에서 추가 로직 구현 + return config +}) + +axiosInstance.interceptors.request.use(undefined, (error) => { + //TODO: 인터셉터에서 에러 처리 로직 구현 + // if (error.isAxiosError && e.response?.status === 401) { + // localStorage.removeItem('token') + // } +}) + +export const get = (url) => + axiosInstance + .get(url) + .then((res) => res.data) + .catch(console.error) + +export const post = (url, data) => + axiosInstance + .post(url, data) + .then((res) => res.data) + .catch(console.error) + +export const put = (url, data) => + axiosInstance + .put(url, data) + .then((res) => res.data) + .catch(console.error) + +export const patch = (url, data) => + axiosInstance + .patch(url, data) + + .then((res) => res.data) + .catch(console.error) + +export const del = (url) => + axiosInstance + .delete(url) + .then((res) => res.data) + .catch(console.error) diff --git a/yarn.lock b/yarn.lock index f34efa44..5eec9ca2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2181,6 +2181,15 @@ asynckit@^0.4.0: resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== +axios@^1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.3.tgz#a1125f2faf702bc8e8f2104ec3a76fab40257d85" + integrity sha512-Ar7ND9pU99eJ9GpoGQKhKf58GpUOgnzuaB7ueNQ5BMi0p+LZ5oaEnfF999fAArcTIBwXTCHAmGcHOZJaWPq9Nw== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" @@ -2539,6 +2548,11 @@ flat@^5.0.2: resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== +follow-redirects@^1.15.6: + version "1.15.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== + foreground-child@^3.1.0: version "3.2.1" resolved "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz" @@ -3233,6 +3247,11 @@ prisma@^5.17.0: dependencies: "@prisma/engines" "5.17.0" +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + psl@^1.1.33: version "1.9.0" resolved "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz"