244 lines
5.3 KiB
JavaScript

import { fabric } from 'fabric'
import { distanceBetweenPoints } from '@/util/canvas-util'
export default class QPolygon extends fabric.Polygon {
#viewLengthText
#text = []
#fontSize
type = 'QPolygon'
constructor(points, option) {
if (!option.fontSize) {
throw new Error('Font size is required.')
}
super(points, option)
this.#fontSize = option.fontSize
this.#init(points)
this.#addControl()
}
#init(points) {
this.#viewLengthText = points.viewLengthText ?? true
}
setViewLengthText(bool) {
this.#viewLengthText = bool
this.#addLengthText()
}
#addControl() {
this.on('removed', () => {
if (this.#text.length > 0) {
this.#text.forEach((text) => {
this.canvas.remove(text)
})
this.#text = []
}
})
this.on('added', () => {
this.#addLengthText()
})
this.on('modified', (e) => {
this.#addLengthText()
})
this.on('scaling', (e) => {
this.#addLengthText()
})
this.on('moving', () => {
this.#addLengthText()
})
}
setTexts(texts) {
this.#text = texts
}
getTexts() {
return this.#text
}
#addLengthText() {
return false
if (this.#text.length > 0) {
this.#text.forEach((text) => {
this.canvas.remove(text)
})
this.#text = []
}
if (!this.#viewLengthText) {
return
}
const scaleX = this.scaleX
const scaleY = this.scaleY
const points = this.points
for (let i = 0; i < points.length; i++) {
const start = points[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,
)
const text = new fabric.Text(length.toFixed(0), {
left: midPoint.x,
top: midPoint.y,
fontSize: this.#fontSize,
selectable: false,
})
this.#text.push(text)
this.canvas.add(text)
}
}
setFontSize(fontSize) {
this.#fontSize = fontSize
this.#addLengthText()
}
getCurrentPoints() {
const scaleX = this.scaleX
const scaleY = this.scaleY
return this.points.map((point) => {
return {
x: point.x * scaleX + this.left,
y: point.y * scaleY + this.top,
}
})
}
#distanceFromEdge(point) {
const vertices = this.points
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
}
fillCell(cell = { width: 50, height: 100, padding: 10 }) {
const points = this.points
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.points
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
}
getCurrentOptions = () => {
return this.options
}
}