244 lines
5.3 KiB
JavaScript
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
|
|
}
|
|
}
|