From 5b1dd76aa6a0bc6274173c92f04d74774b0e4368 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Mon, 29 Jul 2024 15:33:43 +0900 Subject: [PATCH] =?UTF-8?q?QLine,=20QPolygon=20canvas=20=EC=97=AD=EC=A7=81?= =?UTF-8?q?=EB=A0=AC=ED=99=94=20=EB=AC=B8=EC=A0=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 문제 없을 시 제거 --- src/components/Roof2.jsx | 60 +- src/components/fabric/QLine.js | 243 +++----- src/components/fabric/QLine2.js | 184 ++++++ src/components/fabric/QLine3.js | 91 --- src/components/fabric/QPolygon.js | 902 ++++----------------------- src/components/fabric/QPolygon3.js | 941 +++++++++++++++++++++++++++++ src/hooks/useCanvas.js | 69 +-- src/hooks/useMode.js | 2 +- src/util/qpolygon-utils.js | 11 +- 9 files changed, 1395 insertions(+), 1108 deletions(-) create mode 100644 src/components/fabric/QLine2.js delete mode 100644 src/components/fabric/QLine3.js create mode 100644 src/components/fabric/QPolygon3.js diff --git a/src/components/Roof2.jsx b/src/components/Roof2.jsx index e483ad7e..9d36b6b0 100644 --- a/src/components/Roof2.jsx +++ b/src/components/Roof2.jsx @@ -3,7 +3,6 @@ import { useEffect, useState } from 'react' import { Mode, useMode } from '@/hooks/useMode' import { Button } from '@nextui-org/react' import QRect from '@/components/fabric/QRect' -import QPolygon from '@/components/fabric/QPolygon' import RangeSlider from './ui/RangeSlider' import { useRecoilState, useRecoilValue } from 'recoil' @@ -12,6 +11,7 @@ import { QLine } from '@/components/fabric/QLine' import { getTests, getCanvasState, insertCanvasState } from '@/lib/canvas' import { calculateIntersection2 } from '@/util/canvas-util' import { CustomLine } from '@/components/fabric/QLine' +import { QPolygon } from '@/components/fabric/QPolygon' export default function Roof2() { const { canvas, handleRedo, handleUndo, setCanvasBackgroundWithDots, saveImage, addCanvas } = useCanvas('canvas') @@ -100,12 +100,10 @@ export default function Roof2() { selectable: true, fontSize: fontSize, }, - canvas, ) - canvas?.add(polygon) - polygon.fillCell({ width: 50, height: 30, padding: 10 }) + // polygon.fillCell({ width: 50, height: 30, padding: 10 }) } } @@ -187,14 +185,14 @@ export default function Roof2() { ] const eightPoint = [ - { x: 240, y: 130 }, - { x: 240, y: 630 }, - { x: 640, y: 630 }, - { x: 640, y: 480 }, - { x: 440, y: 480 }, - { x: 440, y: 280 }, - { x: 740, y: 280 }, - { x: 740, y: 130 }, + { x: 240.1111, y: 130.1111 }, + { x: 240.1111, y: 630.1111 }, + { x: 640.1111, y: 630.1111 }, + { x: 640.1111, y: 480.1111 }, + { x: 440.1111, y: 480.1111 }, + { x: 440.1111, y: 280.1111 }, + { x: 740.1111, y: 280.1111 }, + { x: 740.1111, y: 130.1111 }, ] const eightPoint2 = [ @@ -209,7 +207,7 @@ export default function Roof2() { ] if (canvas) { - const polygon = new QPolygon(type1, { + const polygon = new QPolygon(eightPoint, { fill: 'transparent', stroke: 'black', strokeWidth: 1, @@ -220,8 +218,6 @@ export default function Roof2() { canvas?.add(polygon) - console.log(polygon) - handleOuterlinesTest2(polygon) // const lines = togglePolygonLine(polygon) @@ -242,27 +238,19 @@ export default function Roof2() { const makeQLine = () => { if (canvas) { - const line = new QLine( - [50, 250, 900, 250], - { - stroke: 'black', - strokeWidth: 5, - fontSize: fontSize, - selectable: true, - }, - 50, - ) + const line = new QLine([50, 250, 900, 250], { + stroke: 'black', + strokeWidth: 5, + fontSize: fontSize, + selectable: true, + }) - const line2 = new QLine( - [450, 450, 821, 78], - { - stroke: 'black', - strokeWidth: 5, - fontSize: fontSize, - selectable: true, - }, - 50, - ) + const line2 = new QLine([450, 450, 821, 78], { + stroke: 'black', + strokeWidth: 5, + fontSize: fontSize, + selectable: true, + }) canvas?.add(line) canvas?.add(line2) @@ -270,8 +258,6 @@ export default function Roof2() { const interSectionPoint = calculateIntersection2(line, line2) if (interSectionPoint) { - console.log(interSectionPoint) - const circle = new fabric.Circle({ radius: 5, fill: 'red', diff --git a/src/components/fabric/QLine.js b/src/components/fabric/QLine.js index a6916774..2d1e87df 100644 --- a/src/components/fabric/QLine.js +++ b/src/components/fabric/QLine.js @@ -1,127 +1,87 @@ import { fabric } from 'fabric' -import { getDirection, getDirectionByPoint } from '@/util/canvas-util' +import { v4 as uuidv4 } from 'uuid' +import { getDirection } from '@/util/canvas-util' -export class QLine extends fabric.Group { - line - text - fontSize - length = 0 - x1 - y1 - x2 - y2 - direction - idx - type = 'QLine' - parent - isAlreadyInterSection = false - - interSectionPoints = [] - lengthTxt = 0 - - initPoints - initOption - initLengthTxt - - constructor(points, option = { isActiveLengthText: true }, lengthTxt) { +export const QLine = fabric.util.createClass(fabric.Line, { + type: 'QLine', + text: null, + id: null, + line: null, + length: 0, + direction: null, + idx: 0, + initialize: function (points, options, canvas) { + this.callSuper('initialize', points, options) + if (options.id) { + this.id = options.id + } else { + this.id = uuidv4() + } + this.line = this // 소수점 전부 제거 points.forEach((point) => { point = Math.round(point) }) - const [x1, y1, x2, y2] = points - - if (!option.fontSize) { - throw new Error('Font size is required.') - } - - const line = new fabric.Line(points, { ...option, strokeWidth: 1 }) - - // 길이가 1이하인 선은 생성하지 않음 - if (Math.abs(x1 - x2) <= 1 && Math.abs(y1 - y2) <= 1) { - super([], {}) - this.initPoints = points - this.initOption = option - this.initLengthTxt = lengthTxt - } else { - 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 ?? getDirectionByPoint({ x: x1, y: y1 }, { x: x2, y: y2 }) - this.parent = option.parent - this.idx = option.idx - - if (lengthTxt > 0) { - this.lengthTxt = Number(lengthTxt) - } - - option.isActiveLengthText ?? this.init() - this.addControl() - } - } - - init() { - this.addLengthText(true) - } - - addControl() { - this.on('moving', () => { - this.addLengthText(false) - }) - - this.on('modified', (e) => { - this.addLengthText(false) - }) - - this.on('selected', () => { - console.log(this) - Object.keys(this.controls).forEach((controlKey) => { - if (controlKey !== 'ml' && controlKey !== 'mr') { - this.setControlVisible(controlKey, false) - } - }) - }) - } - - static fromObject(object, callback) { - const { points, initOption, initLengthTxt } = object - const instance = new QLine(points, initOption, initLengthTxt) - callback && callback(instance) - return instance - } - - 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(), { - left: (this.x1 + this.x2) / 2, - top: (this.y1 + this.y2) / 2, - fontSize: this.fontSize, - }) - this.length = this.lengthTxt - this.text = text - this.addWithUpdate(text) - return - } - 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.direction = options.direction ?? getDirection({ x: this.x1, y: this.y1 }, { x: this.x2, y: this.y2 }) + + if (canvas) { + this.canvas = canvas + } + }, + + toObject: function (propertiesToInclude) { + return fabric.util.object.extend(this.callSuper('toObject', propertiesToInclude), { + type: this.type, + text: this.text, + }) + }, + init: function () { + this.addLengthText() + + this.on('moving', () => { + this.addLengthText() + }) + + this.on('modified', (e) => { + this.addLengthText() + }) + + this.on('removed', () => { + const thisText = this.canvas.getObjects().find((obj) => obj.name === 'lengthText' && obj.parentId === this.id) + if (thisText) { + this.canvas.remove(thisText) + } + this.text = null + }) + }, + + addLengthText() { + const thisText = this.canvas.getObjects().find((obj) => obj.name === 'lengthText' && obj.parentId === this.id) + + 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 + + if (thisText) { + thisText.set({ text: this.length.toFixed(0).toString(), left: (x1 + x2) / 2, top: (y1 + y2) / 2 }) + return + } + const dx = x2 - x1 const dy = y2 - y1 this.length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(0)) @@ -130,52 +90,27 @@ export class QLine extends fabric.Group { left: (x1 + x2) / 2, top: (y1 + y2) / 2, fontSize: this.fontSize, + selectable: false, + parentId: this.id, + name: 'lengthText', }) - this.text = text - this.addWithUpdate(text) - } + this.text = text + this.canvas.add(text) + }, setFontSize(fontSize) { this.fontSize = fontSize 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, - points: this.points, - }) - } - - _set(key, value) { + }, + _render: function (ctx) { + this.callSuper('_render', ctx) + this.init() + }, + _set: function (key, value) { this.callSuper('_set', key, value) - } -} + }, + + setCanvas(canvas) { + this.canvas = canvas + }, +}) diff --git a/src/components/fabric/QLine2.js b/src/components/fabric/QLine2.js new file mode 100644 index 00000000..9379ba26 --- /dev/null +++ b/src/components/fabric/QLine2.js @@ -0,0 +1,184 @@ +/** + * 문제 없는경우 제거 + */ +import { fabric } from 'fabric' +import { getDirection, getDirectionByPoint } from '@/util/canvas-util' + +export class QLine2 extends fabric.Group { + line + text + fontSize + length = 0 + x1 + y1 + x2 + y2 + direction + idx + type = 'QLine' + parent + isAlreadyInterSection = false + + interSectionPoints = [] + lengthTxt = 0 + + initPoints + initOption + initLengthTxt + + constructor(points, option = { isActiveLengthText: true }, lengthTxt) { + // 소수점 전부 제거 + points.forEach((point) => { + point = Math.round(point) + }) + + const [x1, y1, x2, y2] = points + + if (!option.fontSize) { + throw new Error('Font size is required.') + } + + const line = new fabric.Line(points, { ...option, strokeWidth: 1 }) + + // 길이가 1이하인 선은 생성하지 않음 + if (Math.abs(x1 - x2) <= 1 && Math.abs(y1 - y2) <= 1) { + super([], {}) + this.initPoints = points + this.initOption = option + this.initLengthTxt = lengthTxt + } else { + 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 ?? getDirectionByPoint({ x: x1, y: y1 }, { x: x2, y: y2 }) + this.parent = option.parent + this.idx = option.idx + + if (lengthTxt > 0) { + this.lengthTxt = Number(lengthTxt) + } + + option.isActiveLengthText ?? this.init() + this.addControl() + } + } + + init() { + this.addLengthText(true) + } + + addControl() { + this.on('moving', () => { + this.addLengthText(false) + }) + + this.on('modified', (e) => { + this.addLengthText(false) + }) + + this.on('selected', () => { + console.log(this) + Object.keys(this.controls).forEach((controlKey) => { + if (controlKey !== 'ml' && controlKey !== 'mr') { + this.setControlVisible(controlKey, false) + } + }) + }) + } + + static fromObject(object, callback) { + const { points, initOption, initLengthTxt } = object + const instance = new QLine(points, initOption, initLengthTxt) + callback && callback(instance) + return instance + } + + 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(), { + left: (this.x1 + this.x2) / 2, + top: (this.y1 + this.y2) / 2, + fontSize: this.fontSize, + }) + this.length = this.lengthTxt + this.text = text + this.addWithUpdate(text) + return + } + + 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)) + + const text = new fabric.Textbox(this.length.toFixed(0).toString(), { + left: (x1 + x2) / 2, + top: (y1 + y2) / 2, + fontSize: this.fontSize, + }) + this.text = text + this.addWithUpdate(text) + } + + setFontSize(fontSize) { + this.fontSize = fontSize + 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, + points: this.points, + }) + } + + _set(key, value) { + this.callSuper('_set', key, value) + } +} diff --git a/src/components/fabric/QLine3.js b/src/components/fabric/QLine3.js deleted file mode 100644 index 9002d9a7..00000000 --- a/src/components/fabric/QLine3.js +++ /dev/null @@ -1,91 +0,0 @@ -import { fabric } from 'fabric' - -export const QLine2 = fabric.util.createClass(fabric.Line, { - type: 'QLine2', - text: null, - canvas: null, - initialize: function (points, options, canvas) { - if (canvas) { - this.canvas = canvas - } - this.callSuper('initialize', points, options) - }, - - toObject: function (propertiesToInclude) { - return fabric.util.object.extend(this.callSuper('toObject', propertiesToInclude), { - type: this.type, - x1: this.x1, - y1: this.y1, - x2: this.x2, - y2: this.y2, - }) - }, - init: function () { - this.addLengthText(true) - - this.on('moving', () => { - this.addLengthText(false) - }) - - this.on('modified', (e) => { - this.addLengthText(false) - }) - - this.on('removed', () => { - if (this.text) { - this.canvas.remove(this.text) - } - - this.text = null - }) - }, - - addLengthText(isFirst) { - if (this.text) { - this.canvas.remove(this.text) - this.text = null - } - - 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.text = text - this.canvas.add(text) - return - } - 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)) - - const text = new fabric.Textbox(this.length.toFixed(0).toString(), { - left: (x1 + x2) / 2, - top: (y1 + y2) / 2, - fontSize: this.fontSize, - selectable: false, - }) - text.set({ name: 'lengthText' }) - this.text = text - this.canvas.add(text) - }, - setFontSize(fontSize) { - this.fontSize = fontSize - this.text.set({ fontSize }) - }, - _render: function (ctx) { - this.callSuper('_render', ctx) - this.init() - }, - _set: function (key, value) { - this.callSuper('_set', key, value) - }, -}) diff --git a/src/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js index b10c8465..699c7182 100644 --- a/src/components/fabric/QPolygon.js +++ b/src/components/fabric/QPolygon.js @@ -1,135 +1,136 @@ import { fabric } from 'fabric' -import { - calculateIntersection, - calculateIntersection2, - distanceBetweenPoints, - findClosestLineToPoint, - findTopTwoIndexesByDistance, - getDegreeByChon, - getDirectionByPoint, - getRoofHeight, - getRoofHypotenuse, - sortedPoints, -} from '@/util/canvas-util' +import { v4 as uuidv4 } from 'uuid' import { QLine } from '@/components/fabric/QLine' +import { distanceBetweenPoints, findTopTwoIndexesByDistance, getDirectionByPoint, sortedPoints } from '@/util/canvas-util' import { drawHelpLineInHexagon } from '@/util/qpolygon-utils' -export default class QPolygon extends fabric.Group { - type = 'QPolygon' - polygon - points - texts = [] - lines = [] - canvas - fontSize - qCells = [] - name - shape = 0 // 점 6개일때의 shape 모양 - helpPoints = [] - helpLines = [] - - wall - - initPoints - initOption - - constructor(points, options, canvas) { - /*if (points.length !== 4 && points.length !== 6) { - throw new Error('Points must be 4 or 6.') - }*/ - if (!options.fontSize) { - throw new Error('Font size is required.') - } - +export const QPolygon = fabric.util.createClass(fabric.Polygon, { + type: 'QPolygon', + lines: [], + texts: [], + id: null, + length: 0, + initialize: function (points, options, canvas) { // 소수점 전부 제거 points.forEach((point) => { point.x = Math.round(point.x) point.y = Math.round(point.y) }) + points = sortedPoints(points) + this.callSuper('initialize', points, options) + if (options.id) { + this.id = options.id + } else { + this.id = uuidv4() + } - const sortPoints = sortedPoints(points) - const polygon = new fabric.Polygon(sortPoints, options) - - super([polygon], { selectable: false }) - - this.fontSize = options.fontSize - this.points = sortPoints - this.polygon = polygon - this.name = options.name - - this.initPoints = points - this.initOption = options + if (canvas) { + this.canvas = canvas + } this.init() - this.addEvent() this.initLines() this.setShape() - } + }, + + setShape() { + let shape = 0 + if (this.lines.length !== 6) { + return + } + //외각선 기준 + const topIndex = findTopTwoIndexesByDistance(this.lines).sort((a, b) => a - b) //배열중에 큰 2값을 가져옴 TODO: 나중에는 인자로 받아서 다각으로 수정 해야됨 + + //일단 배열 6개 짜리 기준의 선 번호 + if (topIndex[0] === 4) { + if (topIndex[1] === 5) { + //1번 + shape = 1 + } + } else if (topIndex[0] === 1) { + //4번 + if (topIndex[1] === 2) { + shape = 4 + } + } else if (topIndex[0] === 0) { + if (topIndex[1] === 1) { + //2번 + shape = 2 + } else if (topIndex[1] === 5) { + //3번 + shape = 3 + } + } + + this.shape = shape + }, + + toObject: function (propertiesToInclude) { + return fabric.util.object.extend(this.callSuper('toObject', propertiesToInclude), { + type: this.type, + text: this.text, + }) + }, + init: function () { + this.addLengthText() + + this.on('moving', () => { + this.addLengthText() + }) + + this.on('modified', (e) => { + this.addLengthText() + }) + + this.on('selected', () => { + Object.keys(this.controls).forEach((controlKey) => { + if (controlKey !== 'ml' && controlKey !== 'mr') { + this.setControlVisible(controlKey, false) + } + }) + }) + + this.on('removed', () => { + const thisText = this.canvas.getObjects().filter((obj) => obj.name === 'lengthText' && obj.parentId === this.id) + thisText.forEach((text) => { + this.canvas.remove(text) + }) + this.texts = null + }) + }, initLines() { + this.lines = [] + 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], { stroke: this.stroke, strokeWidth: this.strokeWidth, fontSize: this.fontSize, - idx: i, direction: getDirectionByPoint(point, nextPoint), + idx: i, }) - this.lines.push(line) }) - } + }, - init() { - this.addLengthText() - } - - addEvent() { - this.on('scaling', (e) => { - this.updateLengthText() - }) - - this.on('selected', function () { - // 모든 컨트롤 떼기 - - Object.keys(this.controls).forEach((controlKey) => { - if (controlKey !== 'mtr') { - this.setControlVisible(controlKey, false) - } - }) - }) - } - - setWall(wall) { - this.wall = wall - } - - setFontSize(fontSize) { - this.fontSize = fontSize - this.texts.forEach((text) => { - text.set({ fontSize }) - }) - - this.getObjects().forEach((obj) => { - if (obj.type[0] === 'Q') { - obj.setFontSize(fontSize) - } - }) - - this.canvas.add() - } + // 보조선 그리기 + drawHelpLine(chon = 4) { + drawHelpLineInHexagon(this, chon) + }, addLengthText() { - if (this.texts.length > 0) { - this.texts.forEach((text) => { - this.canvas.remove(text) - }) - this.texts = [] - } - const points = this.points + const points = this.getCurrentPoints() points.forEach((start, i) => { + const thisText = this.canvas.getObjects().find((obj) => obj.name === 'lengthText' && obj.parentId === this.id && obj.idx === i) + + if (thisText) { + thisText.set({ left: (start.x + points[(i + 1) % points.length].x) / 2, top: (start.y + points[(i + 1) % points.length].y) / 2 }) + return + } + const end = points[(i + 1) % points.length] const dx = end.x - start.x const dy = end.y - start.y @@ -143,31 +144,29 @@ export default class QPolygon extends fabric.Group { top: midPoint.y, fontSize: this.fontSize, selectable: false, + parentId: this.id, + idx: i, + name: 'lengthText', }) this.texts.push(text) this.canvas.add(text) + this.canvas.renderAll() }) - - this.canvas.renderAll() - } - - updateLengthText() { - const points = this.getCurrentPoints() - - points.forEach((start, i) => { - const end = points[(i + 1) % points.length] - const dx = end.x - start.x - const dy = end.y - start.y - const length = Math.sqrt(dx * dx + dy * dy) - - // Update the text object with the new length - this.texts[i].set({ text: length.toFixed(0) }) - }) - - this.canvas.renderAll() - } - + }, + setFontSize(fontSize) { + this.fontSize = fontSize + this.text.set({ fontSize }) + }, + _render: function (ctx) { + this.callSuper('_render', ctx) + }, + _set: function (key, value) { + this.callSuper('_set', key, value) + }, + setCanvas(canvas) { + this.canvas = canvas + }, fillCell(cell = { width: 50, height: 100, padding: 10 }) { const points = this.getCurrentPoints() let bounds @@ -207,23 +206,7 @@ export default class QPolygon extends fabric.Group { } this.canvas.renderAll() - } - - /** - * this.lines의 direction이 top 인 line의 모든 합이 bottom 인 line의 모든 합과 같은지 확인 - * this.lines의 direction이 left 인 line의 모든 합이 right 인 line의 모든 합과 같은지 확인 - * return {boolean} - */ - isValid() { - const leftLinesLengthSum = this.lines.filter((line) => line.direction === 'left').reduce((sum, line) => sum + line.length, 0) - const rightLinesLengthSum = this.lines.filter((line) => line.direction === 'right').reduce((sum, line) => sum + line.length, 0) - - const topLinesLengthSum = this.lines.filter((line) => line.direction === 'top').reduce((sum, line) => sum + line.length, 0) - const bottomLinesLengthSum = this.lines.filter((line) => line.direction === 'bottom').reduce((sum, line) => sum + line.length, 0) - - return leftLinesLengthSum === rightLinesLengthSum && topLinesLengthSum === bottomLinesLengthSum - } - + }, inPolygon(point) { const vertices = this.getCurrentPoints() let intersects = 0 @@ -253,7 +236,7 @@ export default class QPolygon extends fabric.Group { } return intersects % 2 === 1 - } + }, distanceFromEdge(point) { const vertices = this.getCurrentPoints() @@ -284,16 +267,7 @@ export default class QPolygon extends fabric.Group { } return minDistance - } - - setViewLengthText(boolean) { - this.texts.forEach((text) => { - text.visible = boolean - }) - - this.canvas.renderAll() - } - + }, getCurrentPoints() { const scaleX = this.scaleX const scaleY = this.scaleY @@ -313,625 +287,13 @@ export default class QPolygon extends fabric.Group { y: point.y * scaleY + movingY, } }) - } - - fillBackground(pattern) { - this.polygon.set({ fill: pattern }) - this.canvas.requestRenderAll() - } - - // 보조선 그리기 - drawHelpLine(chon = 4) { - drawHelpLineInHexagon(this, chon) - /*if (!this.isValid()) { - return - }*/ - - /*if (this.lines.length === 4) { - this.drawHelpLineInRect(chon) - } else if (this.lines.length === 6 || this.lines.length === 8) { - // TODO : 6각형 - drawHelpLineInHexagon2(this, chon) - } /!* else if (this.lines.length === 8) { - // TODO : 8각형 - this.drawHelpLineInOctagon(chon) - }*!/*/ - } - - /** - * 현재 점 6개만 가능 - */ - setShape() { - let shape = 0 - if (this.lines.length !== 6) { - return - } - //외각선 기준 - const topIndex = findTopTwoIndexesByDistance(this.lines).sort((a, b) => a - b) //배열중에 큰 2값을 가져옴 TODO: 나중에는 인자로 받아서 다각으로 수정 해야됨 - - //일단 배열 6개 짜리 기준의 선 번호 - if (topIndex[0] === 4) { - if (topIndex[1] === 5) { - //1번 - shape = 1 - } - } else if (topIndex[0] === 1) { - //4번 - if (topIndex[1] === 2) { - shape = 4 - } - } else if (topIndex[0] === 0) { - if (topIndex[1] === 1) { - //2번 - shape = 2 - } else if (topIndex[1] === 5) { - //3번 - shape = 3 - } - } - - this.shape = shape - } - - /** - * 현재 점 6개만 가능 - * @returns {number} - */ - getShape() { - return this.shape - } - - drawHelpLineInRect(chon) { - let type = 1 - let smallestLength = Infinity - let maxLength = 0 - - this.lines.forEach((line) => { - if (line.length < smallestLength) { - smallestLength = line.length - } - if (line.length > maxLength) { - maxLength = line.length - } + }, + setWall: function (wall) { + this.wall = wall + }, + setViewLengthText(isView) { + this.texts.forEach((text) => { + text.set({ visible: isView }) }) - - // QPolygon 객체의 모든 선들을 가져옵니다. - const lines = [...this.lines] - - // 이 선들을 길이에 따라 정렬합니다. - lines.sort((a, b) => a.length - b.length) - - // 정렬된 배열에서 가장 작은 두 선을 선택합니다. - let smallestLines - - if (smallestLength === maxLength) { - // 정사각형인 경우 0, 2번째 라인이 가장 짧은 라인 - - smallestLines = [lines[0], lines[2]] - } else { - smallestLines = lines.slice(0, 2) - } - - let needPlusLine - let needMinusLine - - const direction = smallestLines[0].direction - - if (direction === 'top' || direction === 'bottom') { - needPlusLine = smallestLines[0].x1 < smallestLines[1].x1 ? smallestLines[0] : smallestLines[1] - needMinusLine = needPlusLine === smallestLines[0] ? smallestLines[1] : smallestLines[0] - - type = 1 // 가로가 긴 사각형 - } - - if (direction === 'left' || direction === 'right') { - needPlusLine = smallestLines[0].y1 < smallestLines[1].y1 ? smallestLines[0] : smallestLines[1] - needMinusLine = needPlusLine === smallestLines[0] ? smallestLines[1] : smallestLines[0] - - type = 2 // 세로가 긴 사각형 - } - - let point1 - let point2 - - if (type === 1) { - point1 = { - x: needPlusLine.x1 + smallestLength / 2, - y: needPlusLine.y1 > needPlusLine.y2 ? needPlusLine.y1 - smallestLength / 2 : needPlusLine.y2 - smallestLength / 2, - } - - point2 = { - x: needMinusLine.x1 - smallestLength / 2, - y: needMinusLine.y1 > needMinusLine.y2 ? needMinusLine.y1 - smallestLength / 2 : needMinusLine.y2 - smallestLength / 2, - } - } else if (type === 2) { - point1 = { - x: needPlusLine.x1 > needPlusLine.x2 ? needPlusLine.x1 - smallestLength / 2 : needPlusLine.x2 - smallestLength / 2, - y: needPlusLine.y1 + smallestLength / 2, - } - - point2 = { - x: needMinusLine.x1 > needMinusLine.x2 ? needMinusLine.x1 - smallestLength / 2 : needMinusLine.x2 - smallestLength / 2, - y: needMinusLine.y1 - smallestLength / 2, - } - } - - // 빗변1 - const realLine1 = new QLine( - [needPlusLine.x1, needPlusLine.y1, point1.x, point1.y], - { fontSize: this.fontSize, stroke: 'black', strokeWidth: 1 }, - getRoofHypotenuse(smallestLength / 2), - ) - - // 빗변2 - const realLine2 = new QLine( - [needPlusLine.x2, needPlusLine.y2, point1.x, point1.y], - { fontSize: this.fontSize, stroke: 'black', strokeWidth: 1 }, - getRoofHypotenuse(smallestLength / 2), - ) - - // 빗변3 - const realLine3 = new QLine( - [needMinusLine.x1, needMinusLine.y1, point2.x, point2.y], - { fontSize: this.fontSize, stroke: 'black', strokeWidth: 1 }, - getRoofHypotenuse(smallestLength / 2), - ) - - // 빗변4 - const realLine4 = new QLine( - [needMinusLine.x2, needMinusLine.y2, point2.x, point2.y], - { fontSize: this.fontSize, stroke: 'black', strokeWidth: 1 }, - getRoofHypotenuse(smallestLength / 2), - ) - - let centerPoint1 - let centerPoint2 - - if (type === 1) { - centerPoint1 = { x: point1.x - smallestLength / 2, y: point1.y } - centerPoint2 = { x: point2.x + smallestLength / 2, y: point2.y } - } else if (type === 2) { - centerPoint1 = { x: point1.x, y: point1.y - smallestLength / 2 } - centerPoint2 = { x: point2.x, y: point2.y + smallestLength / 2 } - } - - // 옆으로 누워있는 지붕의 높이 - const realLine5 = new QLine( - [point1.x, point1.y, centerPoint1.x, centerPoint1.y], - { - fontSize: this.fontSize, - stroke: 'black', - strokeWidth: 1, - strokeDashArray: [5, 5], - }, - getRoofHeight(smallestLength / 2, getDegreeByChon(chon)), - ) - - // 옆으로 누워있는 지붕의 높이 - const realLine6 = new QLine( - [point2.x, point2.y, centerPoint2.x, centerPoint2.y], - { - fontSize: this.fontSize, - stroke: 'black', - strokeWidth: 1, - strokeDashArray: [5, 5], - }, - getRoofHeight(smallestLength / 2, getDegreeByChon(chon)), - ) - - // 용마루 - const ridge = new QLine([point1.x, point1.y, point2.x, point2.y], { - fontSize: this.fontSize, - stroke: 'black', - strokeWidth: 1, - }) - - 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) { - const oneSideLines = [...this.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') - - const horizontalMaxLength = horizontalLines.reduce((max, obj) => Math.max(max, obj.length), 0) - const verticalMaxLength = verticalLines.reduce((max, obj) => Math.max(max, obj.length), 0) - // 모든 가로선의 중심선을 긋는다. - horizontalLines.forEach((line, index) => { - const nextLine = horizontalLines[(index + 1) % horizontalLines.length] - - const startCenterX = Math.max(line.x1, nextLine.x1) - const startCenterY = (line.y1 + nextLine.y1) / 2 - - let endCenterX = line.length >= nextLine.length ? startCenterX + line.length : startCenterX + nextLine.length - const endCenterY = startCenterY - - if (endCenterX > Math.max(line.x2, nextLine.x2)) { - endCenterX = Math.max(line.x2, nextLine.x2) - } - - const centerLine = new QLine([startCenterX, startCenterY, endCenterX, endCenterY], { - fontSize: this.fontSize, - stroke: 'red', - strokeWidth: 1, - direction: 'horizontal', - }) - - // this.addWithUpdate(centerLine) - - 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 - - if (endCenterY > Math.max(line.y2, nextLine.y2)) { - endCenterY = Math.max(line.y2, nextLine.y2) - } - - const centerLine = new QLine([startCenterX, startCenterY, endCenterX, endCenterY], { - fontSize: this.fontSize, - stroke: 'blue', - strokeWidth: 1, - direction: 'vertical', - }) - // this.addWithUpdate(centerLine) - - centerLines.push(centerLine) - }) - - const maxLength = horizontalMaxLength < verticalMaxLength ? horizontalMaxLength : verticalMaxLength - - 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 - - // x1, y1을 기준으로 x2, y2와의 거리를 유지한 새로운 직선 생성 - const angle = Math.atan2(y2 - y1, x2 - x1) - - newX2 = Math.floor(x1 + (maxLength / 2 + 50) * Math.cos(angle)) - newY2 = Math.floor(y1 + (maxLength / 2 + 50) * Math.sin(angle)) - - const line = new QLine([x1, y1, newX2, newY2], { - fontSize: this.fontSize, - stroke: 'green', - idx: index, - }) - this.addWithUpdate(line) - helpLines.push(line) - this.canvas.renderAll() - }) - - helpLines.forEach((line, index) => { - if (line.isAlreadyInterSection) { - return - } - const nextLine = helpLines[(index + 1 + helpLines.length) % helpLines.length] - this.canvas.renderAll() - - let intersectionPoint = calculateIntersection(line, nextLine) - if (!intersectionPoint) { - return - } - - line.set({ isAlreadyInterSection: true }) - nextLine.set({ isAlreadyInterSection: true }) - - const helpLine1 = new QLine([nextLine.x1, nextLine.y1, intersectionPoint.x, intersectionPoint.y], { - fontSize: this.fontSize, - stroke: 'skyblue', - }) - - const helpLine2 = new QLine([line.x1, line.y1, intersectionPoint.x, intersectionPoint.y], { - fontSize: this.fontSize, - stroke: 'skyblue', - }) - - ridgeStartPoints.push(intersectionPoint) - this.addWithUpdate(helpLine1) - this.addWithUpdate(helpLine2) - this.removeWithUpdate(nextLine) - this.removeWithUpdate(line) - this.canvas.renderAll() - }) - - // 안만나는 선들 - const notInterSectionLines = helpLines.filter((line) => !line.isAlreadyInterSection) - const ridgeEndPoints = [] - const interSectionPoints = [] - - notInterSectionLines.forEach((line, index) => { - line.line.set({ strokeWidth: (index + 1) * 5 }) - - centerLines.forEach((centerLine) => { - const interSectionPoint = calculateIntersection2(line, centerLine) - - if (!this.inPolygon(interSectionPoint) || !interSectionPoint) { - return - } - line.interSectionPoints.push(interSectionPoint) - interSectionPoints.push(interSectionPoint) - }) - }) - - ridgeStartPoints.forEach((point, index) => { - let arrivalPoint - let distance = Infinity - let startPoint - interSectionPoints.forEach((interSectionPoint) => { - if (Math.abs(point.x - interSectionPoint.x) < 3 || Math.abs(point.y - interSectionPoint.y) < 3) { - 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: this.fontSize, - }) - - const helpLine = new QLine([line.x1, line.y1, arrivalPoint.x, arrivalPoint.y], { - stroke: 'red', - fontSize: this.fontSize, - }) - - ridgeEndPoints.push(arrivalPoint) - this.addWithUpdate(ridge) - this.addWithUpdate(helpLine) - this.removeWithUpdate(line) - this.canvas.renderAll() - } - }) - - ridgeEndPoints.forEach((point, index) => { - const currentRidgeEndPoint = ridgeEndPoints[index] - const nextRidgeEndPoint = ridgeEndPoints[(index + 1) % ridgeEndPoints.length] - const ridgeConnectLine = new QLine([currentRidgeEndPoint.x, currentRidgeEndPoint.y, nextRidgeEndPoint.x, nextRidgeEndPoint.y], { - fontSize: this.fontSize, - stroke: 'green', - }) - - this.addWithUpdate(ridgeConnectLine) - this.canvas.renderAll() - }) - } - drawHelpLineInHexagon(chon) { - 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 - 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 - - // 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) - - 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.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() - }) - } - - drawHelpLineInOctagon(chon) {} - - getObject() { - return this - } - sou - - toObject(propertiesToInclude) { - return fabric.util.object.extend(this.callSuper('toObject'), { - points: this.points, - fontSize: this.fontSize, - name: this.name, - shape: this.shape, - texts: this.texts, - lines: this.lines, - wall: this.wall, - initPoints: this.initPoints, - initOption: this.initOption, - objects: this.getObjects().map((obj) => obj.toObject(propertiesToInclude)), - pattern: this.pattern, - }) - } - - _set(key, value) { - this.callSuper('_set', key, value) - } -} + }, +}) diff --git a/src/components/fabric/QPolygon3.js b/src/components/fabric/QPolygon3.js new file mode 100644 index 00000000..73f1b700 --- /dev/null +++ b/src/components/fabric/QPolygon3.js @@ -0,0 +1,941 @@ +/** + * 문제 없는경우 제거 + */ + +import { fabric } from 'fabric' +import { + calculateIntersection, + calculateIntersection2, + distanceBetweenPoints, + findClosestLineToPoint, + findTopTwoIndexesByDistance, + getDegreeByChon, + getDirectionByPoint, + getRoofHeight, + getRoofHypotenuse, + sortedPoints, +} from '@/util/canvas-util' +import { QLine } from '@/components/fabric/QLine' +import { drawHelpLineInHexagon } from '@/util/qpolygon-utils' + +export default class QPolygon extends fabric.Group { + type = 'QPolygon' + polygon + points + texts = [] + lines = [] + canvas + fontSize + qCells = [] + name + shape = 0 // 점 6개일때의 shape 모양 + helpPoints = [] + helpLines = [] + + wall + + initPoints + initOption + + constructor(points, options, canvas) { + /*if (points.length !== 4 && points.length !== 6) { + throw new Error('Points must be 4 or 6.') + }*/ + if (!options.fontSize) { + throw new Error('Font size is required.') + } + + // 소수점 전부 제거 + points.forEach((point) => { + point.x = Math.round(point.x) + point.y = Math.round(point.y) + }) + + const sortPoints = sortedPoints(points) + const polygon = new fabric.Polygon(sortPoints, options) + + super([polygon], {}) + + this.fontSize = options.fontSize + this.points = sortPoints + this.polygon = polygon + this.name = options.name + + this.initPoints = points + this.initOption = options + + this.init() + this.addEvent() + this.initLines() + this.setShape() + } + + 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], { + stroke: this.stroke, + strokeWidth: this.strokeWidth, + fontSize: this.fontSize, + idx: i, + direction: getDirectionByPoint(point, nextPoint), + }) + + this.lines.push(line) + }) + } + + init() { + this.addLengthText() + } + + addEvent() { + this.on('scaling', (e) => { + this.updateLengthText() + }) + + this.on('selected', function () { + // 모든 컨트롤 떼기 + + Object.keys(this.controls).forEach((controlKey) => { + if (controlKey !== 'mtr') { + this.setControlVisible(controlKey, false) + } + }) + }) + } + + setWall(wall) { + this.wall = wall + } + + setFontSize(fontSize) { + this.fontSize = fontSize + this.texts.forEach((text) => { + text.set({ fontSize }) + }) + + this.getObjects().forEach((obj) => { + if (obj.type[0] === 'Q') { + obj.setFontSize(fontSize) + } + }) + + this.canvas.add() + } + + addLengthText() { + if (this.texts.length > 0) { + this.texts.forEach((text) => { + this.canvas.remove(text) + }) + this.texts = [] + } + const points = this.points + + points.forEach((start, i) => { + const end = points[(i + 1) % points.length] + const dx = end.x - start.x + const dy = end.y - start.y + const length = Math.sqrt(dx * dx + dy * dy) + + const midPoint = new fabric.Point((start.x + end.x) / 2, (start.y + end.y) / 2) + + // Create new text object if it doesn't exist + const text = new fabric.Text(length.toFixed(0), { + left: midPoint.x, + top: midPoint.y, + fontSize: this.fontSize, + selectable: false, + }) + + this.texts.push(text) + this.canvas.add(text) + }) + + this.canvas.renderAll() + } + + updateLengthText() { + const points = this.getCurrentPoints() + + points.forEach((start, i) => { + const end = points[(i + 1) % points.length] + const dx = end.x - start.x + const dy = end.y - start.y + const length = Math.sqrt(dx * dx + dy * dy) + + // Update the text object with the new length + this.texts[i].set({ text: length.toFixed(0) }) + }) + + this.canvas.renderAll() + } + + fillCell(cell = { width: 50, height: 100, padding: 10 }) { + const points = this.getCurrentPoints() + let bounds + + try { + bounds = fabric.util.makeBoundingBoxFromPoints(points) + } catch (error) { + alert('다각형의 꼭지점이 4개 이상이어야 합니다.') + return + } + + for (let x = bounds.left; x < bounds.left + bounds.width; x += cell.width + cell.padding) { + for (let y = bounds.top; y < bounds.top + bounds.height; y += cell.height + cell.padding) { + const rect = new fabric.Rect({ + left: x, + top: y, + width: cell.width, + height: cell.height, + fill: 'transparent', + stroke: 'black', + selectable: false, + }) + + const rectPoints = [ + new fabric.Point(rect.left, rect.top), + new fabric.Point(rect.left + rect.width, rect.top), + new fabric.Point(rect.left, rect.top + rect.height), + new fabric.Point(rect.left + rect.width, rect.top + rect.height), + ] + + const isInside = rectPoints.every((rectPoint) => this.inPolygon(rectPoint) && this.distanceFromEdge(rectPoint) >= cell.padding) + + if (isInside) { + this.canvas.add(rect) + } + } + } + + this.canvas.renderAll() + } + + /** + * this.lines의 direction이 top 인 line의 모든 합이 bottom 인 line의 모든 합과 같은지 확인 + * this.lines의 direction이 left 인 line의 모든 합이 right 인 line의 모든 합과 같은지 확인 + * return {boolean} + */ + isValid() { + const leftLinesLengthSum = this.lines.filter((line) => line.direction === 'left').reduce((sum, line) => sum + line.length, 0) + const rightLinesLengthSum = this.lines.filter((line) => line.direction === 'right').reduce((sum, line) => sum + line.length, 0) + + const topLinesLengthSum = this.lines.filter((line) => line.direction === 'top').reduce((sum, line) => sum + line.length, 0) + const bottomLinesLengthSum = this.lines.filter((line) => line.direction === 'bottom').reduce((sum, line) => sum + line.length, 0) + + return leftLinesLengthSum === rightLinesLengthSum && topLinesLengthSum === bottomLinesLengthSum + } + + inPolygon(point) { + const vertices = this.getCurrentPoints() + let intersects = 0 + + for (let i = 0; i < vertices.length; i++) { + let vertex1 = vertices[i] + let vertex2 = vertices[(i + 1) % vertices.length] + + if (vertex1.y > vertex2.y) { + let tmp = vertex1 + vertex1 = vertex2 + vertex2 = tmp + } + + if (point.y === vertex1.y || point.y === vertex2.y) { + point.y += 0.01 + } + + if (point.y <= vertex1.y || point.y > vertex2.y) { + continue + } + + let xInt = ((point.y - vertex1.y) * (vertex2.x - vertex1.x)) / (vertex2.y - vertex1.y) + vertex1.x + if (xInt < point.x) { + intersects++ + } + } + + return intersects % 2 === 1 + } + + distanceFromEdge(point) { + const vertices = this.getCurrentPoints() + let minDistance = Infinity + + for (let i = 0; i < vertices.length; i++) { + let vertex1 = vertices[i] + let vertex2 = vertices[(i + 1) % vertices.length] + + const dx = vertex2.x - vertex1.x + const dy = vertex2.y - vertex1.y + + const t = ((point.x - vertex1.x) * dx + (point.y - vertex1.y) * dy) / (dx * dx + dy * dy) + + let closestPoint + if (t < 0) { + closestPoint = vertex1 + } else if (t > 1) { + closestPoint = vertex2 + } else { + closestPoint = new fabric.Point(vertex1.x + t * dx, vertex1.y + t * dy) + } + + const distance = distanceBetweenPoints(point, closestPoint) + if (distance < minDistance) { + minDistance = distance + } + } + + return minDistance + } + + setViewLengthText(boolean) { + this.texts.forEach((text) => { + text.visible = boolean + }) + + this.canvas.renderAll() + } + + getCurrentPoints() { + const scaleX = this.scaleX + const scaleY = this.scaleY + + const left = this.left + const top = this.top + + // 시작점 + const point = this.points[0] + + const movingX = left - point.x * scaleX + const movingY = top - point.y * scaleY + + return this.points.map((point) => { + return { + x: point.x * scaleX + movingX, + y: point.y * scaleY + movingY, + } + }) + } + + fillBackground(pattern) { + this.polygon.set({ fill: pattern }) + this.canvas.requestRenderAll() + } + + // 보조선 그리기 + drawHelpLine(chon = 4) { + drawHelpLineInHexagon(this, chon) + /*if (!this.isValid()) { + return + }*/ + + /*if (this.lines.length === 4) { + this.drawHelpLineInRect(chon) + } else if (this.lines.length === 6 || this.lines.length === 8) { + // TODO : 6각형 + drawHelpLineInHexagon2(this, chon) + } /!* else if (this.lines.length === 8) { + // TODO : 8각형 + this.drawHelpLineInOctagon(chon) + }*!/*/ + } + + /** + * 현재 점 6개만 가능 + */ + setShape() { + let shape = 0 + if (this.lines.length !== 6) { + return + } + //외각선 기준 + const topIndex = findTopTwoIndexesByDistance(this.lines).sort((a, b) => a - b) //배열중에 큰 2값을 가져옴 TODO: 나중에는 인자로 받아서 다각으로 수정 해야됨 + + //일단 배열 6개 짜리 기준의 선 번호 + if (topIndex[0] === 4) { + if (topIndex[1] === 5) { + //1번 + shape = 1 + } + } else if (topIndex[0] === 1) { + //4번 + if (topIndex[1] === 2) { + shape = 4 + } + } else if (topIndex[0] === 0) { + if (topIndex[1] === 1) { + //2번 + shape = 2 + } else if (topIndex[1] === 5) { + //3번 + shape = 3 + } + } + + this.shape = shape + } + + /** + * 현재 점 6개만 가능 + * @returns {number} + */ + getShape() { + return this.shape + } + + drawHelpLineInRect(chon) { + let type = 1 + let smallestLength = Infinity + let maxLength = 0 + + this.lines.forEach((line) => { + if (line.length < smallestLength) { + smallestLength = line.length + } + if (line.length > maxLength) { + maxLength = line.length + } + }) + + // QPolygon 객체의 모든 선들을 가져옵니다. + const lines = [...this.lines] + + // 이 선들을 길이에 따라 정렬합니다. + lines.sort((a, b) => a.length - b.length) + + // 정렬된 배열에서 가장 작은 두 선을 선택합니다. + let smallestLines + + if (smallestLength === maxLength) { + // 정사각형인 경우 0, 2번째 라인이 가장 짧은 라인 + + smallestLines = [lines[0], lines[2]] + } else { + smallestLines = lines.slice(0, 2) + } + + let needPlusLine + let needMinusLine + + const direction = smallestLines[0].direction + + if (direction === 'top' || direction === 'bottom') { + needPlusLine = smallestLines[0].x1 < smallestLines[1].x1 ? smallestLines[0] : smallestLines[1] + needMinusLine = needPlusLine === smallestLines[0] ? smallestLines[1] : smallestLines[0] + + type = 1 // 가로가 긴 사각형 + } + + if (direction === 'left' || direction === 'right') { + needPlusLine = smallestLines[0].y1 < smallestLines[1].y1 ? smallestLines[0] : smallestLines[1] + needMinusLine = needPlusLine === smallestLines[0] ? smallestLines[1] : smallestLines[0] + + type = 2 // 세로가 긴 사각형 + } + + let point1 + let point2 + + if (type === 1) { + point1 = { + x: needPlusLine.x1 + smallestLength / 2, + y: needPlusLine.y1 > needPlusLine.y2 ? needPlusLine.y1 - smallestLength / 2 : needPlusLine.y2 - smallestLength / 2, + } + + point2 = { + x: needMinusLine.x1 - smallestLength / 2, + y: needMinusLine.y1 > needMinusLine.y2 ? needMinusLine.y1 - smallestLength / 2 : needMinusLine.y2 - smallestLength / 2, + } + } else if (type === 2) { + point1 = { + x: needPlusLine.x1 > needPlusLine.x2 ? needPlusLine.x1 - smallestLength / 2 : needPlusLine.x2 - smallestLength / 2, + y: needPlusLine.y1 + smallestLength / 2, + } + + point2 = { + x: needMinusLine.x1 > needMinusLine.x2 ? needMinusLine.x1 - smallestLength / 2 : needMinusLine.x2 - smallestLength / 2, + y: needMinusLine.y1 - smallestLength / 2, + } + } + + // 빗변1 + const realLine1 = new QLine( + [needPlusLine.x1, needPlusLine.y1, point1.x, point1.y], + { fontSize: this.fontSize, stroke: 'black', strokeWidth: 1 }, + getRoofHypotenuse(smallestLength / 2), + ) + + // 빗변2 + const realLine2 = new QLine( + [needPlusLine.x2, needPlusLine.y2, point1.x, point1.y], + { fontSize: this.fontSize, stroke: 'black', strokeWidth: 1 }, + getRoofHypotenuse(smallestLength / 2), + ) + + // 빗변3 + const realLine3 = new QLine( + [needMinusLine.x1, needMinusLine.y1, point2.x, point2.y], + { fontSize: this.fontSize, stroke: 'black', strokeWidth: 1 }, + getRoofHypotenuse(smallestLength / 2), + ) + + // 빗변4 + const realLine4 = new QLine( + [needMinusLine.x2, needMinusLine.y2, point2.x, point2.y], + { fontSize: this.fontSize, stroke: 'black', strokeWidth: 1 }, + getRoofHypotenuse(smallestLength / 2), + ) + + let centerPoint1 + let centerPoint2 + + if (type === 1) { + centerPoint1 = { x: point1.x - smallestLength / 2, y: point1.y } + centerPoint2 = { x: point2.x + smallestLength / 2, y: point2.y } + } else if (type === 2) { + centerPoint1 = { x: point1.x, y: point1.y - smallestLength / 2 } + centerPoint2 = { x: point2.x, y: point2.y + smallestLength / 2 } + } + + // 옆으로 누워있는 지붕의 높이 + const realLine5 = new QLine( + [point1.x, point1.y, centerPoint1.x, centerPoint1.y], + { + fontSize: this.fontSize, + stroke: 'black', + strokeWidth: 1, + strokeDashArray: [5, 5], + }, + getRoofHeight(smallestLength / 2, getDegreeByChon(chon)), + ) + + // 옆으로 누워있는 지붕의 높이 + const realLine6 = new QLine( + [point2.x, point2.y, centerPoint2.x, centerPoint2.y], + { + fontSize: this.fontSize, + stroke: 'black', + strokeWidth: 1, + strokeDashArray: [5, 5], + }, + getRoofHeight(smallestLength / 2, getDegreeByChon(chon)), + ) + + // 용마루 + const ridge = new QLine([point1.x, point1.y, point2.x, point2.y], { + fontSize: this.fontSize, + stroke: 'black', + strokeWidth: 1, + }) + + 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) { + const oneSideLines = [...this.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') + + const horizontalMaxLength = horizontalLines.reduce((max, obj) => Math.max(max, obj.length), 0) + const verticalMaxLength = verticalLines.reduce((max, obj) => Math.max(max, obj.length), 0) + // 모든 가로선의 중심선을 긋는다. + horizontalLines.forEach((line, index) => { + const nextLine = horizontalLines[(index + 1) % horizontalLines.length] + + const startCenterX = Math.max(line.x1, nextLine.x1) + const startCenterY = (line.y1 + nextLine.y1) / 2 + + let endCenterX = line.length >= nextLine.length ? startCenterX + line.length : startCenterX + nextLine.length + const endCenterY = startCenterY + + if (endCenterX > Math.max(line.x2, nextLine.x2)) { + endCenterX = Math.max(line.x2, nextLine.x2) + } + + const centerLine = new QLine([startCenterX, startCenterY, endCenterX, endCenterY], { + fontSize: this.fontSize, + stroke: 'red', + strokeWidth: 1, + direction: 'horizontal', + }) + + // this.addWithUpdate(centerLine) + + 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 + + if (endCenterY > Math.max(line.y2, nextLine.y2)) { + endCenterY = Math.max(line.y2, nextLine.y2) + } + + const centerLine = new QLine([startCenterX, startCenterY, endCenterX, endCenterY], { + fontSize: this.fontSize, + stroke: 'blue', + strokeWidth: 1, + direction: 'vertical', + }) + // this.addWithUpdate(centerLine) + + centerLines.push(centerLine) + }) + + const maxLength = horizontalMaxLength < verticalMaxLength ? horizontalMaxLength : verticalMaxLength + + 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 + + // x1, y1을 기준으로 x2, y2와의 거리를 유지한 새로운 직선 생성 + const angle = Math.atan2(y2 - y1, x2 - x1) + + newX2 = Math.floor(x1 + (maxLength / 2 + 50) * Math.cos(angle)) + newY2 = Math.floor(y1 + (maxLength / 2 + 50) * Math.sin(angle)) + + const line = new QLine([x1, y1, newX2, newY2], { + fontSize: this.fontSize, + stroke: 'green', + idx: index, + }) + this.addWithUpdate(line) + helpLines.push(line) + this.canvas.renderAll() + }) + + helpLines.forEach((line, index) => { + if (line.isAlreadyInterSection) { + return + } + const nextLine = helpLines[(index + 1 + helpLines.length) % helpLines.length] + this.canvas.renderAll() + + let intersectionPoint = calculateIntersection(line, nextLine) + if (!intersectionPoint) { + return + } + + line.set({ isAlreadyInterSection: true }) + nextLine.set({ isAlreadyInterSection: true }) + + const helpLine1 = new QLine([nextLine.x1, nextLine.y1, intersectionPoint.x, intersectionPoint.y], { + fontSize: this.fontSize, + stroke: 'skyblue', + }) + + const helpLine2 = new QLine([line.x1, line.y1, intersectionPoint.x, intersectionPoint.y], { + fontSize: this.fontSize, + stroke: 'skyblue', + }) + + ridgeStartPoints.push(intersectionPoint) + this.addWithUpdate(helpLine1) + this.addWithUpdate(helpLine2) + this.removeWithUpdate(nextLine) + this.removeWithUpdate(line) + this.canvas.renderAll() + }) + + // 안만나는 선들 + const notInterSectionLines = helpLines.filter((line) => !line.isAlreadyInterSection) + const ridgeEndPoints = [] + const interSectionPoints = [] + + notInterSectionLines.forEach((line, index) => { + line.line.set({ strokeWidth: (index + 1) * 5 }) + + centerLines.forEach((centerLine) => { + const interSectionPoint = calculateIntersection2(line, centerLine) + + if (!this.inPolygon(interSectionPoint) || !interSectionPoint) { + return + } + line.interSectionPoints.push(interSectionPoint) + interSectionPoints.push(interSectionPoint) + }) + }) + + ridgeStartPoints.forEach((point, index) => { + let arrivalPoint + let distance = Infinity + let startPoint + interSectionPoints.forEach((interSectionPoint) => { + if (Math.abs(point.x - interSectionPoint.x) < 3 || Math.abs(point.y - interSectionPoint.y) < 3) { + 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: this.fontSize, + }) + + const helpLine = new QLine([line.x1, line.y1, arrivalPoint.x, arrivalPoint.y], { + stroke: 'red', + fontSize: this.fontSize, + }) + + ridgeEndPoints.push(arrivalPoint) + this.addWithUpdate(ridge) + this.addWithUpdate(helpLine) + this.removeWithUpdate(line) + this.canvas.renderAll() + } + }) + + ridgeEndPoints.forEach((point, index) => { + const currentRidgeEndPoint = ridgeEndPoints[index] + const nextRidgeEndPoint = ridgeEndPoints[(index + 1) % ridgeEndPoints.length] + const ridgeConnectLine = new QLine([currentRidgeEndPoint.x, currentRidgeEndPoint.y, nextRidgeEndPoint.x, nextRidgeEndPoint.y], { + fontSize: this.fontSize, + stroke: 'green', + }) + + this.addWithUpdate(ridgeConnectLine) + this.canvas.renderAll() + }) + } + drawHelpLineInHexagon(chon) { + 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 + 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 + + // 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) + + 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.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() + }) + } + + drawHelpLineInOctagon(chon) {} + + getObject() { + return this + } + sou + + toObject(propertiesToInclude) { + return fabric.util.object.extend(this.callSuper('toObject'), { + points: this.points, + fontSize: this.fontSize, + name: this.name, + shape: this.shape, + texts: this.texts, + lines: this.lines, + wall: this.wall, + initPoints: this.initPoints, + initOption: this.initOption, + objects: this.getObjects().map((obj) => obj.toObject(propertiesToInclude)), + pattern: this.pattern, + }) + } + + _set(key, value) { + this.callSuper('_set', key, value) + } +} diff --git a/src/hooks/useCanvas.js b/src/hooks/useCanvas.js index 2ffdf9f9..f3b3901e 100644 --- a/src/hooks/useCanvas.js +++ b/src/hooks/useCanvas.js @@ -4,9 +4,9 @@ import { actionHandler, anchorWrapper, polygonPositionHandler } from '@/util/can import { useRecoilState } from 'recoil' import { canvasSizeState, fontSizeState } from '@/store/canvasAtom' -import QPolygon from '@/components/fabric/QPolygon' import { QLine } from '@/components/fabric/QLine' import QRect from '@/components/fabric/QRect' +import { QPolygon } from '@/components/fabric/QPolygon' export function useCanvas(id) { const [canvas, setCanvas] = useState() @@ -111,38 +111,30 @@ export function useCanvas(id) { fabric.Object.prototype.cornerStrokeColor = '#2BEBC8' fabric.Object.prototype.cornerSize = 6 fabric.QLine = QLine + fabric.QPolygon = QPolygon QPolygon.prototype.canvas = canvas QLine.prototype.canvas = canvas QRect.prototype.canvas = canvas - QLine.fromObject = function (object, callback) { - const { x1, x2, y1, y2 } = object - const instance = new QLine([x1, y1, x2, y2], object, canvas) - canvas?.add(instance) - } + 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.QLine.fromObject = fabric.Line.fromObject + fabric.Object._fromObject('QLine', options, _callback, 'points') + } // CustomLine.prototype.canvas = canvas // fabric.QLine = fabric.util.createClass(fabric.Group, {}) - fabric.QPolygon = fabric.util.createClass(fabric.Group, {}) + // Register the custom class with fabric - // 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)) - }) + fabric.Object._fromObject('QPolygon', object, callback, 'points') } } @@ -567,42 +559,19 @@ export function useCanvas(id) { const addCanvas = () => { // const canvasState = canvas - const objs = canvas + const objs = canvas?.toJSON(['selectable', 'name', 'parentId', 'id', 'length', 'idx', 'direction', 'lines', 'points']) - 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) { - // 각 객체가 로드될 때마다 호출됩니다. - // canvas?.add(object) - canvas?.renderAll() - }, - ) + // 역직렬화하여 캔버스에 객체를 다시 추가합니다. + canvas?.loadFromJSON(JSON.parse(str), function () { + // 모든 객체가 로드되고 캔버스에 추가된 후 호출됩니다. + 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) => { diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index 18a7a574..b899095d 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -1,12 +1,12 @@ 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 { canvasSizeState, fontSizeState, roofPolygonPatternArrayState, roofState, sortedPolygonArray, wallState } from '@/store/canvasAtom' import { QLine } from '@/components/fabric/QLine' import { fabric } from 'fabric' +import { QPolygon } from '@/components/fabric/QPolygon' export const Mode = { DRAW_LINE: 'drawLine', // 기준선 긋기모드 diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index d09b8f21..b3f9529b 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -1,5 +1,5 @@ import { fabric } from 'fabric' -import QPolygon from '@/components/fabric/QPolygon' +import QPolygon from '@/components/fabric/QPolygon3' import { QLine } from '@/components/fabric/QLine' import { calculateDistance, @@ -39,7 +39,6 @@ export const drawHelpLineInHexagon = (polygon, chon) => { let maxLength = 0 polygon.lines = polygon.lines.sort((a, b) => a.length - b.length) - polygon.wall.lines = getOneSideLines(polygon.wall) // 짧은 라인 순서대로 삼각 지붕을 그린다. @@ -59,7 +58,7 @@ export const drawHelpLineInHexagon = (polygon, chon) => { const angle1 = Math.atan2(wallLine.y1 - line.y1, wallLine.x1 - line.x1) const angle2 = Math.atan2(wallLine.y2 - line.y2, wallLine.x2 - line.x2) - const helpLineLength = Math.min(line.length) + const helpLineLength = line.length * 2 const firstX2 = Math.floor(line.x1 + helpLineLength * Math.cos(angle1)) const firstY2 = Math.floor(line.y1 + helpLineLength * Math.sin(angle1)) @@ -69,10 +68,12 @@ export const drawHelpLineInHexagon = (polygon, chon) => { const firstHelpLine = new QLine([line.x1, line.y1, firstX2, firstY2], { fontSize: polygon.fontSize, + stroke: 'skyblue', }) const secondHelpLine = new QLine([line.x2, line.y2, secondX2, secondY2], { fontSize: polygon.fontSize, + stroke: 'skyblue', }) const interSectionPoint = calculateIntersection2(firstHelpLine, secondHelpLine) @@ -262,8 +263,8 @@ export const drawCenterLines = (polygon) => { horizontalLines.forEach((line, index) => { const nextLine = horizontalLines[(index + 1) % horizontalLines.length] - line.line.set({ strokeWidth: 5 }) - nextLine.line.set({ strokeWidth: 5 }) + line.set({ strokeWidth: 5 }) + nextLine.set({ strokeWidth: 5 }) polygon.canvas.renderAll()