@woosh/meep-engine 3.2.0 → 3.3.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.
- package/package.json +1 -1
- package/src/avif/encoder_worker.js +5 -18
- package/src/core/color/PQ/PQ_constants.d.ts +43 -0
- package/src/core/color/PQ/PQ_constants.d.ts.map +1 -0
- package/src/core/color/PQ/PQ_constants.js +50 -0
- package/src/core/color/PQ/PQ_to_linear.d.ts +24 -0
- package/src/core/color/PQ/PQ_to_linear.d.ts.map +1 -0
- package/src/core/color/PQ/PQ_to_linear.js +35 -0
- package/src/core/color/PQ/linear_to_PQ.d.ts +27 -0
- package/src/core/color/PQ/linear_to_PQ.d.ts.map +1 -0
- package/src/core/color/PQ/linear_to_PQ.js +32 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_other_face.d.ts +32 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_other_face.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_other_face.js +59 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_face_third_vertex.d.ts +26 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_face_third_vertex.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_face_third_vertex.js +55 -0
- package/src/engine/ecs/storage/populateEngineSerializationRegistry.d.ts.map +1 -1
- package/src/engine/ecs/storage/populateEngineSerializationRegistry.js +269 -265
- package/src/engine/graphics3/VolumetricLightMap.d.ts +90 -0
- package/src/engine/graphics3/VolumetricLightMap.d.ts.map +1 -0
- package/src/engine/graphics3/VolumetricLightMap.js +160 -0
- package/src/engine/graphics3/VolumetricLightMapSerializationAdapter.d.ts +32 -0
- package/src/engine/graphics3/VolumetricLightMapSerializationAdapter.d.ts.map +1 -0
- package/src/engine/graphics3/VolumetricLightMapSerializationAdapter.js +67 -0
- package/src/engine/graphics3/VolumetricLightMapSystem3.d.ts +98 -0
- package/src/engine/graphics3/VolumetricLightMapSystem3.d.ts.map +1 -0
- package/src/engine/graphics3/VolumetricLightMapSystem3.js +261 -0
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
- package/src/shade/renderer/global_illumination/brick4/cpu/brick4_empty_gpu_structure.d.ts +23 -0
- package/src/shade/renderer/global_illumination/brick4/cpu/brick4_empty_gpu_structure.d.ts.map +1 -0
- package/src/shade/renderer/global_illumination/brick4/cpu/brick4_empty_gpu_structure.js +34 -0
- package/src/shade/renderer/scene/optimization/geometry/internal/anchor_grow_topological.d.ts.map +1 -1
- package/src/shade/renderer/scene/optimization/geometry/internal/anchor_grow_topological.js +12 -3
- package/src/shade/renderer/scene/optimization/geometry/internal/match_validate_topology.js +3 -3
- package/src/shade/renderer/scene/optimization/geometry/internal/seed_correspondence_from_window.js +3 -3
- package/src/shade/renderer/scene/serialization/deserialize_scene.d.ts.map +1 -1
- package/src/shade/renderer/scene/serialization/deserialize_scene.js +0 -2
- package/src/shade/renderer/scene/optimization/geometry/internal/bt_face_loop_helpers.d.ts +0 -32
- package/src/shade/renderer/scene/optimization/geometry/internal/bt_face_loop_helpers.d.ts.map +0 -1
- package/src/shade/renderer/scene/optimization/geometry/internal/bt_face_loop_helpers.js +0 -77
- package/src/shade/renderer/scene/optimization/geometry/internal/bt_find_edge_between.d.ts +0 -12
- package/src/shade/renderer/scene/optimization/geometry/internal/bt_find_edge_between.d.ts.map +0 -1
- package/src/shade/renderer/scene/optimization/geometry/internal/bt_find_edge_between.js +0 -35
- package/src/shade/renderer/scene/serialization/linear_to_pq.d.ts +0 -2
- package/src/shade/renderer/scene/serialization/linear_to_pq.d.ts.map +0 -1
- package/src/shade/renderer/scene/serialization/linear_to_pq.js +0 -14
- package/src/shade/renderer/scene/serialization/pq_to_linear.d.ts +0 -9
- package/src/shade/renderer/scene/serialization/pq_to_linear.d.ts.map +0 -1
- package/src/shade/renderer/scene/serialization/pq_to_linear.js +0 -30
package/package.json
CHANGED
|
@@ -8,21 +8,11 @@ import avifEncoderFactory from './codec/enc/avif_enc.js';
|
|
|
8
8
|
import { initEmscriptenModule } from './utils.js';
|
|
9
9
|
import { defaultOptions } from './meta.js';
|
|
10
10
|
import { to_half_float_uint16 } from '../core/binary/to_half_float_uint16.js';
|
|
11
|
+
import { linear_to_PQ } from '../core/color/PQ/linear_to_PQ.js';
|
|
12
|
+
import { PQ_SDR_WHITE_LINEAR } from '../core/color/PQ/PQ_constants.js';
|
|
11
13
|
|
|
12
14
|
const AVIF_DEFAULT_SPEED = 5;
|
|
13
15
|
|
|
14
|
-
// PQ OETF (SMPTE ST 2084). Input: linear light normalized so 1.0 = 10000 nits. Output: PQ signal in [0,1].
|
|
15
|
-
const PQ_M1 = 0.1593017578125;
|
|
16
|
-
const PQ_M2 = 78.84375;
|
|
17
|
-
const PQ_C1 = 0.8359375;
|
|
18
|
-
const PQ_C2 = 18.8515625;
|
|
19
|
-
const PQ_C3 = 18.6875;
|
|
20
|
-
|
|
21
|
-
function linear_to_pq(x) {
|
|
22
|
-
const xp = Math.pow(Math.max(x, 0), PQ_M1);
|
|
23
|
-
return Math.pow((PQ_C1 + PQ_C2 * xp) / (1.0 + PQ_C3 * xp), PQ_M2);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
16
|
const AvifSubsample = {
|
|
27
17
|
YUV400: 0,
|
|
28
18
|
YUV420: 1,
|
|
@@ -30,10 +20,6 @@ const AvifSubsample = {
|
|
|
30
20
|
YUV444: 3,
|
|
31
21
|
};
|
|
32
22
|
|
|
33
|
-
// PQ tone mapping: 1.0 linear = SDR reference white = ~203 nits per BT.2408
|
|
34
|
-
const SDR_WHITE_NITS = 203;
|
|
35
|
-
const PQ_MAX_NITS = 10000;
|
|
36
|
-
|
|
37
23
|
let encoder_promise = null;
|
|
38
24
|
|
|
39
25
|
function get_encoder() {
|
|
@@ -103,8 +89,9 @@ function rgba_float16_to_pq_uint16(f16_view, width, height) {
|
|
|
103
89
|
for (let i = 0; i < f16_view.length; i += 4) {
|
|
104
90
|
for (let c = 0; c < 3; c++) {
|
|
105
91
|
const linear = Math.max(f16_view[i + c], 0);
|
|
106
|
-
|
|
107
|
-
|
|
92
|
+
// Engine linear is normalized to SDR reference white; PQ's is normalized to its own
|
|
93
|
+
// 10000-nit peak, so cross over before encoding.
|
|
94
|
+
const pq = linear_to_PQ(linear * PQ_SDR_WHITE_LINEAR);
|
|
108
95
|
out[i + c] = Math.round(pq * 4095);
|
|
109
96
|
}
|
|
110
97
|
// alpha: linear 0..1 mapped directly to 0..4095
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coefficients of the SMPTE ST 2084 (a.k.a. "PQ", Perceptual Quantizer) transfer function.
|
|
3
|
+
*
|
|
4
|
+
* Every value is exact in binary floating point -- each is a small integer over a power of two, as the
|
|
5
|
+
* standard specifies them -- so the OETF and the EOTF built from these are exact inverses to within the
|
|
6
|
+
* rounding of `Math.pow` alone.
|
|
7
|
+
*
|
|
8
|
+
* @author Alex Goldring
|
|
9
|
+
* @copyright Company Named Limited (c) 2026
|
|
10
|
+
*/
|
|
11
|
+
/** @type {number} 2610 / 16384 */
|
|
12
|
+
export const PQ_M1: number;
|
|
13
|
+
/** @type {number} 2523 / 32 */
|
|
14
|
+
export const PQ_M2: number;
|
|
15
|
+
/** @type {number} 3424 / 4096 */
|
|
16
|
+
export const PQ_C1: number;
|
|
17
|
+
/** @type {number} 2413 / 128 */
|
|
18
|
+
export const PQ_C2: number;
|
|
19
|
+
/** @type {number} 2392 / 128 */
|
|
20
|
+
export const PQ_C3: number;
|
|
21
|
+
/**
|
|
22
|
+
* Peak luminance of the PQ signal range, in nits. A linear value of `1.0` in PQ's own normalization is
|
|
23
|
+
* this bright -- which is why {@link linear_to_PQ} and {@link PQ_to_linear} work in that normalization
|
|
24
|
+
* and not in any renderer's.
|
|
25
|
+
* @type {number}
|
|
26
|
+
*/
|
|
27
|
+
export const PQ_MAX_NITS: number;
|
|
28
|
+
/**
|
|
29
|
+
* Luminance of SDR reference white, in nits, per ITU-R BT.2408. This is the engine's `1.0`, and it is
|
|
30
|
+
* NOT PQ's `1.0`: converting between the two scales is the caller's job and needs
|
|
31
|
+
* {@link PQ_SDR_WHITE_LINEAR} in one direction or its reciprocal in the other.
|
|
32
|
+
* @type {number}
|
|
33
|
+
*/
|
|
34
|
+
export const SDR_WHITE_NITS: number;
|
|
35
|
+
/**
|
|
36
|
+
* SDR reference white expressed in PQ's linear normalization, i.e. `SDR_WHITE_NITS / PQ_MAX_NITS`.
|
|
37
|
+
*
|
|
38
|
+
* Multiply an engine-normalized linear value by this before {@link linear_to_PQ}; divide by it after
|
|
39
|
+
* {@link PQ_to_linear} to come back.
|
|
40
|
+
* @type {number}
|
|
41
|
+
*/
|
|
42
|
+
export const PQ_SDR_WHITE_LINEAR: number;
|
|
43
|
+
//# sourceMappingURL=PQ_constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PQ_constants.d.ts","sourceRoot":"","sources":["../../../../../src/core/color/PQ/PQ_constants.js"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,kCAAkC;AAClC,oBADW,MAAM,CACoB;AAErC,+BAA+B;AAC/B,oBADW,MAAM,CACa;AAE9B,iCAAiC;AACjC,oBADW,MAAM,CACc;AAE/B,gCAAgC;AAChC,oBADW,MAAM,CACe;AAEhC,gCAAgC;AAChC,oBADW,MAAM,CACY;AAE7B;;;;;GAKG;AACH,0BAFU,MAAM,CAEiB;AAEjC;;;;;GAKG;AACH,6BAFU,MAAM,CAEkB;AAElC;;;;;;GAMG;AACH,kCAFU,MAAM,CAEgD"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coefficients of the SMPTE ST 2084 (a.k.a. "PQ", Perceptual Quantizer) transfer function.
|
|
3
|
+
*
|
|
4
|
+
* Every value is exact in binary floating point -- each is a small integer over a power of two, as the
|
|
5
|
+
* standard specifies them -- so the OETF and the EOTF built from these are exact inverses to within the
|
|
6
|
+
* rounding of `Math.pow` alone.
|
|
7
|
+
*
|
|
8
|
+
* @author Alex Goldring
|
|
9
|
+
* @copyright Company Named Limited (c) 2026
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** @type {number} 2610 / 16384 */
|
|
13
|
+
export const PQ_M1 = 0.1593017578125;
|
|
14
|
+
|
|
15
|
+
/** @type {number} 2523 / 32 */
|
|
16
|
+
export const PQ_M2 = 78.84375;
|
|
17
|
+
|
|
18
|
+
/** @type {number} 3424 / 4096 */
|
|
19
|
+
export const PQ_C1 = 0.8359375;
|
|
20
|
+
|
|
21
|
+
/** @type {number} 2413 / 128 */
|
|
22
|
+
export const PQ_C2 = 18.8515625;
|
|
23
|
+
|
|
24
|
+
/** @type {number} 2392 / 128 */
|
|
25
|
+
export const PQ_C3 = 18.6875;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Peak luminance of the PQ signal range, in nits. A linear value of `1.0` in PQ's own normalization is
|
|
29
|
+
* this bright -- which is why {@link linear_to_PQ} and {@link PQ_to_linear} work in that normalization
|
|
30
|
+
* and not in any renderer's.
|
|
31
|
+
* @type {number}
|
|
32
|
+
*/
|
|
33
|
+
export const PQ_MAX_NITS = 10000;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Luminance of SDR reference white, in nits, per ITU-R BT.2408. This is the engine's `1.0`, and it is
|
|
37
|
+
* NOT PQ's `1.0`: converting between the two scales is the caller's job and needs
|
|
38
|
+
* {@link PQ_SDR_WHITE_LINEAR} in one direction or its reciprocal in the other.
|
|
39
|
+
* @type {number}
|
|
40
|
+
*/
|
|
41
|
+
export const SDR_WHITE_NITS = 203;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* SDR reference white expressed in PQ's linear normalization, i.e. `SDR_WHITE_NITS / PQ_MAX_NITS`.
|
|
45
|
+
*
|
|
46
|
+
* Multiply an engine-normalized linear value by this before {@link linear_to_PQ}; divide by it after
|
|
47
|
+
* {@link PQ_to_linear} to come back.
|
|
48
|
+
* @type {number}
|
|
49
|
+
*/
|
|
50
|
+
export const PQ_SDR_WHITE_LINEAR = SDR_WHITE_NITS / PQ_MAX_NITS;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SMPTE ST 2084 EOTF: decode a PQ signal back to linear light. Exact inverse of {@link linear_to_PQ}.
|
|
3
|
+
*
|
|
4
|
+
* NORMALIZATION: the result is in PQ's own scale, where `1.0` is {@link PQ_MAX_NITS} (10000 nits) --
|
|
5
|
+
* not the engine's, where `1.0` is {@link SDR_WHITE_NITS} (203 nits). Divide by
|
|
6
|
+
* {@link PQ_SDR_WHITE_LINEAR} to land back in engine units:
|
|
7
|
+
*
|
|
8
|
+
* ```js
|
|
9
|
+
* const engine_linear = PQ_to_linear(pq) / PQ_SDR_WHITE_LINEAR;
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* The numerator is clamped at `0` so that signals below `linear_to_PQ(0)` -- which the standard's
|
|
13
|
+
* curve never produces, but quantization and interpolation both do -- decode to black instead of
|
|
14
|
+
* `NaN` from a negative base under a fractional exponent.
|
|
15
|
+
*
|
|
16
|
+
* @param {number} pq PQ signal, in `[0, 1]`
|
|
17
|
+
* @returns {number} linear light, `1.0` = 10000 nits
|
|
18
|
+
* @see linear_to_PQ
|
|
19
|
+
*
|
|
20
|
+
* @author Alex Goldring
|
|
21
|
+
* @copyright Company Named Limited (c) 2026
|
|
22
|
+
*/
|
|
23
|
+
export function PQ_to_linear(pq: number): number;
|
|
24
|
+
//# sourceMappingURL=PQ_to_linear.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PQ_to_linear.d.ts","sourceRoot":"","sources":["../../../../../src/core/color/PQ/PQ_to_linear.js"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,iCAPW,MAAM,GACJ,MAAM,CAalB"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { PQ_C1, PQ_C2, PQ_C3, PQ_M1, PQ_M2 } from "./PQ_constants.js";
|
|
2
|
+
|
|
3
|
+
const PQ_M1_INV = 1 / PQ_M1;
|
|
4
|
+
const PQ_M2_INV = 1 / PQ_M2;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* SMPTE ST 2084 EOTF: decode a PQ signal back to linear light. Exact inverse of {@link linear_to_PQ}.
|
|
8
|
+
*
|
|
9
|
+
* NORMALIZATION: the result is in PQ's own scale, where `1.0` is {@link PQ_MAX_NITS} (10000 nits) --
|
|
10
|
+
* not the engine's, where `1.0` is {@link SDR_WHITE_NITS} (203 nits). Divide by
|
|
11
|
+
* {@link PQ_SDR_WHITE_LINEAR} to land back in engine units:
|
|
12
|
+
*
|
|
13
|
+
* ```js
|
|
14
|
+
* const engine_linear = PQ_to_linear(pq) / PQ_SDR_WHITE_LINEAR;
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* The numerator is clamped at `0` so that signals below `linear_to_PQ(0)` -- which the standard's
|
|
18
|
+
* curve never produces, but quantization and interpolation both do -- decode to black instead of
|
|
19
|
+
* `NaN` from a negative base under a fractional exponent.
|
|
20
|
+
*
|
|
21
|
+
* @param {number} pq PQ signal, in `[0, 1]`
|
|
22
|
+
* @returns {number} linear light, `1.0` = 10000 nits
|
|
23
|
+
* @see linear_to_PQ
|
|
24
|
+
*
|
|
25
|
+
* @author Alex Goldring
|
|
26
|
+
* @copyright Company Named Limited (c) 2026
|
|
27
|
+
*/
|
|
28
|
+
export function PQ_to_linear(pq) {
|
|
29
|
+
const y = Math.pow(pq, PQ_M2_INV);
|
|
30
|
+
|
|
31
|
+
const numerator = Math.max(y - PQ_C1, 0);
|
|
32
|
+
const denominator = PQ_C2 - PQ_C3 * y;
|
|
33
|
+
|
|
34
|
+
return Math.pow(numerator / denominator, PQ_M1_INV);
|
|
35
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SMPTE ST 2084 OETF: encode a linear light value as a PQ signal.
|
|
3
|
+
*
|
|
4
|
+
* NORMALIZATION: the input is in PQ's own scale, where `1.0` is {@link PQ_MAX_NITS} (10000 nits) --
|
|
5
|
+
* not the engine's, where `1.0` is {@link SDR_WHITE_NITS} (203 nits). Scale an engine-normalized
|
|
6
|
+
* value by {@link PQ_SDR_WHITE_LINEAR} first:
|
|
7
|
+
*
|
|
8
|
+
* ```js
|
|
9
|
+
* const pq = linear_to_PQ(engine_linear * PQ_SDR_WHITE_LINEAR);
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* Keeping the transfer function free of any white-point convention is what makes it and
|
|
13
|
+
* {@link PQ_to_linear} an exact inverse pair; a scale folded into one side but not the other is the
|
|
14
|
+
* classic way this round-trip ends up off by a factor of ~49.
|
|
15
|
+
*
|
|
16
|
+
* Negative input is clamped to `0`, since `Math.pow` of a negative base to a fractional exponent is
|
|
17
|
+
* `NaN` and a linear light value below zero has no PQ encoding to give.
|
|
18
|
+
*
|
|
19
|
+
* @param {number} linear linear light, `1.0` = 10000 nits
|
|
20
|
+
* @returns {number} PQ signal, in `[0, 1]`
|
|
21
|
+
* @see PQ_to_linear
|
|
22
|
+
*
|
|
23
|
+
* @author Alex Goldring
|
|
24
|
+
* @copyright Company Named Limited (c) 2026
|
|
25
|
+
*/
|
|
26
|
+
export function linear_to_PQ(linear: number): number;
|
|
27
|
+
//# sourceMappingURL=linear_to_PQ.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linear_to_PQ.d.ts","sourceRoot":"","sources":["../../../../../src/core/color/PQ/linear_to_PQ.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qCAPW,MAAM,GACJ,MAAM,CAUlB"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { PQ_C1, PQ_C2, PQ_C3, PQ_M1, PQ_M2 } from "./PQ_constants.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SMPTE ST 2084 OETF: encode a linear light value as a PQ signal.
|
|
5
|
+
*
|
|
6
|
+
* NORMALIZATION: the input is in PQ's own scale, where `1.0` is {@link PQ_MAX_NITS} (10000 nits) --
|
|
7
|
+
* not the engine's, where `1.0` is {@link SDR_WHITE_NITS} (203 nits). Scale an engine-normalized
|
|
8
|
+
* value by {@link PQ_SDR_WHITE_LINEAR} first:
|
|
9
|
+
*
|
|
10
|
+
* ```js
|
|
11
|
+
* const pq = linear_to_PQ(engine_linear * PQ_SDR_WHITE_LINEAR);
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* Keeping the transfer function free of any white-point convention is what makes it and
|
|
15
|
+
* {@link PQ_to_linear} an exact inverse pair; a scale folded into one side but not the other is the
|
|
16
|
+
* classic way this round-trip ends up off by a factor of ~49.
|
|
17
|
+
*
|
|
18
|
+
* Negative input is clamped to `0`, since `Math.pow` of a negative base to a fractional exponent is
|
|
19
|
+
* `NaN` and a linear light value below zero has no PQ encoding to give.
|
|
20
|
+
*
|
|
21
|
+
* @param {number} linear linear light, `1.0` = 10000 nits
|
|
22
|
+
* @returns {number} PQ signal, in `[0, 1]`
|
|
23
|
+
* @see PQ_to_linear
|
|
24
|
+
*
|
|
25
|
+
* @author Alex Goldring
|
|
26
|
+
* @copyright Company Named Limited (c) 2026
|
|
27
|
+
*/
|
|
28
|
+
export function linear_to_PQ(linear) {
|
|
29
|
+
const y = Math.pow(Math.max(linear, 0), PQ_M1);
|
|
30
|
+
|
|
31
|
+
return Math.pow((PQ_C1 + PQ_C2 * y) / (1 + PQ_C3 * y), PQ_M2);
|
|
32
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The first face on `edge_id`'s radial cycle that is not `face_id`, or {@link NULL_POINTER} when the
|
|
3
|
+
* edge has no such face.
|
|
4
|
+
*
|
|
5
|
+
* On a manifold interior edge -- two faces -- that is unambiguously "the face on the other side", which
|
|
6
|
+
* is what callers walking across a mesh want. The other configurations are worth being explicit about,
|
|
7
|
+
* because the answer is only interesting in the first case:
|
|
8
|
+
*
|
|
9
|
+
* - a wire edge (no faces) or a boundary edge whose one face IS `face_id`: {@link NULL_POINTER};
|
|
10
|
+
* - a non-manifold edge (three faces or more): whichever of the others the radial base pointer
|
|
11
|
+
* happens to reach first, which a caller cannot predict. Use {@link bt_edge_count_faces} first if
|
|
12
|
+
* that distinction matters;
|
|
13
|
+
* - an edge that `face_id` uses twice: the walk skips both of its loops and reports a genuine
|
|
14
|
+
* neighbour, rather than reporting `face_id` back.
|
|
15
|
+
*
|
|
16
|
+
* Passing a `face_id` that is not on the edge at all is not detected -- it simply reports the edge's
|
|
17
|
+
* first face. That matches {@link bt_edge_other_vertex}, whose contract is the same shape.
|
|
18
|
+
*
|
|
19
|
+
* The walk is bounded by the loop pool's size, so a corrupted radial cycle cannot hang the caller; a
|
|
20
|
+
* cycle that long is broken, and {@link bt_mesh_validate} will say where.
|
|
21
|
+
*
|
|
22
|
+
* @param {BinaryTopology} mesh
|
|
23
|
+
* @param {number} edge_id
|
|
24
|
+
* @param {number} face_id the face already known to sit on this edge
|
|
25
|
+
* @returns {number} face id, or {@link NULL_POINTER}
|
|
26
|
+
* @see bt_edge_other_vertex
|
|
27
|
+
*
|
|
28
|
+
* @author Alex Goldring
|
|
29
|
+
* @copyright Company Named Limited (c) 2026
|
|
30
|
+
*/
|
|
31
|
+
export function bt_edge_other_face(mesh: BinaryTopology, edge_id: number, face_id: number): number;
|
|
32
|
+
//# sourceMappingURL=bt_edge_other_face.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bt_edge_other_face.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/geom/3d/topology/struct/binary/query/bt_edge_other_face.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,kEARW,MAAM,WACN,MAAM,GACJ,MAAM,CAgClB"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { NULL_POINTER } from "../BinaryTopology.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The first face on `edge_id`'s radial cycle that is not `face_id`, or {@link NULL_POINTER} when the
|
|
5
|
+
* edge has no such face.
|
|
6
|
+
*
|
|
7
|
+
* On a manifold interior edge -- two faces -- that is unambiguously "the face on the other side", which
|
|
8
|
+
* is what callers walking across a mesh want. The other configurations are worth being explicit about,
|
|
9
|
+
* because the answer is only interesting in the first case:
|
|
10
|
+
*
|
|
11
|
+
* - a wire edge (no faces) or a boundary edge whose one face IS `face_id`: {@link NULL_POINTER};
|
|
12
|
+
* - a non-manifold edge (three faces or more): whichever of the others the radial base pointer
|
|
13
|
+
* happens to reach first, which a caller cannot predict. Use {@link bt_edge_count_faces} first if
|
|
14
|
+
* that distinction matters;
|
|
15
|
+
* - an edge that `face_id` uses twice: the walk skips both of its loops and reports a genuine
|
|
16
|
+
* neighbour, rather than reporting `face_id` back.
|
|
17
|
+
*
|
|
18
|
+
* Passing a `face_id` that is not on the edge at all is not detected -- it simply reports the edge's
|
|
19
|
+
* first face. That matches {@link bt_edge_other_vertex}, whose contract is the same shape.
|
|
20
|
+
*
|
|
21
|
+
* The walk is bounded by the loop pool's size, so a corrupted radial cycle cannot hang the caller; a
|
|
22
|
+
* cycle that long is broken, and {@link bt_mesh_validate} will say where.
|
|
23
|
+
*
|
|
24
|
+
* @param {BinaryTopology} mesh
|
|
25
|
+
* @param {number} edge_id
|
|
26
|
+
* @param {number} face_id the face already known to sit on this edge
|
|
27
|
+
* @returns {number} face id, or {@link NULL_POINTER}
|
|
28
|
+
* @see bt_edge_other_vertex
|
|
29
|
+
*
|
|
30
|
+
* @author Alex Goldring
|
|
31
|
+
* @copyright Company Named Limited (c) 2026
|
|
32
|
+
*/
|
|
33
|
+
export function bt_edge_other_face(mesh, edge_id, face_id) {
|
|
34
|
+
const start = mesh.edge_read_loop(edge_id);
|
|
35
|
+
|
|
36
|
+
if (start === NULL_POINTER) {
|
|
37
|
+
// wire edge, no faces at all
|
|
38
|
+
return NULL_POINTER;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const limit = mesh.loops.size;
|
|
42
|
+
|
|
43
|
+
let loop = start;
|
|
44
|
+
let visited = 0;
|
|
45
|
+
|
|
46
|
+
do {
|
|
47
|
+
const face = mesh.loop_read_face(loop);
|
|
48
|
+
|
|
49
|
+
if (face !== face_id) {
|
|
50
|
+
return face;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
visited++;
|
|
54
|
+
|
|
55
|
+
loop = mesh.loop_read_radial_next(loop);
|
|
56
|
+
} while (loop !== start && loop !== NULL_POINTER && visited <= limit);
|
|
57
|
+
|
|
58
|
+
return NULL_POINTER;
|
|
59
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The corner of `face_id` that lies on neither end of `edge_id` -- the apex of a triangle over that
|
|
3
|
+
* edge -- or {@link NULL_POINTER} when the face has no such corner.
|
|
4
|
+
*
|
|
5
|
+
* The edge is read for its two endpoints only; it is never required to be one of the face's own edges.
|
|
6
|
+
* A caller that hands over an unrelated edge therefore gets the face's first corner rather than an
|
|
7
|
+
* error, the same contract {@link bt_edge_other_vertex} carries.
|
|
8
|
+
*
|
|
9
|
+
* "Third" names the intended use, not a precondition: the face loop is walked to its end, so a polygon
|
|
10
|
+
* with more corners reports the first one clear of the edge. {@link NULL_POINTER} comes back for an
|
|
11
|
+
* face with no loop, and for a face all of whose corners sit on the edge -- a degenerate triangle with a
|
|
12
|
+
* repeated vertex, which {@link bt_mesh_validate} counts as a defect.
|
|
13
|
+
*
|
|
14
|
+
* The walk is bounded by the loop pool's size, so a corrupted face cycle cannot hang the caller.
|
|
15
|
+
*
|
|
16
|
+
* @param {BinaryTopology} mesh
|
|
17
|
+
* @param {number} face_id
|
|
18
|
+
* @param {number} edge_id
|
|
19
|
+
* @returns {number} vertex id, or {@link NULL_POINTER}
|
|
20
|
+
* @see bt_edge_read_diamond for both apexes of an interior edge, with winding respected
|
|
21
|
+
*
|
|
22
|
+
* @author Alex Goldring
|
|
23
|
+
* @copyright Company Named Limited (c) 2026
|
|
24
|
+
*/
|
|
25
|
+
export function bt_face_third_vertex(mesh: BinaryTopology, face_id: number, edge_id: number): number;
|
|
26
|
+
//# sourceMappingURL=bt_face_third_vertex.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bt_face_third_vertex.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/geom/3d/topology/struct/binary/query/bt_face_third_vertex.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,oEARW,MAAM,WACN,MAAM,GACJ,MAAM,CAkClB"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { NULL_POINTER } from "../BinaryTopology.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The corner of `face_id` that lies on neither end of `edge_id` -- the apex of a triangle over that
|
|
5
|
+
* edge -- or {@link NULL_POINTER} when the face has no such corner.
|
|
6
|
+
*
|
|
7
|
+
* The edge is read for its two endpoints only; it is never required to be one of the face's own edges.
|
|
8
|
+
* A caller that hands over an unrelated edge therefore gets the face's first corner rather than an
|
|
9
|
+
* error, the same contract {@link bt_edge_other_vertex} carries.
|
|
10
|
+
*
|
|
11
|
+
* "Third" names the intended use, not a precondition: the face loop is walked to its end, so a polygon
|
|
12
|
+
* with more corners reports the first one clear of the edge. {@link NULL_POINTER} comes back for an
|
|
13
|
+
* face with no loop, and for a face all of whose corners sit on the edge -- a degenerate triangle with a
|
|
14
|
+
* repeated vertex, which {@link bt_mesh_validate} counts as a defect.
|
|
15
|
+
*
|
|
16
|
+
* The walk is bounded by the loop pool's size, so a corrupted face cycle cannot hang the caller.
|
|
17
|
+
*
|
|
18
|
+
* @param {BinaryTopology} mesh
|
|
19
|
+
* @param {number} face_id
|
|
20
|
+
* @param {number} edge_id
|
|
21
|
+
* @returns {number} vertex id, or {@link NULL_POINTER}
|
|
22
|
+
* @see bt_edge_read_diamond for both apexes of an interior edge, with winding respected
|
|
23
|
+
*
|
|
24
|
+
* @author Alex Goldring
|
|
25
|
+
* @copyright Company Named Limited (c) 2026
|
|
26
|
+
*/
|
|
27
|
+
export function bt_face_third_vertex(mesh, face_id, edge_id) {
|
|
28
|
+
const start = mesh.face_read_loop(face_id);
|
|
29
|
+
|
|
30
|
+
if (start === NULL_POINTER) {
|
|
31
|
+
return NULL_POINTER;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const edge_v1 = mesh.edge_read_vertex1(edge_id);
|
|
35
|
+
const edge_v2 = mesh.edge_read_vertex2(edge_id);
|
|
36
|
+
|
|
37
|
+
const limit = mesh.loops.size;
|
|
38
|
+
|
|
39
|
+
let loop = start;
|
|
40
|
+
let visited = 0;
|
|
41
|
+
|
|
42
|
+
do {
|
|
43
|
+
const vertex = mesh.loop_read_vertex(loop);
|
|
44
|
+
|
|
45
|
+
if (vertex !== edge_v1 && vertex !== edge_v2) {
|
|
46
|
+
return vertex;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
visited++;
|
|
50
|
+
|
|
51
|
+
loop = mesh.loop_read_next(loop);
|
|
52
|
+
} while (loop !== start && loop !== NULL_POINTER && visited <= limit);
|
|
53
|
+
|
|
54
|
+
return NULL_POINTER;
|
|
55
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"populateEngineSerializationRegistry.d.ts","sourceRoot":"","sources":["../../../../../src/engine/ecs/storage/populateEngineSerializationRegistry.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"populateEngineSerializationRegistry.d.ts","sourceRoot":"","sources":["../../../../../src/engine/ecs/storage/populateEngineSerializationRegistry.js"],"names":[],"mappings":"AAqKA;;;;;;;;GAQG;AACH,iGA8FC"}
|