300 lines
7.7 KiB
JavaScript
300 lines
7.7 KiB
JavaScript
import { fabric } from 'fabric'
|
|
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 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()
|
|
}
|
|
|
|
if (canvas) {
|
|
this.canvas = canvas
|
|
}
|
|
|
|
this.init()
|
|
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,
|
|
direction: getDirectionByPoint(point, nextPoint),
|
|
idx: i,
|
|
})
|
|
this.lines.push(line)
|
|
})
|
|
},
|
|
|
|
// 보조선 그리기
|
|
drawHelpLine(chon = 4) {
|
|
drawHelpLineInHexagon(this, chon)
|
|
},
|
|
|
|
addLengthText() {
|
|
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
|
|
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,
|
|
parentId: this.id,
|
|
idx: i,
|
|
name: 'lengthText',
|
|
})
|
|
|
|
this.texts.push(text)
|
|
this.canvas.add(text)
|
|
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
|
|
|
|
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()
|
|
},
|
|
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
|
|
},
|
|
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,
|
|
}
|
|
})
|
|
},
|
|
setWall: function (wall) {
|
|
this.wall = wall
|
|
},
|
|
setViewLengthText(isView) {
|
|
this.texts.forEach((text) => {
|
|
text.set({ visible: isView })
|
|
})
|
|
},
|
|
})
|