@typeonce/ritmo-math 0.12.0 → 0.14.0

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 (68) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/README.md +3 -3
  3. package/dist/Aabb.d.ts +15 -15
  4. package/dist/Aabb.d.ts.map +1 -1
  5. package/dist/Aabb.js +13 -5
  6. package/dist/Aabb.js.map +1 -1
  7. package/dist/Collide2.d.ts +48 -14
  8. package/dist/Collide2.d.ts.map +1 -1
  9. package/dist/Collide2.js +140 -17
  10. package/dist/Collide2.js.map +1 -1
  11. package/dist/Random.d.ts +16 -6
  12. package/dist/Random.d.ts.map +1 -1
  13. package/dist/Random.js +13 -3
  14. package/dist/Random.js.map +1 -1
  15. package/dist/Scalar.d.ts +26 -7
  16. package/dist/Scalar.d.ts.map +1 -1
  17. package/dist/Scalar.js +6 -3
  18. package/dist/Scalar.js.map +1 -1
  19. package/dist/Segment2.d.ts +56 -0
  20. package/dist/Segment2.d.ts.map +1 -0
  21. package/dist/Segment2.js +32 -0
  22. package/dist/Segment2.js.map +1 -0
  23. package/dist/Size2.d.ts +15 -15
  24. package/dist/Size2.d.ts.map +1 -1
  25. package/dist/Size2.js +13 -5
  26. package/dist/Size2.js.map +1 -1
  27. package/dist/Vector2.d.ts +15 -15
  28. package/dist/Vector2.d.ts.map +1 -1
  29. package/dist/Vector2.js +13 -5
  30. package/dist/Vector2.js.map +1 -1
  31. package/dist/index.d.ts +6 -4
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +6 -4
  34. package/dist/index.js.map +1 -1
  35. package/dist/internal/aabb.d.ts +9 -2
  36. package/dist/internal/aabb.d.ts.map +1 -1
  37. package/dist/internal/aabb.js +3 -1
  38. package/dist/internal/aabb.js.map +1 -1
  39. package/dist/internal/codec.d.ts +9 -0
  40. package/dist/internal/codec.d.ts.map +1 -0
  41. package/dist/internal/codec.js +16 -0
  42. package/dist/internal/codec.js.map +1 -0
  43. package/dist/internal/scalar.d.ts +3 -4
  44. package/dist/internal/scalar.d.ts.map +1 -1
  45. package/dist/internal/scalar.js +19 -12
  46. package/dist/internal/scalar.js.map +1 -1
  47. package/dist/internal/size2.d.ts +9 -2
  48. package/dist/internal/size2.d.ts.map +1 -1
  49. package/dist/internal/size2.js +3 -1
  50. package/dist/internal/size2.js.map +1 -1
  51. package/dist/internal/vector2.d.ts +9 -2
  52. package/dist/internal/vector2.d.ts.map +1 -1
  53. package/dist/internal/vector2.js +3 -1
  54. package/dist/internal/vector2.js.map +1 -1
  55. package/package.json +2 -2
  56. package/src/Aabb.ts +15 -16
  57. package/src/Collide2.ts +153 -22
  58. package/src/Random.ts +21 -7
  59. package/src/Scalar.ts +27 -7
  60. package/src/Segment2.ts +71 -0
  61. package/src/Size2.ts +15 -16
  62. package/src/Vector2.ts +15 -16
  63. package/src/index.ts +6 -4
  64. package/src/internal/aabb.ts +8 -2
  65. package/src/internal/codec.ts +21 -0
  66. package/src/internal/scalar.ts +24 -13
  67. package/src/internal/size2.ts +8 -2
  68. package/src/internal/vector2.ts +8 -2
@@ -0,0 +1,21 @@
1
+ import type { StandardSchema } from "@typeonce/ritmo/Descriptor"
2
+ import type * as Result from "@typeonce/ritmo/Result"
3
+
4
+ /**
5
+ * The codec of one math type, from its decode: a Standard Schema whose saved
6
+ * form is the raw shape. The value is already plain data, so it needs no
7
+ * `encode`.
8
+ */
9
+ export const codecOf = <Raw, Value>(
10
+ decode: (raw: unknown) => Result.Result<Value, unknown>,
11
+ expected: string
12
+ ): StandardSchema<Raw, Value> => ({
13
+ "~standard": {
14
+ version: 1,
15
+ vendor: "ritmo-math",
16
+ validate: (raw) => {
17
+ const value = decode(raw)
18
+ return value.ok ? { value: value.value } : { issues: [{ message: `Expected ${expected}` }] }
19
+ }
20
+ }
21
+ })
@@ -1,18 +1,33 @@
1
- import * as Brand from "@typeonce/ritmo/Brand"
2
1
  import * as Result from "@typeonce/ritmo/Result"
3
2
  import type * as Scalar from "../Scalar.ts"
3
+ import { codecOf } from "./codec.ts"
4
4
 
5
- const finiteBrand = Brand.refine<Scalar.Finite, number, Scalar.Error>((value) =>
5
+ // A branded number's constructors, guard, and codec, from its check.
6
+ const refine = <Value extends number>(
7
+ validate: (raw: number) => Result.Result<Value, Scalar.Error>,
8
+ expected: string
9
+ ): Scalar.Constructor<Value> => ({
10
+ result: validate,
11
+ option: (raw) => {
12
+ const result = validate(raw)
13
+ return result.ok ? result.value : null
14
+ },
15
+ is: (raw): raw is Value => typeof raw === "number" && validate(raw).ok,
16
+ codec: codecOf<number, Value>((raw) => typeof raw === "number" ? validate(raw) : Result.failure(raw), expected)
17
+ })
18
+
19
+ export const Finite = refine<Scalar.Finite>((value) =>
6
20
  Number.isFinite(value)
7
21
  ? Result.success(value as Scalar.Finite)
8
22
  : Result.failure({
9
23
  tag: "Scalar/NotFinite",
10
24
  value
11
- })
25
+ }),
26
+ "a finite number"
12
27
  )
13
28
 
14
- const nonNegativeBrand = Brand.refine<Scalar.NonNegative, number, Scalar.Error>((value) => {
15
- const finite = finiteBrand.result(value)
29
+ export const NonNegative = refine<Scalar.NonNegative>((value) => {
30
+ const finite = Finite.result(value)
16
31
  if (!finite.ok) {
17
32
  return finite
18
33
  }
@@ -23,10 +38,10 @@ const nonNegativeBrand = Brand.refine<Scalar.NonNegative, number, Scalar.Error>(
23
38
  tag: "Scalar/Negative",
24
39
  value
25
40
  })
26
- })
41
+ }, "a finite number, 0 or more")
27
42
 
28
- const positiveBrand = Brand.refine<Scalar.Positive, number, Scalar.Error>((value) => {
29
- const finite = finiteBrand.result(value)
43
+ export const Positive = refine<Scalar.Positive>((value) => {
44
+ const finite = Finite.result(value)
30
45
  if (!finite.ok) {
31
46
  return finite
32
47
  }
@@ -37,11 +52,7 @@ const positiveBrand = Brand.refine<Scalar.Positive, number, Scalar.Error>((value
37
52
  tag: "Scalar/NonPositive",
38
53
  value
39
54
  })
40
- })
41
-
42
- export const Finite = finiteBrand
43
- export const NonNegative = nonNegativeBrand
44
- export const Positive = positiveBrand
55
+ }, "a finite number above 0")
45
56
 
46
57
  export const clamp = (value: number, min: number, max: number): Scalar.Finite =>
47
58
  Math.min(Math.max(value, min), max) as Scalar.Finite
@@ -1,3 +1,4 @@
1
+ import { codecOf } from "./codec.ts"
1
2
  import * as Result from "@typeonce/ritmo/Result"
2
3
  import * as Scalar from "../Scalar.ts"
3
4
  import type * as Size2 from "../Size2.ts"
@@ -28,7 +29,7 @@ export const option = (raw: Size2.Raw): Size2.Size2 | null => {
28
29
  return next.ok ? next.value : null
29
30
  }
30
31
 
31
- export const is = (raw: Size2.Raw): raw is Size2.Raw & Size2.Size2 => result(raw).ok
32
+ export const is = (raw: unknown): raw is Size2.Size2 => decode(raw).ok
32
33
 
33
34
  export const toRaw = (size: Size2.Size2): Size2.Raw => ({
34
35
  width: size.width,
@@ -44,5 +45,10 @@ export const height = (size: Size2.Size2): Scalar.NonNegative => size.height
44
45
  export const isRawShape = (raw: unknown): raw is Size2.Raw =>
45
46
  typeof raw === "object" && raw !== null && typeof (raw as Record<string, unknown>)["width"] === "number" && typeof (raw as Record<string, unknown>)["height"] === "number"
46
47
 
47
- export const decode = (raw: unknown): Result.Result<Size2.Size2, Size2.Error | Size2.DecodeError> =>
48
+ /** Untrusted input without the raw shape. */
49
+ type Malformed = { readonly tag: "Size2/Malformed"; readonly input: unknown }
50
+
51
+ export const decode = (raw: unknown): Result.Result<Size2.Size2, Size2.Error | Malformed> =>
48
52
  isRawShape(raw) ? result(raw) : Result.failure({ tag: "Size2/Malformed", input: raw })
53
+
54
+ export const codec = codecOf<Size2.Raw, Size2.Size2>(decode, "a size { width, height } of non-negative finite numbers")
@@ -1,3 +1,4 @@
1
+ import { codecOf } from "./codec.ts"
1
2
  import * as Result from "@typeonce/ritmo/Result"
2
3
  import * as Scalar from "../Scalar.ts"
3
4
  import type * as Vector2 from "../Vector2.ts"
@@ -28,7 +29,7 @@ export const option = (raw: Vector2.Raw): Vector2.Vector2 | null => {
28
29
  return next.ok ? next.value : null
29
30
  }
30
31
 
31
- export const is = (raw: Vector2.Raw): raw is Vector2.Raw & Vector2.Vector2 => result(raw).ok
32
+ export const is = (raw: unknown): raw is Vector2.Vector2 => decode(raw).ok
32
33
 
33
34
  export const toRaw = (vector: Vector2.Vector2): Vector2.Raw => ({
34
35
  x: vector.x,
@@ -111,5 +112,10 @@ export const normalizeXYOrZero = (x: number, y: number): {
111
112
  export const isRawShape = (raw: unknown): raw is Vector2.Raw =>
112
113
  typeof raw === "object" && raw !== null && typeof (raw as Record<string, unknown>)["x"] === "number" && typeof (raw as Record<string, unknown>)["y"] === "number"
113
114
 
114
- export const decode = (raw: unknown): Result.Result<Vector2.Vector2, Vector2.Error | Vector2.DecodeError> =>
115
+ /** Untrusted input without the raw shape. */
116
+ type Malformed = { readonly tag: "Vector2/Malformed"; readonly input: unknown }
117
+
118
+ export const decode = (raw: unknown): Result.Result<Vector2.Vector2, Vector2.Error | Malformed> =>
115
119
  isRawShape(raw) ? result(raw) : Result.failure({ tag: "Vector2/Malformed", input: raw })
120
+
121
+ export const codec = codecOf<Vector2.Raw, Vector2.Vector2>(decode, "a vector { x, y } of finite numbers")