diff --git a/.env b/.env
new file mode 100644
index 00000000..50ab0d74
--- /dev/null
+++ b/.env
@@ -0,0 +1,12 @@
+# Environment variables declared in this file are automatically made available to Prisma.
+# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema
+
+# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
+# See the documentation for all the connection string options: https://pris.ly/d/connection-strings
+
+# DATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public"
+# DATABASE_URL="mongodb://yoo32767:GuCtswjLGqUaNL0G@cluster0.vsdtcnb.mongodb.net/sample_mflix?retryWrites=true&w=majority"
+#DATABASE_URL = "mongodb%2Bsrv%3A%2F%2Fyoo32767%3AGuCtswjLGqUaNL0G%40cluster0.vsdtcnb.mongodb.net%2F%3FretryWrites%3Dtrue%26w%3Dmajority%26appName%3DCluster0"
+# DATABASE_URL = "mongodb+srv://yoo32767:GuCtswjLGqUaNL0G@cluster0.vsdtcnb.mongodb.net/Cluster0?retryWrites=true&w=majority"
+# DATABASE_URL="mongodb://yoo32767:GuCtswjLGqUaNL0G@cluster0.vsdtcnb.mongodb.net/sample_mflix?retryWrites=true&w=majority"
+DATABASE_URL="mongodb+srv://yoo32767:GuCtswjLGqUaNL0G@cluster0.vsdtcnb.mongodb.net/mytest"
\ No newline at end of file
diff --git a/package.json b/package.json
index f7fa513c..9c745fdf 100644
--- a/package.json
+++ b/package.json
@@ -10,9 +10,11 @@
},
"dependencies": {
"@nextui-org/react": "^2.4.2",
+ "@prisma/client": "^5.17.0",
"fabric": "^5.3.0",
"framer-motion": "^11.2.13",
"mathjs": "^13.0.2",
+ "mongodb": "^6.8.0",
"next": "14.2.3",
"react": "^18",
"react-dom": "^18",
@@ -21,6 +23,7 @@
},
"devDependencies": {
"postcss": "^8",
+ "prisma": "^5.17.0",
"tailwindcss": "^3.4.1"
}
}
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
new file mode 100644
index 00000000..327b757a
--- /dev/null
+++ b/prisma/schema.prisma
@@ -0,0 +1,19 @@
+generator client {
+ provider = "prisma-client-js"
+}
+
+datasource db {
+ provider = "mongodb"
+ url = env("DATABASE_URL")
+}
+
+model test {
+ id String @id @default(auto()) @map("_id") @db.ObjectId
+ content String
+}
+
+model canvas {
+ id String @id @default(auto()) @map("_id") @db.ObjectId
+ loginId String
+ canvas Json
+}
\ No newline at end of file
diff --git a/src/components/Roof2.jsx b/src/components/Roof2.jsx
index 787ea08d..96d3a57e 100644
--- a/src/components/Roof2.jsx
+++ b/src/components/Roof2.jsx
@@ -6,12 +6,14 @@ import QRect from '@/components/fabric/QRect'
import QPolygon from '@/components/fabric/QPolygon'
import RangeSlider from './ui/RangeSlider'
-import { useRecoilState } from 'recoil'
-import { canvasSizeState, fontSizeState, sortedPolygonArray } from '@/store/canvasAtom'
+import { useRecoilState, useRecoilValue } from 'recoil'
+import { canvasAtom, canvasListState, canvasSizeState, fontSizeState, sortedPolygonArray } from '@/store/canvasAtom'
import { QLine } from '@/components/fabric/QLine'
+import { getTests, getCanvasState, insertCanvasState } from '@/lib/canvas'
+import { calculateIntersection2 } from '@/util/canvas-util'
export default function Roof2() {
- const { canvas, handleRedo, handleUndo, setCanvasBackgroundWithDots, saveImage } = useCanvas('canvas')
+ const { canvas, handleRedo, handleUndo, setCanvasBackgroundWithDots, saveImage, addCanvas } = useCanvas('canvas')
//canvas 기본 사이즈
const [canvasSize, setCanvasSize] = useRecoilState(canvasSizeState)
@@ -38,11 +40,14 @@ export default function Roof2() {
zoomOut,
zoom,
togglePolygonLine,
+ handleOuterlinesTest,
handleOuterlinesTest2,
applyTemplateB,
makeRoofPatternPolygon,
} = useMode()
+ // const [canvasState, setCanvasState] = useRecoilState(canvasAtom)
+
useEffect(() => {
if (!canvas) {
return
@@ -190,6 +195,18 @@ export default function Roof2() {
{ x: 740, y: 280 },
{ x: 740, y: 130 },
]
+
+ const eightPoint2 = [
+ { x: 197, y: 215 },
+ { x: 197, y: 815 },
+ { x: 397, y: 815 },
+ { x: 397, y: 1115 },
+ { x: 697, y: 1115 },
+ { x: 697, y: 815 },
+ { x: 897, y: 815 },
+ { x: 897, y: 215 },
+ ]
+
if (canvas) {
const polygon = new QPolygon(eightPoint, {
fill: 'transparent',
@@ -202,6 +219,8 @@ export default function Roof2() {
canvas?.add(polygon)
+ console.log(polygon)
+
handleOuterlinesTest2(polygon)
// const lines = togglePolygonLine(polygon)
@@ -223,16 +242,44 @@ export default function Roof2() {
const makeQLine = () => {
if (canvas) {
const line = new QLine(
- [50, 50, 200, 50],
+ [50, 250, 900, 250],
{
stroke: 'black',
- strokeWidth: 1,
+ strokeWidth: 5,
fontSize: fontSize,
+ selectable: true,
+ },
+ 50,
+ )
+
+ const line2 = new QLine(
+ [450, 450, 821, 78],
+ {
+ stroke: 'black',
+ strokeWidth: 5,
+ fontSize: fontSize,
+ selectable: true,
},
50,
)
canvas?.add(line)
+ canvas?.add(line2)
+
+ const interSectionPoint = calculateIntersection2(line, line2)
+
+ if (interSectionPoint) {
+ console.log(interSectionPoint)
+
+ const circle = new fabric.Circle({
+ radius: 5,
+ fill: 'red',
+ left: interSectionPoint.x - 5,
+ top: interSectionPoint.y - 5,
+ })
+
+ canvas?.add(circle)
+ }
}
}
@@ -258,6 +305,36 @@ export default function Roof2() {
const lines = togglePolygonLine(polygon)
}
+ /**
+ * canvas 내용 저장하기
+ */
+ const handleSaveCanvas = async () => {
+ // const jsonStr = JSON.stringify(canvas?.toDatalessJSON(['type', 'fontSize']))
+ const jsonObj = JSON.stringify(canvas?.toDatalessJSON(['type', 'fontSize', 'lines']))
+ console.log(jsonObj)
+
+ const param = {
+ loginId: 'test',
+ canvas: jsonObj,
+ }
+ console.log(param)
+
+ await insertCanvasState(param)
+ handleClear()
+ }
+
+ /**
+ * canvas 내용 불러오기
+ */
+ const handleLoadCanvas = async () => {
+ const canvasStates = await getCanvasState()
+ console.log(JSON.parse(canvasStates.canvas))
+ canvas?.loadFromJSON(JSON.parse(canvasStates.canvas))
+ }
+
+ /**
+ * 컨트롤러 보이기/숨기기
+ */
const handleShowController = () => {
setShowControl(!showControl)
}
@@ -373,6 +450,15 @@ export default function Roof2() {
+ {/*
+ */}
+
diff --git a/src/components/fabric/QLine.js b/src/components/fabric/QLine.js
index 7796e66e..98948ac4 100644
--- a/src/components/fabric/QLine.js
+++ b/src/components/fabric/QLine.js
@@ -1,4 +1,5 @@
import { fabric } from 'fabric'
+import { getDirection, getDirectionByPoint } from '@/util/canvas-util'
export class QLine extends fabric.Group {
line
@@ -16,7 +17,11 @@ export class QLine extends fabric.Group {
isAlreadyInterSection = false
interSectionPoints = []
- #lengthTxt = 0
+ lengthTxt = 0
+
+ initPoints
+ initOption
+ initLengthTxt
constructor(points, option = { isActiveLengthText: true }, lengthTxt) {
// 소수점 전부 제거
@@ -33,38 +38,43 @@ export class QLine extends fabric.Group {
const line = new fabric.Line(points, { ...option, strokeWidth: 1 })
super([line], {})
+ this.initPoints = points
+ this.initOption = option
+ this.initLengthTxt = lengthTxt
+
this.x1 = x1
this.y1 = y1
this.x2 = x2
this.y2 = y2
this.line = line
this.fontSize = option.fontSize
- this.direction = option.direction
+ this.direction = option.direction ?? getDirectionByPoint({ x: x1, y: y1 }, { x: x2, y: y2 })
this.parent = option.parent
this.idx = option.idx
if (lengthTxt > 0) {
- this.#lengthTxt = Number(lengthTxt)
+ this.lengthTxt = Number(lengthTxt)
}
- option.isActiveLengthText ?? this.#init()
- this.#addControl()
+ option.isActiveLengthText ?? this.init()
+ this.addControl()
}
- #init() {
- this.#addLengthText(true)
+ init() {
+ this.addLengthText(true)
}
- #addControl() {
+ addControl() {
this.on('moving', () => {
- this.#addLengthText(false)
+ this.addLengthText(false)
})
this.on('modified', (e) => {
- this.#addLengthText(false)
+ this.addLengthText(false)
})
this.on('selected', () => {
+ console.log(this)
Object.keys(this.controls).forEach((controlKey) => {
if (controlKey !== 'ml' && controlKey !== 'mr') {
this.setControlVisible(controlKey, false)
@@ -73,19 +83,19 @@ export class QLine extends fabric.Group {
})
}
- #addLengthText(isFirst) {
+ addLengthText(isFirst) {
if (this.text) {
this.removeWithUpdate(this.text)
this.text = null
}
- if (isFirst && this.#lengthTxt > 0) {
- const text = new fabric.Textbox(this.#lengthTxt.toFixed(0).toString(), {
+ if (isFirst && this.lengthTxt > 0) {
+ const text = new fabric.Textbox(this.lengthTxt.toFixed(0).toString(), {
left: (this.x1 + this.x2) / 2,
top: (this.y1 + this.y2) / 2,
fontSize: this.fontSize,
})
- this.length = this.#lengthTxt
+ this.length = this.lengthTxt
this.text = text
this.addWithUpdate(text)
return
@@ -115,4 +125,41 @@ export class QLine extends fabric.Group {
this.text.set({ fontSize })
this.addWithUpdate()
}
+
+ fromObject(object, callback) {
+ console.log('fromObject', object, callback)
+ }
+
+ async = true
+
+ toObject(propertiesToInclude) {
+ return fabric.util.object.extend(this.callSuper('toObject'), {
+ length: this.length,
+ line: this.line,
+ text: this.text,
+ fontSize: this.fontSize,
+ x1: this.x1,
+ y1: this.y1,
+ x2: this.x2,
+ y2: this.y2,
+ direction: this.direction,
+ idx: this.idx,
+ type: this.type,
+ parent: this.parent,
+ isAlreadyInterSection: this.isAlreadyInterSection,
+ interSectionPoints: this.interSectionPoints,
+ lengthTxt: this.lengthTxt,
+ setFontSize: this.setFontSize,
+ addLengthText: this.addLengthText,
+ init: this.init,
+ addControl: this.addControl,
+ initPoints: this.initPoints,
+ initOption: this.initOption,
+ initLengthTxt: this.initLengthTxt,
+ })
+ }
+
+ _set(key, value) {
+ this.callSuper('_set', key, value)
+ }
}
diff --git a/src/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js
index 5d7c796e..f5f1ee96 100644
--- a/src/components/fabric/QPolygon.js
+++ b/src/components/fabric/QPolygon.js
@@ -12,6 +12,7 @@ import {
sortedPoints,
} from '@/util/canvas-util'
import { QLine } from '@/components/fabric/QLine'
+import { drawHelpLineInHexagon2 } from '@/util/qpolygon-utils'
export default class QPolygon extends fabric.Group {
type = 'QPolygon'
@@ -28,8 +29,9 @@ export default class QPolygon extends fabric.Group {
helpLines = []
wall
- ridges = []
- hips = []
+
+ initPoints
+ initOption
constructor(points, options, canvas) {
/*if (points.length !== 4 && points.length !== 6) {
@@ -48,19 +50,23 @@ export default class QPolygon extends fabric.Group {
const sortPoints = sortedPoints(points)
const polygon = new fabric.Polygon(sortPoints, options)
- super([polygon], {})
+ super([polygon], { selectable: false })
this.fontSize = options.fontSize
this.points = sortPoints
this.polygon = polygon
this.name = options.name
- this.#init()
- this.#addEvent()
- this.#initLines()
+
+ this.initPoints = points
+ this.initOption = options
+
+ this.init()
+ this.addEvent()
+ this.initLines()
this.setShape()
}
- #initLines() {
+ initLines() {
this.points.forEach((point, i) => {
const nextPoint = this.points[(i + 1) % this.points.length]
const line = new QLine([point.x, point.y, nextPoint.x, nextPoint.y], {
@@ -74,13 +80,13 @@ export default class QPolygon extends fabric.Group {
})
}
- #init() {
- this.#addLengthText()
+ init() {
+ this.addLengthText()
}
- #addEvent() {
+ addEvent() {
this.on('scaling', (e) => {
- this.#updateLengthText()
+ this.updateLengthText()
})
this.on('selected', function() {
@@ -110,10 +116,10 @@ export default class QPolygon extends fabric.Group {
}
})
- this.addWithUpdate()
+ this.canvas.add()
}
- #addLengthText() {
+ addLengthText() {
if (this.texts.length > 0) {
this.texts.forEach((text) => {
this.canvas.remove(text)
@@ -139,13 +145,13 @@ export default class QPolygon extends fabric.Group {
})
this.texts.push(text)
- this.addWithUpdate(text)
+ this.canvas.add(text)
})
this.canvas.renderAll()
}
- #updateLengthText() {
+ updateLengthText() {
const points = this.getCurrentPoints()
points.forEach((start, i) => {
@@ -191,10 +197,10 @@ export default class QPolygon extends fabric.Group {
new fabric.Point(rect.left + rect.width, rect.top + rect.height),
]
- const isInside = rectPoints.every((rectPoint) => this.inPolygon(rectPoint) && this.#distanceFromEdge(rectPoint) >= cell.padding)
+ const isInside = rectPoints.every((rectPoint) => this.inPolygon(rectPoint) && this.distanceFromEdge(rectPoint) >= cell.padding)
if (isInside) {
- this.addWithUpdate(rect)
+ this.canvas.add(rect)
}
}
}
@@ -248,7 +254,7 @@ export default class QPolygon extends fabric.Group {
return intersects % 2 === 1
}
- #distanceFromEdge(point) {
+ distanceFromEdge(point) {
const vertices = this.getCurrentPoints()
let minDistance = Infinity
@@ -375,7 +381,7 @@ export default class QPolygon extends fabric.Group {
return this.shape
}
- #drawHelpLineInRect(chon) {
+ drawHelpLineInRect(chon) {
let type = 1
let smallestLength = Infinity
let maxLength = 0
@@ -520,19 +526,18 @@ export default class QPolygon extends fabric.Group {
strokeWidth: 1,
})
- this.addWithUpdate(realLine1)
- this.addWithUpdate(realLine2)
- this.addWithUpdate(realLine3)
- this.addWithUpdate(realLine4)
- this.addWithUpdate(realLine5)
- this.addWithUpdate(realLine6)
+ this.canvas.add(realLine1)
+ this.canvas.add(realLine2)
+ this.canvas.add(realLine3)
+ this.canvas.add(realLine4)
+ this.canvas.add(realLine5)
+ this.canvas.add(realLine6)
if (smallestLength !== maxLength) {
// 정사각형이 아닌경우에만 용마루를 추가한다.
this.canvas.add(ridge)
}
}
-
- #drawHelpLineInHexagon2(chon) {
+ drawHelpLineInHexagon2(chon) {
const oneSideLines = [...this.lines].map((line) => {
let newX1, newY1, newX2, newY2
if (line.direction === 'top') {
@@ -748,8 +753,7 @@ export default class QPolygon extends fabric.Group {
this.canvas.renderAll()
})
}
-
- #drawHelpLineInHexagon(chon) {
+ drawHelpLineInHexagon(chon) {
const historyLines = []
const helpPoints = []
const notInterSectionLines = []
@@ -762,133 +766,146 @@ export default class QPolygon extends fabric.Group {
const y1 = point.y
const x2 = wallPoint.x
const y2 = wallPoint.y
+ const historyLines = []
+ const helpPoints = []
+ const notInterSectionLines = []
+ const ridge = []
+ const maxLength = this.lines.reduce((max, obj) => Math.max(max, obj.length), 0)
+ this.points.forEach((point, index) => {
+ const wallPoint = this.wall.points[index]
+ // 두 점의 좌표
+ const x1 = point.x
+ const y1 = point.y
+ const x2 = wallPoint.x
+ const y2 = wallPoint.y
- let newX2, newY2
+ let newX2, newY2
- // x1, y1을 기준으로 x2, y2와의 거리를 유지한 새로운 직선 생성
- const angle = Math.atan2(y2 - y1, x2 - x1)
+ // x1, y1을 기준으로 x2, y2와의 거리를 유지한 새로운 직선 생성
+ const angle = Math.atan2(y2 - y1, x2 - x1)
- newX2 = x1 + (maxLength / 2) * Math.cos(angle)
- newY2 = y1 + (maxLength / 2) * Math.sin(angle)
+ newX2 = x1 + (maxLength / 2) * Math.cos(angle)
+ newY2 = y1 + (maxLength / 2) * Math.sin(angle)
- const line = new QLine([x1, y1, newX2, newY2], {
- fontSize: this.fontSize,
- stroke: 'blue',
- idx: index,
+ const line = new QLine([x1, y1, newX2, newY2], {
+ fontSize: this.fontSize,
+ stroke: 'blue',
+ idx: index,
+ })
+ historyLines.push(line)
+ this.canvas.add(line)
+
+ this.canvas.renderAll()
})
- historyLines.push(line)
- this.addWithUpdate(line)
+
+ /**
+ * 삼각 지붕
+ */
+ historyLines.forEach((line, index) => {
+ const prevLine = historyLines[(index - 1 + historyLines.length) % historyLines.length]
+
+ let intersectionPoint = calculateIntersection(line, prevLine)
+
+ if (!intersectionPoint) {
+ notInterSectionLines.push(line)
+ return
+ }
+
+ const helpLine1 = new QLine([prevLine.x1, prevLine.y1, intersectionPoint.x, intersectionPoint.y], {
+ fontSize: this.fontSize,
+ stroke: 'red',
+ })
+
+ const helpLine2 = new QLine([line.x1, line.y1, intersectionPoint.x, intersectionPoint.y], {
+ fontSize: this.fontSize,
+ stroke: 'red',
+ })
+ notInterSectionLines.pop()
+ helpPoints.push(intersectionPoint)
+
+ this.canvas.add(helpLine1)
+ this.canvas.add(helpLine2)
+ this.canvas.remove(prevLine)
+ this.canvas.remove(line)
+ this.canvas.renderAll()
+ })
+ // 용마루
+
+ const ridgePoint = []
+
+ helpPoints.forEach((helpPoint, index) => {
+ const closestLine = findClosestLineToPoint(helpPoint, notInterSectionLines)
+
+ // 가까운 선의 중심점
+ const centerClosestLinePoint = {
+ x: (closestLine.x1 + closestLine.x2) / 2,
+ y: (closestLine.y1 + closestLine.y2) / 2,
+ }
+
+ const direction = getDirectionByPoint(helpPoint, centerClosestLinePoint)
+
+ let newX, newY
+
+ switch (direction) {
+ case 'left': {
+ newX = ((closestLine.x2 - closestLine.x1) * (helpPoint.y - closestLine.y1)) / (closestLine.y2 - closestLine.y1) + closestLine.x1
+ newY = helpPoint.y
+ break
+ }
+ case 'right': {
+ newX = ((closestLine.x2 - closestLine.x1) * (helpPoint.y - closestLine.y1)) / (closestLine.y2 - closestLine.y1) + closestLine.x1
+ newY = helpPoint.y
+ break
+ }
+ case 'top': {
+ newX = helpPoint.x
+ newY = ((closestLine.y2 - closestLine.y1) * (helpPoint.x - closestLine.x1)) / (closestLine.x2 - closestLine.x1) + closestLine.y1
+ break
+ }
+ case 'bottom': {
+ newX = helpPoint.x
+ newY = ((closestLine.y2 - closestLine.y1) * (helpPoint.x - closestLine.x1)) / (closestLine.x2 - closestLine.x1) + closestLine.y1
+ break
+ }
+ }
+
+ const ridgeHelpLine = new QLine([closestLine.x1, closestLine.y1, newX, newY], {
+ fontSize: this.fontSize,
+ stroke: 'purple',
+ strokeWidth: 5,
+ })
+
+ ridgePoint.push({ x: newX, y: newY })
+
+ const ridge = new QLine([helpPoint.x, helpPoint.y, newX, newY], {
+ fontSize: this.fontSize,
+ stroke: 'skyblue',
+ strokeWidth: 5,
+ })
+
+ this.canvas.add(ridge)
+ this.canvas.renderAll()
+
+ this.canvas.add(ridgeHelpLine)
+ this.canvas.remove(closestLine)
+ this.canvas.renderAll()
+ })
+
+ // 용마루 끼리 연결
+ for (let i = 0; i < ridgePoint.length; i = i + 2) {
+ const currentRidgeEndPoint = ridgePoint[i]
+ const nextRidgeEndPoint = ridgePoint[(i + 1) % ridgePoint.length]
+ const ridgeConnectLine = new QLine([currentRidgeEndPoint.x, currentRidgeEndPoint.y, nextRidgeEndPoint.x, nextRidgeEndPoint.y], {
+ fontSize: this.fontSize,
+ stroke: 'green',
+ strokeWidth: 5,
+ })
+ this.canvas.add(ridgeConnectLine)
+ this.canvas.renderAll()
+ }
this.canvas.renderAll()
})
-
- /**
- * 삼각 지붕
- */
- historyLines.forEach((line, index) => {
- const prevLine = historyLines[(index - 1 + historyLines.length) % historyLines.length]
-
- let intersectionPoint = calculateIntersection(line, prevLine)
-
- if (!intersectionPoint) {
- notInterSectionLines.push(line)
- return
- }
-
- const helpLine1 = new QLine([prevLine.x1, prevLine.y1, intersectionPoint.x, intersectionPoint.y], {
- fontSize: this.fontSize,
- stroke: 'red',
- })
-
- const helpLine2 = new QLine([line.x1, line.y1, intersectionPoint.x, intersectionPoint.y], {
- fontSize: this.fontSize,
- stroke: 'red',
- })
- notInterSectionLines.pop()
- helpPoints.push(intersectionPoint)
-
- this.addWithUpdate(helpLine1)
- this.addWithUpdate(helpLine2)
- this.removeWithUpdate(prevLine)
- this.removeWithUpdate(line)
- this.canvas.renderAll()
- })
- // 용마루
-
- const ridgePoint = []
-
- helpPoints.forEach((helpPoint, index) => {
- const closestLine = findClosestLineToPoint(helpPoint, notInterSectionLines)
-
- // 가까운 선의 중심점
- const centerClosestLinePoint = {
- x: (closestLine.x1 + closestLine.x2) / 2,
- y: (closestLine.y1 + closestLine.y2) / 2,
- }
-
- const direction = getDirectionByPoint(helpPoint, centerClosestLinePoint)
-
- let newX, newY
-
- switch (direction) {
- case 'left': {
- newX = ((closestLine.x2 - closestLine.x1) * (helpPoint.y - closestLine.y1)) / (closestLine.y2 - closestLine.y1) + closestLine.x1
- newY = helpPoint.y
- break
- }
- case 'right': {
- newX = ((closestLine.x2 - closestLine.x1) * (helpPoint.y - closestLine.y1)) / (closestLine.y2 - closestLine.y1) + closestLine.x1
- newY = helpPoint.y
- break
- }
- case 'top': {
- newX = helpPoint.x
- newY = ((closestLine.y2 - closestLine.y1) * (helpPoint.x - closestLine.x1)) / (closestLine.x2 - closestLine.x1) + closestLine.y1
- break
- }
- case 'bottom': {
- newX = helpPoint.x
- newY = ((closestLine.y2 - closestLine.y1) * (helpPoint.x - closestLine.x1)) / (closestLine.x2 - closestLine.x1) + closestLine.y1
- break
- }
- }
-
- const ridgeHelpLine = new QLine([closestLine.x1, closestLine.y1, newX, newY], {
- fontSize: this.fontSize,
- stroke: 'purple',
- strokeWidth: 5,
- })
-
- ridgePoint.push({ x: newX, y: newY })
-
- const ridge = new QLine([helpPoint.x, helpPoint.y, newX, newY], {
- fontSize: this.fontSize,
- stroke: 'skyblue',
- strokeWidth: 5,
- })
-
- this.addWithUpdate(ridge)
- this.canvas.renderAll()
-
- this.addWithUpdate(ridgeHelpLine)
- this.removeWithUpdate(closestLine)
- this.canvas.renderAll()
- })
-
- // 용마루 끼리 연결
- for (let i = 0; i < ridgePoint.length; i = i + 2) {
- const currentRidgeEndPoint = ridgePoint[i]
- const nextRidgeEndPoint = ridgePoint[(i + 1) % ridgePoint.length]
- const ridgeConnectLine = new QLine([currentRidgeEndPoint.x, currentRidgeEndPoint.y, nextRidgeEndPoint.x, nextRidgeEndPoint.y], {
- fontSize: this.fontSize,
- stroke: 'green',
- strokeWidth: 5,
- })
- this.addWithUpdate(ridgeConnectLine)
- this.canvas.renderAll()
- }
-
- this.canvas.renderAll()
}
#drawHelpLineInOctagon(chon) {
diff --git a/src/hooks/useCanvas.js b/src/hooks/useCanvas.js
index 5f44060a..44ed17ce 100644
--- a/src/hooks/useCanvas.js
+++ b/src/hooks/useCanvas.js
@@ -1,10 +1,6 @@
import { useEffect, useRef, useState } from 'react'
import { fabric } from 'fabric'
-import {
- actionHandler,
- anchorWrapper,
- polygonPositionHandler,
-} from '@/util/canvas-util'
+import { actionHandler, anchorWrapper, polygonPositionHandler } from '@/util/canvas-util'
import { useRecoilState } from 'recoil'
import { canvasSizeState, fontSizeState } from '@/store/canvasAtom'
@@ -31,17 +27,6 @@ export function useCanvas(id) {
selection: false,
})
- // settings for all canvas in the app
- fabric.Object.prototype.transparentCorners = false
- fabric.Object.prototype.cornerColor = '#2BEBC8'
- fabric.Object.prototype.cornerStyle = 'rect'
- fabric.Object.prototype.cornerStrokeColor = '#2BEBC8'
- fabric.Object.prototype.cornerSize = 6
-
- QPolygon.prototype.canvas = c
- QLine.prototype.canvas = c
- QRect.prototype.canvas = c
-
setCanvas(c)
return () => {
c.dispose()
@@ -57,24 +42,14 @@ export function useCanvas(id) {
useEffect(() => {
canvas
?.getObjects()
- .filter(
- (obj) =>
- obj.type === 'textbox' ||
- obj.type === 'text' ||
- obj.type === 'i-text',
- )
+ .filter((obj) => obj.type === 'textbox' || obj.type === 'text' || obj.type === 'i-text')
.forEach((obj) => {
obj.set({ fontSize: fontSize })
})
canvas
?.getObjects()
- .filter(
- (obj) =>
- obj.type === 'QLine' ||
- obj.type === 'QPolygon' ||
- obj.type === 'QRect',
- )
+ .filter((obj) => obj.type === 'QLine' || obj.type === 'QPolygon' || obj.type === 'QRect')
.forEach((obj) => {
obj.setFontSize(fontSize)
})
@@ -121,26 +96,43 @@ export function useCanvas(id) {
*/
const removeMouseLines = () => {
if (canvas?._objects.length > 0) {
- const mouseLines = canvas?._objects.filter(
- (obj) => obj.name === 'mouseLine',
- )
+ const mouseLines = canvas?._objects.filter((obj) => obj.name === 'mouseLine')
mouseLines.forEach((item) => canvas?.remove(item))
}
canvas?.renderAll()
}
- /**
- * 눈금 그리기
- */
const initialize = () => {
canvas?.clear()
+ // settings for all canvas in the app
+ fabric.Object.prototype.transparentCorners = false
+ fabric.Object.prototype.cornerColor = '#2BEBC8'
+ fabric.Object.prototype.cornerStyle = 'rect'
+ fabric.Object.prototype.cornerStrokeColor = '#2BEBC8'
+ fabric.Object.prototype.cornerSize = 6
- // 기존 이벤트가 있을 경우 제거한다.
- // removeEventOnCanvas()
+ QPolygon.prototype.canvas = canvas
+ QLine.prototype.canvas = canvas
+ QRect.prototype.canvas = canvas
- // 작업 후에 event를 추가해준다.
+ fabric.QLine = fabric.util.createClass(fabric.Group, {})
+ fabric.QPolygon = fabric.util.createClass(fabric.Group, {})
- // addEventOnCanvas()
+ // fromObject 메서드를 QLine 클래스에 직접 추가
+ fabric.QLine.fromObject = function (object, callback) {
+ const { initOption, initPoints, initLengthTxt } = object
+ fabric.util.enlivenObjects(object.objects, function (enlivenedObjects) {
+ return callback(new QLine(initPoints, initOption, initLengthTxt))
+ })
+ }
+
+ // fromObject 메서드를 QLine 클래스에 직접 추가
+ fabric.QPolygon.fromObject = function (object, callback) {
+ const { initOption, initPoints, initLengthTxt } = object
+ fabric.util.enlivenObjects(object.objects, function (enlivenedObjects) {
+ return callback(new QPolygon(initPoints, initOption, initLengthTxt))
+ })
+ }
}
/**
@@ -176,26 +168,20 @@ export function useCanvas(id) {
}
// 가로선을 그립니다.
- const horizontalLine = new fabric.Line(
- [0, pointer.y, canvasSize.horizontal, pointer.y],
- {
- stroke: 'black',
- strokeWidth: 1,
- selectable: false,
- name: 'mouseLine',
- },
- )
+ const horizontalLine = new fabric.Line([0, pointer.y, canvasSize.horizontal, pointer.y], {
+ stroke: 'black',
+ strokeWidth: 1,
+ selectable: false,
+ name: 'mouseLine',
+ })
// 세로선을 그립니다.
- const verticalLine = new fabric.Line(
- [pointer.x, 0, pointer.x, canvasSize.vertical],
- {
- stroke: 'black',
- strokeWidth: 1,
- selectable: false,
- name: 'mouseLine',
- },
- )
+ const verticalLine = new fabric.Line([pointer.x, 0, pointer.x, canvasSize.vertical], {
+ stroke: 'black',
+ strokeWidth: 1,
+ selectable: false,
+ name: 'mouseLine',
+ })
// 선들을 캔버스에 추가합니다.
canvas?.add(horizontalLine, verticalLine)
@@ -325,7 +311,6 @@ export function useCanvas(id) {
}
const jsonStr = JSON.stringify(canvas)
localStorage.setItem('canvas', jsonStr)
- handleClear()
}
/**
@@ -479,10 +464,7 @@ export function useCanvas(id) {
poly.controls = poly.points.reduce(function (acc, point, index) {
acc['p' + index] = new fabric.Control({
positionHandler: polygonPositionHandler,
- actionHandler: anchorWrapper(
- index > 0 ? index - 1 : lastControl,
- actionHandler,
- ),
+ actionHandler: anchorWrapper(index > 0 ? index - 1 : lastControl, actionHandler),
actionName: 'modifyPolygon',
pointIndex: index,
})
@@ -571,6 +553,54 @@ export function useCanvas(id) {
})
}
+ const addCanvas = () => {
+ // const canvasState = canvas
+
+ const objs = canvas
+
+ console.log(objs)
+ const str = JSON.stringify(objs)
+
+ canvas?.clear()
+
+ console.log(str)
+ console.log(JSON.parse(str))
+
+ // 역직렬화하여 캔버스에 객체를 다시 추가합니다.
+ setTimeout(() => {
+ canvas?.loadFromJSON(
+ JSON.parse(str),
+ function () {
+ // 모든 객체가 로드되고 캔버스에 추가된 후 호출됩니다.
+ canvas?.renderAll() // 캔버스를 다시 그립니다.
+ console.log('Objects are reloaded and rendered on canvas.')
+ },
+ function (o, object) {
+ // 각 객체가 로드될 때마다 호출됩니다.
+ console.log('Object loaded: ', o, object)
+
+ canvas?.add(object)
+ canvas?.renderAll()
+ },
+ )
+ }, 1000)
+
+ /*canvas?.loadFromJSON(JSON.parse(str), () => {
+ console.log('load done')
+ })*/
+
+ /*const stateArr = canvasState.map((state) => state.getObject().getObjects())
+
+ const newCanvasList = [...canvasList, JSON.stringify(stateArr)]
+
+ setCanvasList(newCanvasList)*/
+ }
+
+ const changeCanvas = (idx) => {
+ canvas?.clear()
+ const canvasState = JSON.parse(canvasList[idx])
+ }
+
return {
canvas,
addShape,
@@ -585,5 +615,7 @@ export function useCanvas(id) {
saveImage,
handleFlip,
setCanvasBackgroundWithDots,
+ addCanvas,
+ changeCanvas,
}
}
diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js
index e7c84a6d..dab596c2 100644
--- a/src/hooks/useMode.js
+++ b/src/hooks/useMode.js
@@ -1,9 +1,10 @@
-import { useRef, useState } from 'react'
+import { useEffect, useRef, useState } from 'react'
import QRect from '@/components/fabric/QRect'
import QPolygon from '@/components/fabric/QPolygon'
import { findTopTwoIndexesByDistance, getCenterPoint, getDirection, getStartIndex, rearrangeArray } from '@/util/canvas-util'
import { useRecoilState } from 'recoil'
-import { fontSizeState, roofState, sortedPolygonArray, roofPolygonPatternArrayState, wallState } from '@/store/canvasAtom'
+
+import { fontSizeState, roofPolygonPatternArrayState, roofState, sortedPolygonArray, wallState } from '@/store/canvasAtom'
import { QLine } from '@/components/fabric/QLine'
export const Mode = {
@@ -30,8 +31,22 @@ export function useMode() {
const [sortedArray, setSortedArray] = useRecoilState(sortedPolygonArray)
const [roof, setRoof] = useRecoilState(roofState)
const [wall, setWall] = useRecoilState(wallState)
+
const [roofPolygonPattern, setRoofPolygonPattern] = useRecoilState(roofPolygonPatternArrayState)
+ useEffect(() => {
+ // 이벤트 리스너 추가
+ if (!canvas) {
+ return
+ }
+ document.addEventListener('keydown', handleKeyDown)
+
+ // 컴포넌트가 언마운트될 때 이벤트 리스너 제거
+ return () => {
+ document.removeEventListener('keydown', handleKeyDown)
+ }
+ }, [canvas]) // 빈 배열을 전달하여 컴포넌트가 마운트될 때만 실행되도록 함
+
const addEvent = (mode) => {
switch (mode) {
case 'default':
@@ -64,6 +79,147 @@ export function useMode() {
}
}
+ const keyValid = () => {
+ if (points.current.length === 0) {
+ alert('시작점을 선택해주세요')
+ return false
+ }
+ return true
+ }
+
+ const drawCircleAndLine = (verticalLength, horizontalLength) => {
+ const circle = new fabric.Circle({
+ radius: 5,
+ fill: 'transparent', // 원 안을 비웁니다.
+ stroke: 'black', // 원 테두리 색상을 검은색으로 설정합니다.
+ left: points.current[points.current.length - 1].left + horizontalLength - 5,
+ top: points.current[points.current.length - 1].top + verticalLength - 5,
+ originX: 'center',
+ originY: 'center',
+ selectable: false,
+ })
+ historyPoints.current.push(circle)
+ points.current.push(circle)
+ canvas?.add(circle)
+ canvas?.renderAll()
+
+ // length 값이 숫자가 아닌 경우
+ if (isNaN(length) || Math.max(Math.abs(verticalLength), Math.abs(horizontalLength)) === 0) {
+ //마지막 추가 된 points 제거합니다.
+
+ const lastPoint = historyPoints.current[historyPoints.current.length - 1]
+
+ canvas?.remove(lastPoint)
+
+ historyPoints.current.pop()
+ points.current.pop()
+ return
+ }
+
+ const line = new QLine(
+ [points.current[0].left, points.current[0].top, points.current[0].left + horizontalLength, points.current[0].top + verticalLength],
+ {
+ stroke: 'black',
+ strokeWidth: 2,
+ selectable: false,
+ viewLengthText: true,
+ direction: getDirection(points.current[0], points.current[1]),
+ fontSize: fontSize,
+ },
+ )
+
+ pushHistoryLine(line)
+
+ // 라인의 끝에 점을 추가합니다.
+ const endPointCircle = new fabric.Circle({
+ radius: 1,
+ fill: 'transparent', // 원 안을 비웁니다.
+ stroke: 'black', // 원 테두리 색상을 검은색으로 설정합니다.
+ left: points.current[0].left + horizontalLength,
+ top: points.current[0].top + verticalLength,
+ originX: 'center',
+ originY: 'center',
+ selectable: false,
+ })
+
+ canvas?.add(endPointCircle)
+
+ historyPoints.current.push(endPointCircle)
+
+ points.current.forEach((point) => {
+ canvas?.remove(point)
+ })
+ points.current = [endPointCircle]
+
+ canvas.renderAll()
+ }
+
+ const handleKeyDown = (e) => {
+ switch (e.key) {
+ case 'ArrowDown': {
+ if (!keyValid()) {
+ return
+ }
+ const verticalLength = Number(prompt('길이를 입력하세요:'))
+ const horizontalLength = 0
+
+ drawCircleAndLine(verticalLength, horizontalLength)
+
+ break
+ }
+ case 'ArrowUp': {
+ if (!keyValid()) {
+ return
+ }
+ const verticalLength = -Number(prompt('길이를 입력하세요:'))
+ const horizontalLength = 0
+
+ drawCircleAndLine(verticalLength, horizontalLength)
+
+ break
+ }
+ case 'ArrowLeft': {
+ if (!keyValid()) {
+ return
+ }
+ const verticalLength = 0
+ const horizontalLength = -Number(prompt('길이를 입력하세요:'))
+
+ drawCircleAndLine(verticalLength, horizontalLength)
+
+ break
+ }
+ case 'ArrowRight': {
+ if (!keyValid()) {
+ return
+ }
+
+ const verticalLength = 0
+ const horizontalLength = Number(prompt('길이를 입력하세요:'))
+
+ drawCircleAndLine(verticalLength, horizontalLength)
+
+ break
+ }
+
+ case 'Enter': {
+ const result = prompt('입력하세요 (a(A패턴),b(B패턴),t(지붕))')
+
+ switch (result) {
+ case 'a':
+ applyTemplateA()
+ break
+ case 'b':
+ applyTemplateB()
+ break
+ case 't':
+ templateMode()
+ break
+ }
+ }
+ }
+ }
+
const changeMode = (canvas, mode) => {
setMode(mode)
// mode변경 시 이전 이벤트 제거
@@ -720,10 +876,17 @@ export function useMode() {
/**
*벽 지붕 외곽선 생성
*/
- const handleOuterlinesTest = (polygon, offset = 71) => {
- var offsetPoints = []
+ const handleOuterlinesTest = (offsetInputX, offsetInputY = 0) => {
+ const polygon = drawWallPolygon()
- debugger
+ let offsetPoints = []
+ const originalMax = 71
+ const transformedMax = 100
+
+ offsetInputY = offsetInputY !== 0 ? offsetInputY : offsetInputX
+
+ const offsetX = (offsetInputX / transformedMax) * originalMax * 2
+ const offsetY = (offsetInputY / transformedMax) * originalMax * 2
const sortedIndex = getStartIndex(polygon.lines)
let tmpArraySorted = rearrangeArray(polygon.lines, sortedIndex)
@@ -776,17 +939,14 @@ export function useMode() {
// 오프셋 적용
var offsetPoint = {
- x1: current.x + unitNormal.x * offset,
- y1: current.y + unitNormal.y * offset,
+ x1: current.x + unitNormal.x * offsetX,
+ y1: current.y + unitNormal.y * offsetY,
}
offsetPoints.push(offsetPoint)
}
- const roof = makePolygon(offsetPoints)
- setRoof(roof)
-
- return roof
+ makePolygon(offsetPoints)
}
/**
@@ -855,6 +1015,7 @@ export function useMode() {
const roof = makePolygon(offsetPoints)
roof.setWall(polygon)
setRoof(roof)
+
roof.drawHelpLine()
}
@@ -905,6 +1066,8 @@ export function useMode() {
} else if (polygon.lines.length === 6) {
//6각형
handleOuterLineTemplateA6Points(polygon)
+ } else if (polygon.lines.length === 8) {
+ handleOuterLineTemplateA8Points(polygon)
}
}
@@ -1721,6 +1884,214 @@ export function useMode() {
canvas.renderAll()
}
+ const handleOuterLineTemplateA8Points = (polygon, offsetInputX = 50, offsetInputY = 20) => {
+ let offsetPoints = []
+
+ const originalMax = 71
+ const transformedMax = 100
+
+ let lines = [] //내각라인
+ let outLines = [] //아웃라인
+ let halfLength = 0 //선길이
+
+ const dashedCenterLineOpt = {
+ stroke: 'black',
+ strokeWidth: 4,
+ property: 'centerLine',
+ strokeDashArray: [5, 5],
+ fontSize: 14,
+ }
+
+ const centerLineOpt = {
+ stroke: 'blue',
+ strokeWidth: 5,
+ property: 'bigHoriCenter',
+ fontSize: 14,
+ }
+
+ // 폴리곤의 각 변을 선으로 생성
+ for (let i = 0; i < polygon.points.length; i++) {
+ const start = polygon.points[i]
+ const end = polygon.points[(i + 1) % polygon.points.length] // 다음 점, 마지막 점의 경우 첫 점으로
+
+ const color = i % 2 === 0 ? '#A0D468' : 'skyblue'
+
+ const line = new QLine([start.x, start.y, end.x, end.y], {
+ stroke: color,
+ strokeWidth: 2,
+ property: 'normal',
+ fontSize: 14,
+ })
+
+ // 선을 배열에 추가
+ lines.push(line)
+ canvas.add(line)
+ }
+
+ offsetInputY = offsetInputY !== 0 ? offsetInputY : offsetInputX
+
+ const sortedIndex = getStartIndex(polygon.lines)
+ let tmpArraySorted = rearrangeArray(polygon.lines, sortedIndex)
+
+ if (tmpArraySorted[0].direction === 'right') {
+ //시계방향
+ tmpArraySorted = tmpArraySorted.reverse() //그럼 배열을 거꾸로 만들어서 무조건 반시계방향으로 배열 보정
+ }
+
+ setSortedArray(tmpArraySorted) //recoil에 넣음
+
+ const points = tmpArraySorted.map((line) => ({
+ x: line.x1,
+ y: line.y1,
+ }))
+
+ // 외적을 계산하는 함수
+ function crossProduct(p1, p2, p3) {
+ const dx1 = p2.x - p1.x
+ const dy1 = p2.y - p1.y
+ const dx2 = p3.x - p2.x
+ const dy2 = p3.y - p2.y
+ return dx1 * dy2 - dy1 * dx2
+ }
+
+ let concaveIndices = [] //볼록한 부분 인덱스 배열
+ let concavePointIndices = [] //오목한 부분 인덱스 배열
+
+ // 오목한 부분 찾기
+ function findConcavePointIndices(points) {
+ let concaveIndices = []
+ for (let i = 0; i < points.length; i++) {
+ const p1 = points[i]
+ const p2 = points[(i + 1) % points.length]
+ const p3 = points[(i + 2) % points.length]
+ const cross = crossProduct(p1, p2, p3)
+ if (cross < 0) {
+ concaveIndices.push((i + 1) % points.length)
+ } else {
+ concavePointIndices.push((i + 1) % points.length)
+ }
+ }
+ return concaveIndices
+ }
+
+ // 오목한 부분 인덱스 찾기
+ concaveIndices = findConcavePointIndices(points) //오목한 부분을 제외한 인덱스
+ const concavePoints = concaveIndices.map((index) => points[index])
+
+ for (var i = 0; i < points.length; i++) {
+ var prev = points[(i - 1 + points.length) % points.length]
+ var current = points[i]
+ var next = points[(i + 1) % points.length]
+
+ // 두 벡터 계산 (prev -> current, current -> next)
+ var vector1 = { x: current.x - prev.x, y: current.y - prev.y }
+ var vector2 = { x: next.x - current.x, y: next.y - current.y }
+
+ // 벡터의 길이 계산
+ var length1 = Math.sqrt(vector1.x * vector1.x + vector1.y * vector1.y)
+ var length2 = Math.sqrt(vector2.x * vector2.x + vector2.y * vector2.y)
+
+ // 벡터를 단위 벡터로 정규화
+ var unitVector1 = { x: vector1.x / length1, y: vector1.y / length1 }
+ var unitVector2 = { x: vector2.x / length2, y: vector2.y / length2 }
+
+ // 법선 벡터 계산 (왼쪽 방향)
+ var normal1 = { x: -unitVector1.y, y: unitVector1.x }
+ var normal2 = { x: -unitVector2.y, y: unitVector2.x }
+
+ // 법선 벡터 평균 계산
+ var averageNormal = {
+ x: (normal1.x + normal2.x) / 2,
+ y: (normal1.y + normal2.y) / 2,
+ }
+
+ // 평균 법선 벡터를 단위 벡터로 정규화
+ var lengthNormal = Math.sqrt(averageNormal.x * averageNormal.x + averageNormal.y * averageNormal.y)
+ var unitNormal = {
+ x: averageNormal.x / lengthNormal,
+ y: averageNormal.y / lengthNormal,
+ }
+
+ let offsetX
+ let offsetY
+
+ if (concavePointIndices[0] === i || concavePointIndices[1] === i) {
+ // 인덱스가 배열이랑 같으면
+ if ((concavePointIndices[0] === 4 && concavePointIndices[1] === 5) || (concavePointIndices[0] === 2 && concavePointIndices[1] === 3)) {
+ offsetX = 1
+ offsetY = (offsetInputY / transformedMax) * originalMax * 2
+ } else {
+ offsetX = (offsetInputX / transformedMax) * originalMax * 2
+ offsetY = 1
+ }
+ } else {
+ offsetX = (offsetInputX / transformedMax) * originalMax * 2
+ offsetY = (offsetInputY / transformedMax) * originalMax * 2
+ }
+
+ // 오프셋 적용
+ var offsetPoint = {
+ x1: current.x + unitNormal.x * offsetX,
+ y1: current.y + unitNormal.y * offsetY,
+ }
+
+ offsetPoints.push(offsetPoint)
+ }
+
+ const outlinePolygon = makePolygon(offsetPoints)
+
+ // 아웃라인 폴리곤의 각 변을 선으로 생성
+ for (let i = 0; i < outlinePolygon.points.length; i++) {
+ const start = outlinePolygon.points[i]
+ const end = outlinePolygon.points[(i + 1) % outlinePolygon.points.length] // 다음 점, 마지막 점의 경우 첫 점으로
+
+ const line = new QLine([start.x, start.y, end.x, end.y], {
+ stroke: 'blue',
+ strokeWidth: 2,
+ property: 'normal',
+ fontSize: 14,
+ })
+
+ // 선을 배열에 추가
+ outLines.push(line)
+ canvas.add(line)
+ }
+
+ canvas?.remove(outlinePolygon) //임시 폴리곤을 삭제
+ canvas?.remove(outLines[concavePointIndices[0]]) //가운데 제외되는 선을 지운다
+
+ //라인들을 좌측에서 -> 우측으로 그리는거처럼 데이터 보정
+ outLines.forEach((outline, index) => {
+ let minX, minY, maxX, maxY
+ if (outline.x2 < outline.x1 || outline.y2 < outline.y1) {
+ outLines[index].x1 = outline.x2
+ outLines[index].y1 = outline.y2
+ outLines[index].x2 = outline.x1
+ outLines[index].y2 = outline.y1
+ outLines[index].line.x1 = minX
+ outLines[index].line.y1 = minY
+ outLines[index].line.x2 = maxX
+ outLines[index].line.y2 = maxY
+ }
+ })
+
+ // for (let i = 0; i < outLines.length; i++) {
+ // if (!i % 2 === 0) {
+ // if (i === concavePointIndices[0] - 1 || i === concavePointIndices[1] + 1) {
+ // //배열 3번이나 5번일때
+ // } else {
+ // }
+ // }
+ // }
+
+ let parallelLines = concavePointIndices[0] + 4 //들어간선에 무조건 평행하는 선 찾기
+ if (parallelLines > outLines.length) {
+ parallelLines = outLines[concavePointIndices[0] + 4 - outLines.length - 1]
+ }
+
+ canvas.renderAll()
+ }
+
/**
* 템플릿 B 적용
*/
@@ -2217,6 +2588,7 @@ export function useMode() {
zoomOut,
zoom,
togglePolygonLine,
+ handleOuterlinesTest,
handleOuterlinesTest2,
makeRoofPatternPolygon,
}
diff --git a/src/lib/canvas.js b/src/lib/canvas.js
new file mode 100644
index 00000000..c2dd207f
--- /dev/null
+++ b/src/lib/canvas.js
@@ -0,0 +1,38 @@
+'use server'
+
+import { PrismaClient } from '@prisma/client'
+
+const prisma = new PrismaClient()
+
+export const getTests = () => {
+ return prisma.test.findMany()
+}
+
+export const insertTest = async (param) => {
+ return prisma.test.create({
+ data: {
+ content: param,
+ },
+ })
+}
+
+export const getCanvasStateAll = () => {
+ return prisma.canvas.findMany()
+}
+
+export const getCanvasState = () => {
+ return prisma.canvas.findFirst({
+ where: {
+ loginId: 'test',
+ },
+ orderBy: {
+ id: 'desc',
+ },
+ })
+}
+
+export const insertCanvasState = (param) => {
+ return prisma.canvas.create({
+ data: param,
+ })
+}
diff --git a/src/lib/prisma.js b/src/lib/prisma.js
new file mode 100644
index 00000000..aa946098
--- /dev/null
+++ b/src/lib/prisma.js
@@ -0,0 +1,14 @@
+import { PrismaClient } from '@prisma/client'
+
+let prisma
+
+if (process.env.NODE_ENV === 'production') {
+ prisma = new PrismaClient()
+} else {
+ if (!global.prisma) {
+ global.prisma = new PrismaClient()
+ }
+ prisma = global.prisma
+}
+
+export default prisma
diff --git a/src/util/canvas-util.js b/src/util/canvas-util.js
index aa9c7315..072f7bab 100644
--- a/src/util/canvas-util.js
+++ b/src/util/canvas-util.js
@@ -312,21 +312,109 @@ export function calculateIntersection(line1, line2) {
if (t >= 0 && t <= 1 && u >= 0 && u <= 1) {
const intersectionX = x1_1 + t * (x2_1 - x1_1)
const intersectionY = y1_1 + t * (y2_1 - y1_1)
- return { x: Math.round(intersectionX), y: Math.round(intersectionY) }
+
+ // Determine the min and max for line1 and line2 for both x and y
+ const line1MinX = Math.min(line1.x1, line1.x2)
+ const line1MaxX = Math.max(line1.x1, line1.x2)
+ const line2MinX = Math.min(line2.x1, line2.x2)
+ const line2MaxX = Math.max(line2.x1, line2.x2)
+
+ const line1MinY = Math.min(line1.y1, line1.y2)
+ const line1MaxY = Math.max(line1.y1, line1.y2)
+ const line2MinY = Math.min(line2.y1, line2.y2)
+ const line2MaxY = Math.max(line2.y1, line2.y2)
+
+ // 교차점이 선분의 범위 내에 있는지 확인
+ if (
+ intersectionX >= line1MinX &&
+ intersectionX <= line1MaxX &&
+ intersectionX >= line2MinX &&
+ intersectionX <= line2MaxX &&
+ intersectionY >= line1MinY &&
+ intersectionY <= line1MaxY &&
+ intersectionY >= line2MinY &&
+ intersectionY <= line2MaxY
+ ) {
+ return { x: Math.round(intersectionX), y: Math.round(intersectionY) }
+ }
}
return null // 교차점이 선분의 범위 내에 없음
}
-export const calculateIntersection2 = (line1, line2) => {
- const result = intersect([line1.x1, line1.y1], [line1.x2, line1.y2], [line2.x1, line2.y1], [line2.x2, line2.y2])
- return { x: Math.round(result[0]), y: Math.round(result[1]) }
+export const findIntersection1 = (line1, line2) => {
+ const { x1, y1, x2, y2 } = line1 // 첫 번째 선의 두 점
+
+ const x3 = line2.x1
+ const y3 = line2.y1
+ const x4 = line2.x2
+ const y4 = line2.y2
+
+ // 선의 방정식의 계수 계산
+ const A1 = y2 - y1
+ const B1 = x1 - x2
+ const C1 = A1 * x1 + B1 * y1
+
+ const A2 = y4 - y3
+ const B2 = x3 - x4
+ const C2 = A2 * x3 + B2 * y3
+
+ const determinant = A1 * B2 - A2 * B1
+
+ if (determinant === 0) {
+ // 두 선이 평행하거나 일직선일 경우
+ return null
+ }
+
+ const x = (B1 * C2 - B2 * C1) / determinant
+ const y = (A1 * C2 - A2 * C1) / determinant
+
+ return { x, y }
}
-function findOrthogonalPoint(x1, y1, x2, y2, x3, y3, x4, y4) {
+export const calculateIntersection2 = (line1, line2) => {
+ const result = intersect([line1.x1, line1.y1], [line1.x2, line1.y2], [line2.x1, line2.y1], [line2.x2, line2.y2])
+
+ if (!result) {
+ return null
+ }
+
+ // Determine the min and max for line1 and line2 for both x and y
+ const line1MinX = Math.min(line1.x1, line1.x2)
+ const line1MaxX = Math.max(line1.x1, line1.x2)
+ const line2MinX = Math.min(line2.x1, line2.x2)
+ const line2MaxX = Math.max(line2.x1, line2.x2)
+
+ const line1MinY = Math.min(line1.y1, line1.y2)
+ const line1MaxY = Math.max(line1.y1, line1.y2)
+ const line2MinY = Math.min(line2.y1, line2.y2)
+ const line2MaxY = Math.max(line2.y1, line2.y2)
+
+ // Check if the intersection X and Y are within the range of both lines
+ if (
+ result[0] >= line1MinX &&
+ result[0] <= line1MaxX &&
+ result[0] >= line2MinX &&
+ result[0] <= line2MaxX &&
+ result[1] >= line1MinY &&
+ result[1] <= line1MaxY &&
+ result[1] >= line2MinY &&
+ result[1] <= line2MaxY
+ ) {
+ return { x: Math.round(result[0]), y: Math.round(result[1]) }
+ } else {
+ return null // Intersection is out of range
+ }
+}
+
+export function findOrthogonalPoint(line1, line2) {
// Calculate the intersection point of two lines
- const intersectionX = ((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / ((x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4))
- const intersectionY = ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / ((x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4))
+ const intersectionX =
+ ((line1.x1 * line2.y1 - line1.y1 * line2.x1) * (line2.x2 - line2.x1) - (line1.x1 - line1.x2) * (line2.x2 * line2.y2 - line2.y1 * line2.x1)) /
+ ((line1.x1 - line1.x2) * (line2.y1 - line2.y2) - (line1.y1 - line1.y2) * (line2.x2 - line2.x1))
+ const intersectionY =
+ ((line1.x1 * line2.y1 - line1.y1 * line2.x1) * (line2.y2 - line2.y1) - (line1.y1 - line1.y2) * (line2.x2 * line2.y2 - line2.y1 * line2.x1)) /
+ ((line1.x1 - line1.x2) * (line2.y1 - line2.y2) - (line1.y1 - line1.y2) * (line2.x2 - line2.x1))
return { x: intersectionX, y: intersectionY }
}
@@ -338,7 +426,6 @@ function findOrthogonalPoint(x1, y1, x2, y2, x3, y3, x4, y4) {
export const sortedPoints = (points) => {
const copyPoints = [...points]
//points를 x,y좌표를 기준으로 정렬합니다.
-
copyPoints.sort((a, b) => {
if (a.x === b.x) {
return a.y - b.y
@@ -354,7 +441,6 @@ export const sortedPoints = (points) => {
let index = 1
let currentPoint = { ...copyPoints[0] }
copyPoints.splice(0, 1)
-
while (index < points.length) {
if (index === points.length - 1) {
resultPoints.push(copyPoints[0])
@@ -364,9 +450,15 @@ export const sortedPoints = (points) => {
// 짝수번째는 y값이 같은 점을 찾는다.
for (let i = 0; i < copyPoints.length; i++) {
// y값이 같은 point가 많은 경우 그 중 x값이 가장 큰걸 찾는다.
- const temp = copyPoints.filter((point) => point.y === currentPoint.y)
- // temp중 x값이 가장 큰 값 copyPoint보다 큰값
- const min = temp.reduce((prev, current) => (prev.x <= current.x ? prev : current))
+ let temp = copyPoints.filter((point) => point.y === currentPoint.y)
+ if (temp.length === 0) {
+ // temp가 비어있을 경우 copyPoints에서 가장 가까운 점을 찾는다.
+ temp = Array.of(findClosestPointByY(currentPoint, copyPoints))
+ }
+ // temp중 x값이 가장 가까운 값
+
+ const min = temp.reduce((prev, current) => (Math.abs(current.x - currentPoint.x) <= Math.abs(prev.x - currentPoint.x) ? current : prev))
+
resultPoints.push(min)
currentPoint = min
copyPoints.splice(copyPoints.indexOf(min), 1)
@@ -377,9 +469,14 @@ export const sortedPoints = (points) => {
// 홀수번째는 x값이 같은 점을 찾는다.
for (let i = 0; i < copyPoints.length; i++) {
// x값이 같은 point가 많은 경우 그 중 y값이 가장 큰걸 찾는다.
- const temp = copyPoints.filter((point) => point.x === currentPoint.x)
- // temp중 y값이 가장 작은 값
- const min = temp.reduce((prev, current) => (prev.y <= current.y ? prev : current))
+ let temp = copyPoints.filter((point) => point.x === currentPoint.x)
+ if (temp.length === 0) {
+ // temp가 비어있을 경우 copyPoints에서 가장 가까운 점을 찾는다.
+
+ temp = Array.of(findClosestPointByX(currentPoint, copyPoints))
+ }
+ // temp중 y값이 가장 가까운 값
+ const min = temp.reduce((prev, current) => (Math.abs(current.y - currentPoint.y) <= Math.abs(prev.y - currentPoint.y) ? current : prev))
resultPoints.push(min)
currentPoint = min
@@ -389,7 +486,6 @@ export const sortedPoints = (points) => {
}
}
}
-
return resultPoints
}
@@ -430,6 +526,81 @@ export function findClosestLineToPoint(point, lines) {
return closestLine
}
+/**
+ * x값이 가장 가까운 점
+ * @param targetPoint
+ * @param points
+ * @returns {*|null}
+ */
+function findClosestPointByX(targetPoint, points) {
+ if (points.length === 0) {
+ return null // Return null if the points array is empty
+ }
+
+ let closestPoint = points[0]
+ let smallestDistance = Math.abs(targetPoint.x - points[0].x)
+
+ for (let i = 1; i < points.length; i++) {
+ const currentDistance = Math.abs(targetPoint.x - points[i].x)
+ if (currentDistance < smallestDistance) {
+ smallestDistance = currentDistance
+ closestPoint = points[i]
+ }
+ }
+
+ return closestPoint
+}
+
+/**
+ * y값이 가장 가까운 점
+ * @param targetPoint
+ * @param points
+ * @returns {*|null}
+ */
+function findClosestPointByY(targetPoint, points) {
+ if (points.length === 0) {
+ return null // Return null if the points array is empty
+ }
+
+ let closestPoint = points[0]
+ let smallestDistance = Math.abs(targetPoint.y - points[0].y)
+
+ for (let i = 1; i < points.length; i++) {
+ const currentDistance = Math.abs(targetPoint.y - points[i].y)
+ if (currentDistance < smallestDistance) {
+ smallestDistance = currentDistance
+ closestPoint = points[i]
+ }
+ }
+
+ return closestPoint
+}
+
+/**
+ * 주어진 점에서 points 배열 중 가장 가까운 점을 찾는 함수입니다.
+ * @param {Object} targetPoint - { x: number, y: number } 형태의 대상 점 객체
+ * @param {Array} points - { x: number, y: number } 형태의 점 객체들의 배열
+ * @returns {Object} targetPoint에 가장 가까운 점 객체
+ */
+export function findClosestPoint(targetPoint, points) {
+ if (points.length === 0) {
+ return null // points 배열이 비어있으면 null 반환
+ }
+
+ let closestPoint = points[0]
+ let smallestDistance = calculateDistancePoint(targetPoint, closestPoint)
+
+ for (let i = 1; i < points.length; i++) {
+ const currentDistance = calculateDistancePoint(targetPoint, points[i])
+ if (currentDistance < smallestDistance) {
+ smallestDistance = currentDistance
+ closestPoint = points[i]
+ }
+ }
+
+ return closestPoint
+}
+
/**
* 점과 직선사이의 최단 거리
* @param point
@@ -448,3 +619,33 @@ export function calculateDistance(point, line) {
const denominator = Math.sqrt(Math.pow(y2 - y1, 2) + Math.pow(x2 - x1, 2))
return numerator / denominator
}
+
+/**
+ * 두 점 사이의 거리를 계산하는 함수입니다.
+ * @param {Object} point1 - 첫 번째 점 { x: number, y: number }
+ * @param {Object} point2 - 두 번째 점 { x: number, y: number }
+ * @returns {number} 두 점 사이의 거리
+ */
+function calculateDistancePoint(point1, point2) {
+ return Math.sqrt(Math.pow(point2.x - point1.x, 2) + Math.pow(point2.y - point1.y, 2))
+}
+
+/**
+ * {x, y} 형태의 배열을 받아 중복된 점을 제거하는 함수
+ * @param points
+ * @returns {*[]}
+ */
+export function removeDuplicatePoints(points) {
+ const uniquePoints = []
+ const seen = new Set()
+
+ points.forEach((point) => {
+ const identifier = `${point.x}:${point.y}`
+ if (!seen.has(identifier)) {
+ seen.add(identifier)
+ uniquePoints.push(point)
+ }
+ })
+
+ return uniquePoints
+}
diff --git a/src/util/qline-utils.js b/src/util/qline-utils.js
new file mode 100644
index 00000000..4cbcb118
--- /dev/null
+++ b/src/util/qline-utils.js
@@ -0,0 +1,12 @@
+import { fabric } from 'fabric'
+import { QLine } from '@/components/fabric/QLine'
+
+export const defineQLine = () => {
+ fabric.QLine = fabric.util.createClass(fabric.Group, {})
+ fabric.QLine.fromObject = function (object, callback) {
+ const { initOption, initPoints, initLengthTxt } = object
+ fabric.util.enlivenObjects(object.objects, function (enlivenedObjects) {
+ return callback(new QLine(initPoints, initOption, initLengthTxt))
+ })
+ }
+}
diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js
new file mode 100644
index 00000000..e683e4be
--- /dev/null
+++ b/src/util/qpolygon-utils.js
@@ -0,0 +1,329 @@
+import { fabric } from 'fabric'
+import QPolygon from '@/components/fabric/QPolygon'
+import { QLine } from '@/components/fabric/QLine'
+import { calculateIntersection, calculateIntersection2, distanceBetweenPoints, findIntersection1, removeDuplicatePoints } from '@/util/canvas-util'
+
+export const defineQPloygon = () => {
+ fabric.QPolygon = fabric.util.createClass(fabric.Group, {})
+ // fromObject 메서드를 QLine 클래스에 직접 추가
+ fabric.QPolygon.fromObject = function (object, callback) {
+ const { initOption, initPoints, initLengthTxt } = object
+ fabric.util.enlivenObjects(object.objects, function (enlivenedObjects) {
+ return callback(new QPolygon(initPoints, initOption, initLengthTxt))
+ })
+ }
+}
+
+export const drawHelpLineInHexagon2 = (polygon, chon) => {
+ const oneSideLines = [...polygon.lines].map((line) => {
+ let newX1, newY1, newX2, newY2
+ if (line.direction === 'top') {
+ newX1 = line.x2
+ newY1 = line.y2
+ newX2 = line.x1
+ newY2 = line.y1
+
+ line.x1 = newX1
+ line.y1 = newY1
+ line.x2 = newX2
+ line.y2 = newY2
+ line.direction = 'bottom'
+ } else if (line.direction === 'left') {
+ newX1 = line.x2
+ newY1 = line.y2
+ newX2 = line.x1
+ newY2 = line.y1
+
+ line.x1 = newX1
+ line.y1 = newY1
+ line.x2 = newX2
+ line.y2 = newY2
+ line.direction = 'right'
+ }
+ return line
+ })
+
+ const centerLines = []
+ const helpLines = []
+ const ridgeStartPoints = []
+
+ const horizontalLines = oneSideLines.filter((line) => line.direction === 'right')
+ const verticalLines = oneSideLines.filter((line) => line.direction === 'bottom')
+ // horizontalLines 를 y1 좌표 기준으로 정렬한다.
+ horizontalLines.sort((a, b) => a.y1 - b.y1)
+ // verticalLines 를 x1 좌표 기준으로 정렬한다.
+ verticalLines.sort((a, b) => a.x1 - b.x1)
+
+ const maxHorizontalLineLength = horizontalLines.reduce((prev, current) => (prev.length > current.length ? prev.length : current.length))
+ const maxVerticalLineLength = verticalLines.reduce((prev, current) => (prev.length > current.length ? prev.length : current.length))
+
+ // 모든 가로선의 중심선을 긋는다.
+ horizontalLines.forEach((line, index) => {
+ const nextLine = horizontalLines[(index + 1) % horizontalLines.length]
+
+ line.line.set({ strokeWidth: 5 })
+ nextLine.line.set({ strokeWidth: 5 })
+
+ polygon.canvas.renderAll()
+
+ const startCenterX = Math.min(line.x1, nextLine.x1)
+ const startCenterY = (line.y1 + nextLine.y1) / 2
+
+ const endCenterX = line.length >= nextLine.length ? startCenterX + line.length : startCenterX + nextLine.length
+ const endCenterY = startCenterY
+
+ const centerLine = new QLine([startCenterX, startCenterY, endCenterX, endCenterY], {
+ fontSize: polygon.fontSize,
+ stroke: 'red',
+ strokeWidth: 1,
+ direction: 'horizontal',
+ })
+
+ /*polygon.canvas.add(centerLine)
+ polygon.canvas.renderAll()*/
+
+ centerLines.push(centerLine)
+ })
+
+ // 모든 세로선의 중심선을 긋는다.
+ verticalLines.forEach((line, index) => {
+ const nextLine = verticalLines[(index + 1) % verticalLines.length]
+
+ const startCenterX = (line.x1 + nextLine.x1) / 2
+ const startCenterY = Math.min(line.y1, nextLine.y1)
+
+ const endCenterX = startCenterX
+ let endCenterY = line.length >= nextLine.length ? startCenterY + line.length : startCenterY + nextLine.length
+
+ const centerLine = new QLine([startCenterX, startCenterY, endCenterX, endCenterY], {
+ fontSize: polygon.fontSize,
+ stroke: 'blue',
+ strokeWidth: 1,
+ direction: 'vertical',
+ })
+
+ /*polygon.canvas.add(centerLine)
+ polygon.canvas.renderAll()*/
+ centerLines.push(centerLine)
+ })
+
+ polygon.points.forEach((point, index) => {
+ const wallPoint = polygon.wall.points[index]
+ // 두 점의 좌표
+ const x1 = point.x
+ const y1 = point.y
+ const x2 = wallPoint.x
+ const y2 = wallPoint.y
+
+ let newX2, newY2
+
+ // x1, y1을 기준으로 x2, y2와의 거리를 유지한 새로운 직선 생성
+ const angle = Math.atan2(y2 - y1, x2 - x1)
+
+ let previousIndex = index === 0 ? polygon.lines.length - 1 : index - 1
+ const maxLength = Math.max(polygon.lines[index].length, polygon.lines[previousIndex].length)
+
+ newX2 = Math.floor(x1 + (maxLength / 2 + polygon.points.length * 10) * Math.cos(angle))
+ newY2 = Math.floor(y1 + (maxLength / 2 + polygon.points.length * 10) * Math.sin(angle))
+
+ const line = new QLine([x1, y1, newX2, newY2], {
+ fontSize: polygon.fontSize,
+ stroke: 'green',
+ idx: index,
+ })
+ line.set({ degree: fabric.util.radiansToDegrees(angle) })
+ polygon.canvas.add(line)
+ helpLines.push(line)
+ polygon.canvas.renderAll()
+ })
+
+ helpLines.forEach((line, index) => {
+ for (let i = index + 1; i < helpLines.length; i++) {
+ const nextLine = helpLines[i]
+
+ if (line.isAlreadyInterSection || nextLine.isAlreadyInterSection) {
+ continue
+ }
+
+ let intersectionPoint = calculateIntersection(line, nextLine)
+
+ if (!intersectionPoint) {
+ continue
+ }
+
+ const circle = new fabric.Circle({
+ radius: 3,
+ fill: 'red',
+ left: intersectionPoint.x - 3,
+ top: intersectionPoint.y - 3,
+ })
+
+ polygon.canvas.add(circle)
+
+ line.set({ isAlreadyInterSection: true })
+ nextLine.set({ isAlreadyInterSection: true })
+
+ const helpLine1 = new QLine([nextLine.x1, nextLine.y1, intersectionPoint.x, intersectionPoint.y], {
+ fontSize: polygon.fontSize,
+ stroke: 'skyblue',
+ })
+
+ const helpLine2 = new QLine([line.x1, line.y1, intersectionPoint.x, intersectionPoint.y], {
+ fontSize: polygon.fontSize,
+ stroke: 'skyblue',
+ })
+
+ ridgeStartPoints.push(intersectionPoint)
+ polygon.canvas.add(helpLine1)
+ polygon.canvas.add(helpLine2)
+ polygon.canvas.remove(nextLine)
+ polygon.canvas.remove(line)
+ polygon.canvas.renderAll()
+ }
+ })
+
+ // 안만나는 선들
+ const notInterSectionLines = helpLines.filter((line) => !line.isAlreadyInterSection)
+ const ridgeEndPoints = []
+ let interSectionPoints = []
+
+ notInterSectionLines.forEach((line, index) => {
+ let subCenterLines
+ if (Math.abs(line.degree) < 90) {
+ subCenterLines = centerLines.filter((centerLine) => centerLine.direction === 'vertical')
+ } else {
+ subCenterLines = centerLines.filter((centerLine) => centerLine.direction === 'horizontal')
+ }
+
+ centerLines.forEach((centerLine) => {
+ const interSectionPoint = calculateIntersection2(line, centerLine)
+
+ if (!interSectionPoint) {
+ return
+ }
+
+ ridgeStartPoints.forEach((point) => {
+ line.interSectionPoints.push(interSectionPoint)
+ interSectionPoints.push(interSectionPoint)
+
+ const newLine = new QLine([line.x1, line.y1, interSectionPoint.x, interSectionPoint.y], {
+ stroke: 'black',
+ fontSize: polygon.fontSize,
+ })
+
+ const circle = new fabric.Circle({
+ radius: 3,
+ fill: 'blue',
+ left: interSectionPoint.x - 3,
+ top: interSectionPoint.y - 3,
+ })
+ polygon.canvas.add(circle)
+ polygon.canvas.add(newLine)
+
+ polygon.canvas.remove(line)
+
+ line.set({ isAlreadyInterSection: true })
+ })
+ })
+ })
+
+ interSectionPoints = removeDuplicatePoints(interSectionPoints)
+
+ const startRidgePoint = ridgeStartPoints[0]
+
+ const endRidgePoint = ridgeStartPoints[ridgeStartPoints.length - 1]
+
+ let step = 0
+
+ while (true) {
+ if (step % 2 === 0) {
+ const nextPoint = interSectionPoints.find((point) => point.x === startRidgePoint.x || point.y === startRidgePoint.y)
+
+ if (nextPoint) {
+ const ridge = new QLine([startRidgePoint.x, startRidgePoint.y, nextPoint.x, nextPoint.y], {
+ stroke: 'green',
+ fontSize: polygon.fontSize,
+ })
+ polygon.canvas.add(ridge)
+ polygon.canvas.renderAll()
+ }
+
+ console.log('nextPoint', nextPoint)
+ console.log('startRidgePoint', startRidgePoint)
+ } else {
+ }
+
+ step++
+
+ break
+ }
+
+ /*ridgeStartPoints.forEach((point, index) => {
+ for (let i = index + 1; i < ridgeStartPoints.length; i++) {
+ const currentPoint = ridgeStartPoints[index]
+ const nextPoint = ridgeStartPoints[i]
+
+ if (currentPoint.x === nextPoint.x || currentPoint.y === nextPoint.y) {
+ const ridge = new QLine([currentPoint.x, currentPoint.y, nextPoint.x, nextPoint.y], {
+ stroke: 'black',
+ fontSize: polygon.fontSize,
+ })
+ polygon.canvas.add(ridge)
+ polygon.canvas.renderAll()
+ break
+ }
+ }
+ })*/
+
+ /*ridgeStartPoints.forEach((point, index) => {
+ let arrivalPoint
+ let distance = Infinity
+ let startPoint
+ interSectionPoints.forEach((interSectionPoint) => {
+ if (Math.abs(point.x - interSectionPoint.x) < 1 || Math.abs(point.y - interSectionPoint.y) < 1) {
+ if (distanceBetweenPoints(point, interSectionPoint) < distance) {
+ startPoint = point
+ distance = distanceBetweenPoints(point, interSectionPoint)
+ arrivalPoint = interSectionPoint
+ }
+ }
+ })
+
+ if (arrivalPoint) {
+ const line = notInterSectionLines.filter((line) => line.interSectionPoints.includes(arrivalPoint))[0]
+
+ const ridge = new QLine([startPoint.x, startPoint.y, arrivalPoint.x, arrivalPoint.y], {
+ stroke: 'black',
+ fontSize: polygon.fontSize,
+ })
+
+ const helpLine = new QLine([line.x1, line.y1, arrivalPoint.x, arrivalPoint.y], {
+ stroke: 'red',
+ fontSize: polygon.fontSize,
+ })
+
+ ridgeEndPoints.push(arrivalPoint)
+ polygon.canvas.add(ridge)
+ polygon.canvas.add(helpLine)
+ // polygon.canvas.remove(line)
+ polygon.canvas.renderAll()
+ debugger
+ }
+ })*/
+
+ /*for (let i = 0; i < ridgeEndPoints.length; i = i + 2) {
+ const currentRidgeEndPoint = ridgeEndPoints[i]
+ const nextRidgeEndPoint = ridgeEndPoints[(i + 1) % ridgeEndPoints.length]
+ const ridgeConnectLine = new QLine([currentRidgeEndPoint.x, currentRidgeEndPoint.y, nextRidgeEndPoint.x, nextRidgeEndPoint.y], {
+ fontSize: polygon.fontSize,
+ stroke: 'green',
+ })
+
+ polygon.canvas.add(ridgeConnectLine)
+ polygon.canvas.renderAll()
+ }*/
+}
+
+export const drawHelpLineInHexagon = (polygon, chon) => {
+ // 가장 긴라인을 기준으로 centerLine을 그린다.
+}
diff --git a/yarn.lock b/yarn.lock
index bd27ddfe..94bdd351 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -148,6 +148,13 @@
semver "^7.3.5"
tar "^6.1.11"
+"@mongodb-js/saslprep@^1.1.5":
+ version "1.1.8"
+ resolved "https://registry.yarnpkg.com/@mongodb-js/saslprep/-/saslprep-1.1.8.tgz#d39744540be8800d17749990b0da95b4271840d1"
+ integrity sha512-qKwC/M/nNNaKUBMQ0nuzm47b7ZYWQHN3pcXq4IIcoSBc2hOIrflAxJduIvvqmhoz3gR2TacTAs8vlsCVPkiEdQ==
+ dependencies:
+ sparse-bitfield "^3.0.3"
+
"@next/env@14.2.3":
version "14.2.3"
resolved "https://registry.npmjs.org/@next/env/-/env-14.2.3.tgz"
@@ -1143,6 +1150,47 @@
resolved "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz"
integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==
+"@prisma/client@^5.17.0":
+ version "5.17.0"
+ resolved "https://registry.yarnpkg.com/@prisma/client/-/client-5.17.0.tgz#9079947bd749689c2dabfb9ecc70a24ebefb1f43"
+ integrity sha512-N2tnyKayT0Zf7mHjwEyE8iG7FwTmXDHFZ1GnNhQp0pJUObsuel4ZZ1XwfuAYkq5mRIiC/Kot0kt0tGCfLJ70Jw==
+
+"@prisma/debug@5.17.0":
+ version "5.17.0"
+ resolved "https://registry.yarnpkg.com/@prisma/debug/-/debug-5.17.0.tgz#a765105848993984535b6066f8ebc6e6ead26533"
+ integrity sha512-l7+AteR3P8FXiYyo496zkuoiJ5r9jLQEdUuxIxNCN1ud8rdbH3GTxm+f+dCyaSv9l9WY+29L9czaVRXz9mULfg==
+
+"@prisma/engines-version@5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053":
+ version "5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053"
+ resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053.tgz#3c7cc1ef3ebc34cbd069e5873b9982f2aabf5acd"
+ integrity sha512-tUuxZZysZDcrk5oaNOdrBnnkoTtmNQPkzINFDjz7eG6vcs9AVDmA/F6K5Plsb2aQc/l5M2EnFqn3htng9FA4hg==
+
+"@prisma/engines@5.17.0":
+ version "5.17.0"
+ resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-5.17.0.tgz#74dd1aabb22675892760b3cf69a448e3aef4616b"
+ integrity sha512-+r+Nf+JP210Jur+/X8SIPLtz+uW9YA4QO5IXA+KcSOBe/shT47bCcRMTYCbOESw3FFYFTwe7vU6KTWHKPiwvtg==
+ dependencies:
+ "@prisma/debug" "5.17.0"
+ "@prisma/engines-version" "5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053"
+ "@prisma/fetch-engine" "5.17.0"
+ "@prisma/get-platform" "5.17.0"
+
+"@prisma/fetch-engine@5.17.0":
+ version "5.17.0"
+ resolved "https://registry.yarnpkg.com/@prisma/fetch-engine/-/fetch-engine-5.17.0.tgz#f718dc7426411d1ebeeee53e2d0d38652387f87c"
+ integrity sha512-ESxiOaHuC488ilLPnrv/tM2KrPhQB5TRris/IeIV4ZvUuKeaicCl4Xj/JCQeG9IlxqOgf1cCg5h5vAzlewN91Q==
+ dependencies:
+ "@prisma/debug" "5.17.0"
+ "@prisma/engines-version" "5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053"
+ "@prisma/get-platform" "5.17.0"
+
+"@prisma/get-platform@5.17.0":
+ version "5.17.0"
+ resolved "https://registry.yarnpkg.com/@prisma/get-platform/-/get-platform-5.17.0.tgz#89fdcae2adddebbbf0e7bd0474a6c49d6023519b"
+ integrity sha512-UlDgbRozCP1rfJ5Tlkf3Cnftb6srGrEQ4Nm3og+1Se2gWmCZ0hmPIi+tQikGDUVLlvOWx3Gyi9LzgRP+HTXV9w==
+ dependencies:
+ "@prisma/debug" "5.17.0"
+
"@react-aria/breadcrumbs@3.5.13":
version "3.5.13"
resolved "https://registry.yarnpkg.com/@react-aria/breadcrumbs/-/breadcrumbs-3.5.13.tgz#2686f7f460f20d67fe5cdfe185e32e3e78186962"
@@ -2023,6 +2071,18 @@
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.6.tgz#193ced6a40c8006cfc1ca3f4553444fb38f0e543"
integrity sha512-OpXEVoCKSS3lQqjx9GGGOapBeuW5eUboYHRlHP9urXPX25IKZ6AnP5ZRxtVf63iieUbsHxLn8NQ5Nlftc6yzAA==
+"@types/webidl-conversions@*":
+ version "7.0.3"
+ resolved "https://registry.yarnpkg.com/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz#1306dbfa53768bcbcfc95a1c8cde367975581859"
+ integrity sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==
+
+"@types/whatwg-url@^11.0.2":
+ version "11.0.5"
+ resolved "https://registry.yarnpkg.com/@types/whatwg-url/-/whatwg-url-11.0.5.tgz#aaa2546e60f0c99209ca13360c32c78caf2c409f"
+ integrity sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==
+ dependencies:
+ "@types/webidl-conversions" "*"
+
abab@^2.0.5, abab@^2.0.6:
version "2.0.6"
resolved "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz"
@@ -2158,6 +2218,11 @@ browser-process-hrtime@^1.0.0:
resolved "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz"
integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==
+bson@^6.7.0:
+ version "6.8.0"
+ resolved "https://registry.yarnpkg.com/bson/-/bson-6.8.0.tgz#5063c41ba2437c2b8ff851b50d9e36cb7aaa7525"
+ integrity sha512-iOJg8pr7wq2tg/zSlCCHMi3hMm5JTOxLTagf3zxhcenHsFp+c6uOs6K7W5UE7A4QIJGtqh/ZovFNMP4mOPJynQ==
+
busboy@1.6.0:
version "1.6.0"
resolved "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz"
@@ -2852,6 +2917,11 @@ mathjs@^13.0.2:
tiny-emitter "^2.1.0"
typed-function "^4.2.1"
+memory-pager@^1.0.2:
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/memory-pager/-/memory-pager-1.5.0.tgz#d8751655d22d384682741c972f2c3d6dfa3e66b5"
+ integrity sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==
+
merge2@^1.3.0:
version "1.4.1"
resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz"
@@ -2926,6 +2996,23 @@ mkdirp@^1.0.3:
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
+mongodb-connection-string-url@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.1.tgz#c13e6ac284ae401752ebafdb8cd7f16c6723b141"
+ integrity sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==
+ dependencies:
+ "@types/whatwg-url" "^11.0.2"
+ whatwg-url "^13.0.0"
+
+mongodb@^6.8.0:
+ version "6.8.0"
+ resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-6.8.0.tgz#680450f113cdea6d2d9f7121fe57cd29111fd2ce"
+ integrity sha512-HGQ9NWDle5WvwMnrvUxsFYPd3JEbqD3RgABHBQRuoCEND0qzhsd0iH5ypHsf1eJ+sXmvmyKpP+FLOKY8Il7jMw==
+ dependencies:
+ "@mongodb-js/saslprep" "^1.1.5"
+ bson "^6.7.0"
+ mongodb-connection-string-url "^3.0.0"
+
ms@2.1.2:
version "2.1.2"
resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"
@@ -3134,12 +3221,19 @@ postcss@^8, postcss@^8.4.23:
picocolors "^1.0.0"
source-map-js "^1.2.0"
+prisma@^5.17.0:
+ version "5.17.0"
+ resolved "https://registry.yarnpkg.com/prisma/-/prisma-5.17.0.tgz#267b43921ab94805b010537cffa5ccaf530fa066"
+ integrity sha512-m4UWkN5lBE6yevqeOxEvmepnL5cNPEjzMw2IqDB59AcEV6w7D8vGljDLd1gPFH+W6gUxw9x7/RmN5dCS/WTPxA==
+ dependencies:
+ "@prisma/engines" "5.17.0"
+
psl@^1.1.33:
version "1.9.0"
resolved "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz"
integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==
-punycode@^2.1.1:
+punycode@^2.1.1, punycode@^2.3.0:
version "2.3.1"
resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz"
integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
@@ -3378,6 +3472,13 @@ source-map@~0.6.1:
resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
+sparse-bitfield@^3.0.3:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz#ff4ae6e68656056ba4b3e792ab3334d38273ca11"
+ integrity sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==
+ dependencies:
+ memory-pager "^1.0.2"
+
streamsearch@^1.1.0:
version "1.1.0"
resolved "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz"
@@ -3563,6 +3664,13 @@ tr46@^3.0.0:
dependencies:
punycode "^2.1.1"
+tr46@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/tr46/-/tr46-4.1.1.tgz#281a758dcc82aeb4fe38c7dfe4d11a395aac8469"
+ integrity sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==
+ dependencies:
+ punycode "^2.3.0"
+
tr46@~0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
@@ -3690,6 +3798,14 @@ whatwg-url@^11.0.0:
tr46 "^3.0.0"
webidl-conversions "^7.0.0"
+whatwg-url@^13.0.0:
+ version "13.0.0"
+ resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-13.0.0.tgz#b7b536aca48306394a34e44bda8e99f332410f8f"
+ integrity sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==
+ dependencies:
+ tr46 "^4.1.1"
+ webidl-conversions "^7.0.0"
+
whatwg-url@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"