diff --git a/.env.development b/.env.development
new file mode 100644
index 00000000..161467af
--- /dev/null
+++ b/.env.development
@@ -0,0 +1,3 @@
+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
new file mode 100644
index 00000000..efb12105
--- /dev/null
+++ b/.env.production
@@ -0,0 +1,3 @@
+NEXT_PUBLIC_TEST="테스트변수입니다. production"
+
+NEXT_PUBLIC_API_SERVER_PATH="http://localhost:8080"
\ 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/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/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..1dcc8d0e 100644
--- a/src/app/changelog/page.jsx
+++ b/src/app/changelog/page.jsx
@@ -1,19 +1,22 @@
'use client'
+import { Button, 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'
+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 (
<>
+
이 영역은 테스트입니다.
@@ -35,6 +38,15 @@ export default function changelogPage() {
+
+
+
+ {testVar}
+
>
)
}
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/Roof2.jsx b/src/components/Roof2.jsx
index e3009432..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 = [
@@ -257,20 +258,23 @@ 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(type2, {
fill: 'transparent',
stroke: 'black',
strokeWidth: 1,
- selectable: true,
+ selectable: false,
fontSize: fontSize,
- name: 'QPolygon1',
+ name: 'wall',
})
canvas?.add(polygon)
-
handleOuterlinesTest2(polygon)
-
// const lines = togglePolygonLine(polygon)
// togglePolygonLine(lines[0])
}
@@ -360,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 내용 불러오기
*/
@@ -380,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 && (
@@ -486,6 +569,12 @@ export default function Roof2() {
+
+
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/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js
index 1817862a..685d83ee 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)
})
},
@@ -201,6 +221,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
@@ -212,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) {
@@ -222,23 +243,25 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
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.6,
+ opacity: 0.8,
})
-
+ drawCellsArray.push(rect) //배열에 넣어서 반환한다
this.canvas.add(rect)
}
}
}
this.canvas?.renderAll()
+ return drawCellsArray
},
inPolygon(point) {
- const vertices = this.getCurrentPoints()
+ const vertices = this.points
let intersects = 0
for (let i = 0; i < vertices.length; i++) {
@@ -326,5 +349,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
text.set({ visible: isView })
})
},
- divideLine() {},
+ divideLine() {
+ dividePolygon(this)
+ },
})
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;
+}
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..c60fba2b 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,
@@ -18,17 +19,16 @@ 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',
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(() => {
// 이벤트 리스너 추가
@@ -641,6 +642,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 +704,6 @@ export function useMode() {
stroke: 'black',
fill: 'transparent',
viewLengthText: true,
- selectable: true,
fontSize: fontSize,
},
canvas,
@@ -1174,6 +1177,7 @@ export function useMode() {
setRoof(roof)
roof.drawHelpLine()
+ roof.divideLine()
}
const togglePolygonLine = (obj) => {
@@ -3175,23 +3179,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를 바꿈
@@ -3237,6 +3245,7 @@ export function useMode() {
fill: pattern,
selectable: false,
fontSize: 15, // fontSize는 필요에 따라 조정
+ sort: false,
lockMovementX: true,
lockMovementY: true,
lockRotation: true,
@@ -3246,6 +3255,7 @@ export function useMode() {
let polygonArray = []
+ //패턴 폴리곤을 생성 후 배열에 담음
roofPolygonPattern.roofPatternPolygonArray.forEach((patternPolygon, index) => {
const drawPolygon = new QPolygon(patternPolygon, commonOption)
canvas.add(drawPolygon)
@@ -3255,9 +3265,13 @@ export function useMode() {
})
canvas?.renderAll()
+ //지붕 폴리곤 recoil에 담음
setRoofPolygonArray(polygonArray)
}
+ /**
+ * 가대 생성 로직
+ */
const makeRoofTrestle = () => {
if (Object.keys(roofPolygonPattern).length === 0 && roofPolygonPattern.constructor === Object) {
alert('객체가 비어있습니다.')
@@ -3278,8 +3292,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 +3307,7 @@ export function useMode() {
canvas.discardActiveObject() // 객체의 활성 상태 해제
selectedAreaArray.push(polygon)
} else {
+ //선택후 재선택하면 선택안됨으로 변경
polygon.set({
stroke: defualtStrokeStyle.stroke,
strokeWidth: defualtStrokeStyle.strokeWidth,
@@ -3295,6 +3315,7 @@ export function useMode() {
})
canvas.discardActiveObject() // 객체의 활성 상태 해제
+ //폴리곤에 커스텀 인덱스를 가지고 해당 배열 인덱스를 찾아 삭제함
const removeIndex = polygon.customIndex
const removeArrayIndex = selectedAreaArray.findIndex((x) => x.customIndex === removeIndex)
selectedAreaArray.splice(removeArrayIndex, 1)
@@ -3302,6 +3323,7 @@ export function useMode() {
canvas?.renderAll()
}
+ //외각선을 안쪽으로 그려 가대선을 그린다.
polygons.forEach((polygon, index) => {
const trestlePolygon = handleOuterlinesTest(polygon, -12)
trestlePolygon.setViewLengthText(false) //얘는 set으로 안먹는다...
@@ -3315,25 +3337,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 +3384,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/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/src/store/canvasAtom.js b/src/store/canvasAtom.js
index c51a7061..48eb0281 100644
--- a/src/store/canvasAtom.js
+++ b/src/store/canvasAtom.js
@@ -53,3 +53,17 @@ export const templateTypeState = atom({
default: 1, //1:모임지붕, 2:A타입, 3:B타입
dangerouslyAllowMutability: true,
})
+
+//셀 그린 이후에 생성하는 state
+export const drewRoofCellsState = atom({
+ key: 'drewRoofCells',
+ 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/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..36eadd4d 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,214 @@ 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) {
+ return
+ }
+
+ 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: false,
+ 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: false,
+ 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: false,
+ 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: false,
+ 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
+}
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"