@benev/math 0.1.0 → 0.2.0-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/README.md +110 -23
  2. package/package.json +3 -1
  3. package/s/{concepts → core}/quat.ts +56 -16
  4. package/s/{concepts → core}/vec2.ts +52 -38
  5. package/s/{concepts → core}/vec3.ts +57 -47
  6. package/s/{concepts → core}/vec4.ts +13 -16
  7. package/s/index.ts +25 -10
  8. package/s/optimizers/hash-map.ts +71 -0
  9. package/s/optimizers/hash-set.ts +50 -0
  10. package/s/optimizers/zen.ts +151 -0
  11. package/s/physics/2d/collide2d.ts +51 -0
  12. package/s/physics/2d/intersect2d.ts +80 -0
  13. package/s/shapes/2d/circle.ts +43 -0
  14. package/s/shapes/2d/edge.ts +3 -0
  15. package/s/shapes/2d/index.ts +6 -0
  16. package/s/shapes/2d/pill.ts +3 -0
  17. package/s/shapes/2d/rect.ts +74 -0
  18. package/s/shapes/3d/box.ts +84 -0
  19. package/s/shapes/3d/capsule.ts +3 -0
  20. package/s/shapes/3d/index.ts +6 -0
  21. package/s/shapes/3d/segment.ts +48 -0
  22. package/s/shapes/3d/sphere.ts +3 -0
  23. package/s/tools/angles.ts +49 -0
  24. package/s/{concepts → tools}/circular.ts +5 -3
  25. package/s/{concepts → tools}/randy.ts +0 -14
  26. package/s/{concepts → tools}/scalar.ts +11 -11
  27. package/s/{concepts → tools}/spline.ts +11 -10
  28. package/x/{concepts → core}/quat.d.ts +10 -5
  29. package/x/{concepts → core}/quat.js +49 -12
  30. package/x/core/quat.js.map +1 -0
  31. package/x/{concepts → core}/vec2.d.ts +16 -17
  32. package/x/{concepts → core}/vec2.js +37 -21
  33. package/x/core/vec2.js.map +1 -0
  34. package/x/{concepts → core}/vec3.d.ts +19 -20
  35. package/x/{concepts → core}/vec3.js +46 -27
  36. package/x/core/vec3.js.map +1 -0
  37. package/x/{concepts → core}/vec4.d.ts +5 -7
  38. package/x/{concepts → core}/vec4.js +12 -12
  39. package/x/core/vec4.js.map +1 -0
  40. package/x/index.d.ts +21 -10
  41. package/x/index.js +21 -10
  42. package/x/index.js.map +1 -1
  43. package/x/optimizers/hash-map.d.ts +17 -0
  44. package/x/optimizers/hash-map.js +55 -0
  45. package/x/optimizers/hash-map.js.map +1 -0
  46. package/x/optimizers/hash-set.d.ts +13 -0
  47. package/x/optimizers/hash-set.js +39 -0
  48. package/x/optimizers/hash-set.js.map +1 -0
  49. package/x/optimizers/zen.d.ts +33 -0
  50. package/x/optimizers/zen.js +121 -0
  51. package/x/optimizers/zen.js.map +1 -0
  52. package/x/physics/2d/collide2d.d.ts +8 -0
  53. package/x/physics/2d/collide2d.js +36 -0
  54. package/x/physics/2d/collide2d.js.map +1 -0
  55. package/x/physics/2d/intersect2d.d.ts +13 -0
  56. package/x/physics/2d/intersect2d.js +57 -0
  57. package/x/physics/2d/intersect2d.js.map +1 -0
  58. package/x/shapes/2d/circle.d.ts +18 -0
  59. package/x/shapes/2d/circle.js +34 -0
  60. package/x/shapes/2d/circle.js.map +1 -0
  61. package/x/shapes/2d/edge.d.ts +1 -0
  62. package/x/shapes/2d/edge.js +3 -0
  63. package/x/shapes/2d/edge.js.map +1 -0
  64. package/x/shapes/2d/index.d.ts +4 -0
  65. package/x/shapes/2d/index.js +5 -0
  66. package/x/shapes/2d/index.js.map +1 -0
  67. package/x/shapes/2d/pill.d.ts +1 -0
  68. package/x/shapes/2d/pill.js +3 -0
  69. package/x/shapes/2d/pill.js.map +1 -0
  70. package/x/shapes/2d/rect.d.ts +23 -0
  71. package/x/shapes/2d/rect.js +59 -0
  72. package/x/shapes/2d/rect.js.map +1 -0
  73. package/x/shapes/3d/box.d.ts +24 -0
  74. package/x/shapes/3d/box.js +68 -0
  75. package/x/shapes/3d/box.js.map +1 -0
  76. package/x/shapes/3d/capsule.d.ts +1 -0
  77. package/x/shapes/3d/capsule.js +3 -0
  78. package/x/shapes/3d/capsule.js.map +1 -0
  79. package/x/shapes/3d/index.d.ts +4 -0
  80. package/x/shapes/3d/index.js +5 -0
  81. package/x/shapes/3d/index.js.map +1 -0
  82. package/x/shapes/3d/segment.d.ts +13 -0
  83. package/x/shapes/3d/segment.js +37 -0
  84. package/x/shapes/3d/segment.js.map +1 -0
  85. package/x/shapes/3d/sphere.d.ts +1 -0
  86. package/x/shapes/3d/sphere.js +3 -0
  87. package/x/shapes/3d/sphere.js.map +1 -0
  88. package/x/{concepts → tools}/angles.d.ts +3 -13
  89. package/x/tools/angles.js +41 -0
  90. package/x/tools/angles.js.map +1 -0
  91. package/x/{concepts → tools}/circular.js +4 -3
  92. package/x/tools/circular.js.map +1 -0
  93. package/x/tools/noise.js.map +1 -0
  94. package/x/{concepts → tools}/randy.d.ts +0 -8
  95. package/x/{concepts → tools}/randy.js +0 -10
  96. package/x/tools/randy.js.map +1 -0
  97. package/x/{concepts → tools}/scalar.d.ts +4 -4
  98. package/x/{concepts → tools}/scalar.js +11 -11
  99. package/x/tools/scalar.js.map +1 -0
  100. package/x/{concepts → tools}/spline.d.ts +3 -3
  101. package/x/{concepts → tools}/spline.js +3 -1
  102. package/x/tools/spline.js.map +1 -0
  103. package/s/concepts/angles.ts +0 -74
  104. package/x/concepts/angles.js +0 -62
  105. package/x/concepts/angles.js.map +0 -1
  106. package/x/concepts/circular.js.map +0 -1
  107. package/x/concepts/noise.js.map +0 -1
  108. package/x/concepts/quat.js.map +0 -1
  109. package/x/concepts/randy.js.map +0 -1
  110. package/x/concepts/scalar.js.map +0 -1
  111. package/x/concepts/spline.js.map +0 -1
  112. package/x/concepts/vec2.js.map +0 -1
  113. package/x/concepts/vec3.js.map +0 -1
  114. package/x/concepts/vec4.js.map +0 -1
  115. /package/s/{concepts → tools}/noise.ts +0 -0
  116. /package/x/{concepts → tools}/circular.d.ts +0 -0
  117. /package/x/{concepts → tools}/noise.d.ts +0 -0
  118. /package/x/{concepts → tools}/noise.js +0 -0
@@ -1,7 +1,5 @@
1
1
 
2
- import {Xyzw} from "./quat.js"
3
-
4
- export type Vec4Array = [number, number, number, number]
2
+ import {Xyzw, XyzwArray} from "./quat.js"
5
3
 
6
4
  export class Vec4 {
7
5
  constructor(
@@ -19,21 +17,24 @@ export class Vec4 {
19
17
  return new this(0, 0, 0, 0)
20
18
  }
21
19
 
22
- static array(v: Vec4Array) {
23
- return new this(...v)
20
+ static from(v: XyzwArray | Xyzw) {
21
+ return Array.isArray(v)
22
+ ? new this(...v)
23
+ : new this(v.x, v.y, v.z, v.w)
24
24
  }
25
25
 
26
- static import({x, y, z, w}: Xyzw) {
27
- return new this(x, y, z, w)
26
+ clone() {
27
+ return new Vec4(this.x, this.y, this.z, this.w)
28
28
  }
29
29
 
30
- static from(v: Vec4Array | Xyzw) {
31
- return Array.isArray(v)
32
- ? this.array(v)
33
- : this.import(v)
30
+ *[Symbol.iterator]() {
31
+ yield this.x
32
+ yield this.y
33
+ yield this.z
34
+ yield this.w
34
35
  }
35
36
 
36
- array(): Vec4Array {
37
+ toJSON(): XyzwArray {
37
38
  const {x, y, z, w} = this
38
39
  return [x, y, z, w]
39
40
  }
@@ -42,10 +43,6 @@ export class Vec4 {
42
43
  return `(Vec4 x${this.x.toFixed(2)}, y${this.y.toFixed(2)}, z${this.z.toFixed(2)}, w${this.w.toFixed(2)})`
43
44
  }
44
45
 
45
- clone() {
46
- return new Vec4(...this.array())
47
- }
48
-
49
46
  set_(x: number, y: number, z: number, w: number) {
50
47
  this.x = x
51
48
  this.y = y
package/s/index.ts CHANGED
@@ -1,12 +1,27 @@
1
1
 
2
- export * from "./concepts/angles.js"
3
- export * from "./concepts/circular.js"
4
- export * from "./concepts/noise.js"
5
- export * from "./concepts/quat.js"
6
- export * from "./concepts/randy.js"
7
- export * from "./concepts/scalar.js"
8
- export * as spline from "./concepts/spline.js"
9
- export * from "./concepts/vec2.js"
10
- export * from "./concepts/vec3.js"
11
- export * from "./concepts/vec4.js"
2
+ export * from "./core/quat.js"
3
+ export * from "./core/vec2.js"
4
+ export * from "./core/vec3.js"
5
+ export * from "./core/vec4.js"
6
+
7
+ export * from "./tools/angles.js"
8
+ export * from "./tools/circular.js"
9
+ export * from "./tools/noise.js"
10
+ export * from "./tools/randy.js"
11
+ export * from "./tools/scalar.js"
12
+ export * as spline from "./tools/spline.js"
13
+
14
+ export * from "./optimizers/hash-map.js"
15
+ export * from "./optimizers/hash-set.js"
16
+ export * from "./optimizers/zen.js"
17
+
18
+ export * from "./physics/2d/collide2d.js"
19
+ export * as collide2d from "./physics/2d/collide2d.js"
20
+ export * from "./physics/2d/intersect2d.js"
21
+ export * as intersect2d from "./physics/2d/intersect2d.js"
22
+
23
+ export * from "./shapes/2d/index.js"
24
+ export * as shapes2d from "./shapes/2d/index.js"
25
+ export * from "./shapes/3d/index.js"
26
+ export * as shapes3d from "./shapes/3d/index.js"
12
27
 
@@ -0,0 +1,71 @@
1
+
2
+ import {MapG} from "@e280/stz"
3
+
4
+ export class HashMap<Key, Value> {
5
+ #map = new MapG<string, [Key, Value]>
6
+
7
+ constructor(
8
+ private hash: (x: Key) => string,
9
+ entries: [Key, Value][] = [],
10
+ ) {
11
+ for (const [key, value] of entries)
12
+ this.set(key, value)
13
+ }
14
+
15
+ get size() {
16
+ return this.#map.size
17
+ }
18
+
19
+ get(key: Key) {
20
+ const result = this.#map.get(this.hash(key))
21
+ if (result)
22
+ return result[1]
23
+ }
24
+
25
+ require(key: Key) {
26
+ return this.#map.require(this.hash(key))[1]
27
+ }
28
+
29
+ has(key: Key) {
30
+ return this.#map.has(this.hash(key))
31
+ }
32
+
33
+ set(key: Key, value: Value) {
34
+ this.#map.set(this.hash(key), [key, value])
35
+ return this
36
+ }
37
+
38
+ delete(...keys: Key[]) {
39
+ for (const key of keys)
40
+ this.#map.delete(this.hash(key))
41
+ return this
42
+ }
43
+
44
+ entries() {
45
+ return this.#map.values()
46
+ }
47
+
48
+ [Symbol.iterator]() {
49
+ return this.entries()
50
+ }
51
+
52
+ *keys() {
53
+ for (const [key] of this.#map.values())
54
+ yield key
55
+ }
56
+
57
+ *values() {
58
+ for (const [,value] of this.#map.values())
59
+ yield value
60
+ }
61
+
62
+ array() {
63
+ return [...this.entries()]
64
+ }
65
+
66
+ guarantee(key: Key, fn: () => Value) {
67
+ const [,value] = this.#map.guarantee(this.hash(key), () => [key, fn()])
68
+ return value
69
+ }
70
+ }
71
+
@@ -0,0 +1,50 @@
1
+
2
+ export class HashSet<Value> {
3
+ #map = new Map<string, Value>
4
+
5
+ constructor(
6
+ private hash: (value: Value) => string,
7
+ values: Value[] = [],
8
+ ) {
9
+ this.add(...values)
10
+ }
11
+
12
+ get size() {
13
+ return this.#map.size
14
+ }
15
+
16
+ get(value: Value) {
17
+ return this.#map.get(this.hash(value))
18
+ }
19
+
20
+ has(value: Value) {
21
+ return this.#map.has(this.hash(value))
22
+ }
23
+
24
+ add(...values: Value[]) {
25
+ for (const value of values) {
26
+ const key = this.hash(value)
27
+ this.#map.set(key, value)
28
+ }
29
+ return this
30
+ }
31
+
32
+ delete(...values: Value[]) {
33
+ for (const value of values)
34
+ this.#map.delete(this.hash(value))
35
+ return this
36
+ }
37
+
38
+ array() {
39
+ return [...this.#map.values()]
40
+ }
41
+
42
+ values() {
43
+ return this.#map.values()
44
+ }
45
+
46
+ [Symbol.iterator]() {
47
+ return this.values()
48
+ }
49
+ }
50
+
@@ -0,0 +1,151 @@
1
+
2
+ import {MapG} from "@e280/stz"
3
+ import {Vec2} from "../core/vec2.js"
4
+ import {Rect} from "../shapes/2d/rect.js"
5
+ import {rectVsRect} from "../physics/2d/collide2d.js"
6
+
7
+ export class Zen<X> {
8
+ zones = new Set<ZenZone<X>>()
9
+
10
+ constructor(
11
+ public grid: ZenGrid<X>,
12
+ public rect: Rect,
13
+ public item: X,
14
+ ) {}
15
+
16
+ update() {
17
+ this.grid.update(this)
18
+ }
19
+
20
+ delete() {
21
+ this.grid.delete(this)
22
+ }
23
+ }
24
+
25
+ export class ZenZone<X> {
26
+ rect: Rect
27
+ zens = new Set<Zen<X>>()
28
+
29
+ constructor(public hash: string, center: Vec2, size: Vec2) {
30
+ this.rect = Rect.fromCenter(center, size)
31
+ }
32
+ }
33
+
34
+ export class ZenGrid<X> {
35
+ #zones = new MapG<string, ZenZone<X>>()
36
+
37
+ constructor(private zoneExtent: Vec2) {}
38
+
39
+ count() {
40
+ let n = 0
41
+ for (const zone of this.#zones.values())
42
+ n += zone.zens.size
43
+ return n
44
+ }
45
+
46
+ create(rect: Rect, item: X) {
47
+ const zen = new Zen<X>(this, rect, item)
48
+ this.update(zen)
49
+ return zen
50
+ }
51
+
52
+ update(zen: Zen<X>) {
53
+ const wantedZones = this.#selectZones(zen.rect)
54
+
55
+ // delete stale zones
56
+ for (const zone of zen.zones) {
57
+ if (!wantedZones.has(zone)) {
58
+ zen.zones.delete(zone)
59
+ zone.zens.delete(zen)
60
+ }
61
+ }
62
+
63
+ // add fresh zones
64
+ for (const zone of wantedZones) {
65
+ if (!zen.zones.has(zone)) {
66
+ zone.zens.add(zen)
67
+ zen.zones.add(zone)
68
+ }
69
+ }
70
+ }
71
+
72
+ delete(zen: Zen<X>) {
73
+ const emptyZones: ZenZone<X>[] = []
74
+
75
+ for (const zone of zen.zones) {
76
+ zone.zens.delete(zen)
77
+ if (zone.zens.size === 0)
78
+ emptyZones.push(zone)
79
+ }
80
+
81
+ for (const emptyZone of emptyZones)
82
+ this.#zones.delete(emptyZone.hash)
83
+ }
84
+
85
+ check(rect: Rect) {
86
+ const zones = this.#selectZones(rect)
87
+
88
+ for (const zone of zones)
89
+ for (const zen of zone.zens)
90
+ if (rectVsRect(rect, zen.rect))
91
+ return true
92
+
93
+ return false
94
+ }
95
+
96
+ /** return set of zens that touch the given rect */
97
+ query(rect: Rect) {
98
+ const zones = this.#selectZones(rect)
99
+ const selected = new Set<Zen<X>>()
100
+
101
+ for (const zone of zones)
102
+ for (const zen of zone.zens)
103
+ if (!selected.has(zen) && rectVsRect(rect, zen.rect))
104
+ selected.add(zen)
105
+
106
+ return selected
107
+ }
108
+
109
+ /** return all zen items that touch the given rect */
110
+ queryItems(rect: Rect) {
111
+ return [...this.query(rect)].map(zen => zen.item)
112
+ }
113
+
114
+ /** return all zen rects that touch the given rect */
115
+ queryRects(rect: Rect) {
116
+ return [...this.query(rect)].map(zen => zen.rect)
117
+ }
118
+
119
+ #hash(v: Vec2) {
120
+ return `${v.x},${v.y}`
121
+ }
122
+
123
+ #calculateZoneCorner(point: Vec2) {
124
+ return new Vec2(
125
+ Math.floor(point.x / this.zoneExtent.x),
126
+ Math.floor(point.y / this.zoneExtent.y),
127
+ ).multiply(this.zoneExtent)
128
+ }
129
+
130
+ #obtainZone(zoneCorner: Vec2) {
131
+ const hash = this.#hash(zoneCorner)
132
+ return this.#zones.guarantee(hash, () => new ZenZone(
133
+ hash,
134
+ zoneCorner.clone().add(this.zoneExtent.clone().half()),
135
+ this.zoneExtent,
136
+ ))
137
+ }
138
+
139
+ #selectZones(rect: Rect) {
140
+ const zones = new Set<ZenZone<X>>()
141
+ const minZoneCorner = this.#calculateZoneCorner(rect.min)
142
+ const maxZoneCorner = this.#calculateZoneCorner(rect.max)
143
+
144
+ for (let x = minZoneCorner.x; x <= maxZoneCorner.x; x += this.zoneExtent.x)
145
+ for (let y = minZoneCorner.y; y <= maxZoneCorner.y; y += this.zoneExtent.y)
146
+ zones.add(this.#obtainZone(new Vec2(x, y)))
147
+
148
+ return zones
149
+ }
150
+ }
151
+
@@ -0,0 +1,51 @@
1
+
2
+ import {Vec2} from "../../core/vec2.js"
3
+ import {Rect} from "../../shapes/2d/rect.js"
4
+ import {Scalar} from "../../tools/scalar.js"
5
+ import {Circle} from "../../shapes/2d/circle.js"
6
+
7
+ export function pointVsRect(point: Vec2, box: Rect) {
8
+ const {min, max} = box
9
+ return (
10
+ point.x >= min.x &&
11
+ point.x <= max.x &&
12
+ point.y >= min.y &&
13
+ point.y <= max.y
14
+ )
15
+ }
16
+
17
+ export function pointVsCircle(point: Vec2, circle: Circle) {
18
+ const dx = point.x - circle.center.x
19
+ const dy = point.y - circle.center.y
20
+ const distanceSquared = (dx ** 2) + (dy ** 2)
21
+ return distanceSquared <= (circle.radius ** 2)
22
+ }
23
+
24
+ export function rectVsRect(a: Rect, b: Rect) {
25
+ return !(
26
+ a.max.x <= b.min.x ||
27
+ a.min.x >= b.max.x ||
28
+ a.max.y <= b.min.y ||
29
+ a.min.y >= b.max.y
30
+ )
31
+ }
32
+
33
+ export function rectVsCircle(rect: Rect, circle: Circle) {
34
+ const clamped = new Vec2(
35
+ Scalar.clamp(circle.center.x, rect.min.x, rect.max.x),
36
+ Scalar.clamp(circle.center.y, rect.min.y, rect.max.y),
37
+ )
38
+ const difference = circle.center.clone().subtract(clamped)
39
+ const distanceSquared = (difference.x ** 2) + (difference.y ** 2)
40
+ const radiusSquared = circle.radius ** 2
41
+ return distanceSquared <= radiusSquared
42
+ }
43
+
44
+ export function circleVsCircle(circleA: Circle, circleB: Circle) {
45
+ const dx = circleB.center.x - circleA.center.x
46
+ const dy = circleB.center.y - circleA.center.y
47
+ const distanceSquared = (dx ** 2) + (dy ** 2)
48
+ const radiusSum = circleA.radius + circleB.radius
49
+ return distanceSquared <= (radiusSum ** 2)
50
+ }
51
+
@@ -0,0 +1,80 @@
1
+
2
+ import {Vec2} from "../../core/vec2.js"
3
+ import {Rect} from "../../shapes/2d/rect.js"
4
+ import {Scalar} from "../../tools/scalar.js"
5
+ import {Circle} from "../../shapes/2d/circle.js"
6
+ import {rectVsCircle, rectVsRect} from "./collide2d.js"
7
+
8
+ export class Intersection {
9
+ constructor(
10
+ public contactPoint: Vec2,
11
+ public depth: number,
12
+ public normalA: Vec2,
13
+ public normalB: Vec2,
14
+ ) {}
15
+ }
16
+
17
+ export function intersectRectVsRect(a: Rect, b: Rect) {
18
+ if (!rectVsRect(a, b)) return null
19
+
20
+ const overlapX = Math.min(a.max.x - b.min.x, b.max.x - a.min.x)
21
+ const overlapY = Math.min(a.max.y - b.min.y, b.max.y - a.min.y)
22
+ const depth = Math.min(overlapX, overlapY)
23
+
24
+ const aCenter = a.center()
25
+ const bCenter = b.center()
26
+
27
+ const contactPoint = new Vec2(
28
+ Scalar.clamp((aCenter.x + bCenter.x) / 2, b.min.x, b.max.x),
29
+ Scalar.clamp((aCenter.y + bCenter.y) / 2, b.min.y, b.max.y)
30
+ )
31
+
32
+ const normalA = depth === overlapX
33
+ ? new Vec2(bCenter.x > aCenter.x ? -1 : 1, 0)
34
+ : new Vec2(0, bCenter.y > aCenter.y ? -1 : 1)
35
+
36
+ const normalB = normalA.clone().multiplyBy(-1)
37
+
38
+ return new Intersection(contactPoint, depth, normalA, normalB)
39
+ }
40
+
41
+ export function intersectRectVsCircle(rect: Rect, circle: Circle) {
42
+ if (!rectVsCircle(rect, circle)) return null
43
+
44
+ const clamped = new Vec2(
45
+ Scalar.clamp(circle.center.x, rect.min.x, rect.max.x),
46
+ Scalar.clamp(circle.center.y, rect.min.y, rect.max.y),
47
+ )
48
+ const difference = circle.center.clone().subtract(clamped)
49
+ const distance = difference.magnitude()
50
+ const depth = circle.radius - distance
51
+
52
+ const contactPoint = clamped
53
+ const normalA = difference.normalize()
54
+ const normalB = normalA.clone().multiplyBy(-1)
55
+
56
+ return new Intersection(contactPoint, depth, normalA, normalB)
57
+ }
58
+
59
+ export function intersectCircleVsCircle(a: Circle, b: Circle) {
60
+ const dx = b.center.x - a.center.x
61
+ const dy = b.center.y - a.center.y
62
+ const distance = Math.sqrt(dx ** 2 + dy ** 2)
63
+ if (distance >= a.radius + b.radius) return null
64
+
65
+ const depth = Math.max(0, a.radius + b.radius - distance)
66
+
67
+ const normalA = distance === 0
68
+ ? new Vec2(1, 0) // fallback for perfectly overlapping circles
69
+ : new Vec2(dx / distance, dy / distance)
70
+
71
+ const normalB = normalA.clone().multiplyBy(-1)
72
+
73
+ const contactPoint = new Vec2(
74
+ (a.center.x + b.center.x) / 2,
75
+ (a.center.y + b.center.y) / 2
76
+ )
77
+
78
+ return new Intersection(contactPoint, depth, normalA, normalB)
79
+ }
80
+
@@ -0,0 +1,43 @@
1
+
2
+ import {Rect} from "./rect.js"
3
+ import {Vec2, XyArray, Xy} from "../../core/vec2.js"
4
+
5
+ export type CircleJson = [center: XyArray, radius: number]
6
+ export type CircleLike = {center: Xy, radius: number}
7
+
8
+ export class Circle {
9
+ constructor(
10
+ public center: Vec2,
11
+ public radius: number,
12
+ ) {}
13
+
14
+ static from(data: CircleJson | CircleLike) {
15
+ return Array.isArray(data)
16
+ ? new this(Vec2.from(data[0]), data[1])
17
+ : new this(Vec2.from(data.center), data.radius)
18
+ }
19
+
20
+ toJSON(): CircleJson {
21
+ return [this.center.clone().toJSON(), this.radius]
22
+ }
23
+
24
+ clone() {
25
+ return new Circle(this.center.clone(), this.radius)
26
+ }
27
+
28
+ set(circle: CircleLike) {
29
+ this.center.set(circle.center)
30
+ this.radius = circle.radius
31
+ }
32
+
33
+ translate(delta: Vec2) {
34
+ this.center.add(delta)
35
+ return this
36
+ }
37
+
38
+ boundingBox() {
39
+ const size = Vec2.all(this.radius * 2)
40
+ return Rect.fromCenter(this.center.clone(), size)
41
+ }
42
+ }
43
+
@@ -0,0 +1,3 @@
1
+
2
+ // TODO
3
+
@@ -0,0 +1,6 @@
1
+
2
+ export * from "./circle.js"
3
+ export * from "./edge.js"
4
+ export * from "./pill.js"
5
+ export * from "./rect.js"
6
+
@@ -0,0 +1,3 @@
1
+
2
+ // TODO
3
+
@@ -0,0 +1,74 @@
1
+
2
+ import {Vec2, XyArray, Xy} from "../../core/vec2.js"
3
+ import {pointVsRect} from "../../physics/2d/collide2d.js"
4
+
5
+ export type RectJson = [min: XyArray, max: XyArray]
6
+ export type RectLike = {min: Xy, max: Xy}
7
+
8
+ export class Rect {
9
+ constructor(
10
+ public min: Vec2,
11
+ public max: Vec2,
12
+ ) {}
13
+
14
+ static from(data: RectJson | RectLike) {
15
+ return Array.isArray(data)
16
+ ? new this(Vec2.from(data[0]), Vec2.from(data[1]))
17
+ : new this(Vec2.from(data.min), Vec2.from(data.max))
18
+ }
19
+
20
+ static fromCorner(min: Vec2, size: Vec2) {
21
+ const max = min.clone().add(size)
22
+ return new this(min, max)
23
+ }
24
+
25
+ static fromCenter(center: Vec2, size: Vec2) {
26
+ const halfSize = size.clone().half()
27
+ const min = center.clone().subtract(halfSize)
28
+ const max = center.clone().add(halfSize)
29
+ return new this(min, max)
30
+ }
31
+
32
+ clone() {
33
+ return new Rect(this.min.clone(), this.max.clone())
34
+ }
35
+
36
+ toJSON(): RectJson {
37
+ return [this.min.toJSON(), this.max.toJSON()]
38
+ }
39
+
40
+ set(rect: RectLike) {
41
+ this.min.set(rect.min)
42
+ this.max.set(rect.max)
43
+ }
44
+
45
+ normalize() {
46
+ const {min, max} = this
47
+ this.min.set(Vec2.min(min, max))
48
+ this.max.set(Vec2.max(min, max))
49
+ return this
50
+ }
51
+
52
+ size() {
53
+ return this.max.clone().subtract(this.min)
54
+ }
55
+
56
+ center() {
57
+ return this.min.clone().add(this.size().half())
58
+ }
59
+
60
+ translate(delta: Vec2) {
61
+ this.min.add(delta)
62
+ this.max.add(delta)
63
+ return this
64
+ }
65
+
66
+ contains(point: Vec2) {
67
+ return pointVsRect(point, this)
68
+ }
69
+
70
+ boundingBox() {
71
+ return this.clone()
72
+ }
73
+ }
74
+