You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

110 lines
2.8 KiB

  1. import { v4 as uuidv4 } from 'uuid'
  2. import Flatten from '@flatten-js/core'
  3. import LabelProps from '@/domain/models/tasks/shared/LabelProps'
  4. import LineSegment from './LineSegment'
  5. import PolygonProps from './PolygonProps'
  6. import ValidationError from './errors'
  7. import Point = Flatten.Point
  8. import Vector = Flatten.Vector
  9. const MINIMUM_NUMBER_OF_SIDES = 3
  10. export default class Polygon {
  11. readonly id: string
  12. readonly labelId: number
  13. points: Point[] = []
  14. constructor(labelId: number, points: number[] = [], id: string = uuidv4()) {
  15. if (points.length % 2 !== 0) {
  16. throw new ValidationError('Invalid number of points')
  17. }
  18. this.id = id
  19. this.labelId = labelId
  20. for (let i = 0; i < points.length; i += 2) {
  21. this.addPoint(points[i], points[i + 1])
  22. }
  23. }
  24. clone(): Polygon {
  25. return new Polygon(this.labelId, this.flattenedPoints, this.id)
  26. }
  27. translate(x: number, y: number): void {
  28. const vector = new Vector(x, y)
  29. this.points = this.points.map((point) => point.translate(vector))
  30. }
  31. canBeClosed(): boolean {
  32. return this.points.length >= MINIMUM_NUMBER_OF_SIDES
  33. }
  34. addPoint(x: number, y: number): void {
  35. const point = new Point(x, y)
  36. this.points.push(point)
  37. }
  38. movePoint(index: number, x: number, y: number): void {
  39. const point = new Point(x, y)
  40. this.points[index] = point
  41. }
  42. removePoint(index: number): void {
  43. if (this.points.length > MINIMUM_NUMBER_OF_SIDES) {
  44. this.points.splice(index, 1)
  45. }
  46. }
  47. insertPoint(x: number, y: number, index: number): void {
  48. const point = new Point(x, y)
  49. this.points.splice(index, 0, point)
  50. }
  51. toPoints(): Point[] {
  52. return this.points.map((point) => point.clone())
  53. }
  54. minMaxPoints(): [number, number, number, number] {
  55. const [minX, minY, maxX, maxY] = this.points.reduce(
  56. ([minx, miny, maxx, maxy], point) => [
  57. Math.min(minx, point.x),
  58. Math.min(miny, point.y),
  59. Math.max(maxx, point.x),
  60. Math.max(maxy, point.y)
  61. ],
  62. [Infinity, Infinity, -Infinity, -Infinity]
  63. )
  64. return [minX, minY, maxX, maxY]
  65. }
  66. toProps(): PolygonProps {
  67. return {
  68. id: this.id,
  69. label: this.labelId,
  70. points: this.points.flatMap((point) => [point.x, point.y])
  71. }
  72. }
  73. getColor(labels: LabelProps[]): string {
  74. return labels.find((label) => label.id === this.labelId)!.color || '##ff0000'
  75. }
  76. get lineSegments(): LineSegment[] {
  77. const lineSegments = []
  78. for (let i = 0; i < this.points.length; i += 1) {
  79. const p1 = this.points[i]
  80. const p2 = this.points[(i + 1) % this.points.length]
  81. lineSegments.push(new LineSegment(p1, p2))
  82. }
  83. return lineSegments
  84. }
  85. get flattenedPoints(): number[] {
  86. return this.points.flatMap((point) => [point.x, point.y])
  87. }
  88. get numberOfSides(): number {
  89. return this.points.length
  90. }
  91. }