import { VecN } from '../math/vecn.js'; export interface HyperplaneSlice4Options { /** Hyperplane normal in R^4 (normalized internally). */ normal: VecN | ArrayLike; /** Signed distance of the hyperplane from the origin along the normal. */ offset?: number; } export type SliceFrameUpdatePolicy = 'continuous' | 'canonical'; export interface HyperplaneSlice4SetNormalOptions { /** * `continuous` transports the preceding display basis into the new * hyperplane. `canonical` recomputes the deterministic axis-based frame. */ readonly frame?: SliceFrameUpdatePolicy; } /** Optional source-edge and interpolation data for every emitted slice vertex. */ export interface SliceVertexProvenanceBuffers { /** Packed source vertex pairs, two entries per emitted vertex. */ readonly edgeVertices: Uint32Array; /** `p = from + t * (to - from)`, one entry per emitted vertex. */ readonly edgeParameters: Float64Array; } /** * An affine hyperplane in ℝ⁴, `{ x : ⟨normal, x⟩ = offset }`, together with * an orthonormal basis of the hyperplane used as the display frame: sliced * geometry is expressed in these 3 in-plane coordinates and rendered * directly as 3D. * * 4D-specific for now (the slice of ℝ⁴ is the only one that is itself a * renderable 3-flat); the N-parameterized generalization arrives with * chained slicing. */ export declare class HyperplaneSlice4 { readonly normal: VecN; offset: number; /** Rows: 3 orthonormal in-plane basis vectors (each length 4). */ readonly basis: [Float64Array, Float64Array, Float64Array]; constructor({ normal, offset }: HyperplaneSlice4Options); /** * Slice orthogonal to a coordinate axis (default: w). Axis indices are * `0=x`, `1=y`, `2=z`, `3=w`; for hiddenAxis 3 the display frame is exactly * x, y, z. */ static axisAligned(hiddenAxis?: number, offset?: number): HyperplaneSlice4; /** * Reorients the hyperplane. The normal is normalized and the in-plane * display basis recomputed **in place**, so render products holding a * reference to `normal` or `basis` see the new frame on their next update. */ setNormal(normal: VecN | ArrayLike, { frame }?: HyperplaneSlice4SetNormalOptions): this; signedDistance(x0: number, x1: number, x2: number, x3: number): number; /** * Orthogonally project an ambient R4 point into this slice's 3D display * chart while retaining the discarded normal component explicitly. * * For a point on the hyperplane, `embedPoint(result.coordinates)` recovers * the input. For an arbitrary point, add * `result.signedDistance * normal` to that embedded point to reconstruct it. */ projectPointToChart(point: ArrayLike): { /** Coordinates of the point's orthogonal projection in the slice basis. */ readonly coordinates: [number, number, number]; /** Signed ambient distance from the point to this hyperplane. */ readonly signedDistance: number; }; /** Embed one point from this slice's 3D display frame back into ambient R4. */ embedPoint(point: ArrayLike): [number, number, number, number]; } /** Construction parameters for a {@link HyperplaneSliceN}. */ export interface HyperplaneSliceNOptions { /** Hyperplane normal; its length fixes the ambient dimension. */ readonly normal: VecN | ArrayLike; /** Signed distance of the hyperplane from the origin along the normal. */ readonly offset?: number; } /** * An affine hyperplane in ℝⁿ, `{ x : ⟨normal, x⟩ = offset }`, with an * orthonormal basis of that hyperplane as its chart: a section expressed in * these `n - 1` in-plane coordinates is an intersection of the ambient set, * not a projection of it. * * A section and a projection lose different things, and conflating them is the * misreading this class exists to prevent. A projection is many-to-one — several * ambient points share one image, so an image point does not name a source * point. A section is injective on what it keeps: every chart point came from * exactly one ambient point, the one lying in the hyperplane. What a section * loses is *dimension* — everything off the plane is absent rather than * flattened onto it. * * `ambientDim` is inferred from the normal rather than passed separately, so * there is no second source of truth to disagree with it. * * @example * A chart in R5, and the two directions a point travels through it. The chart * is injective on the hyperplane: a point already lying in it round-trips * exactly, while an off-plane point keeps its distance explicitly rather than * being silently flattened: * ```ts * const slice = HyperplaneSliceN.axisAligned(5, 4, 0.25); * * slice.ambientDim; // 5 * slice.chartDim; // 4 — one less, because a hyperplane is codimension one * * const onPlane = [1, 2, 3, 4, 0.25]; * const chart = slice.projectPointToChart(onPlane); * chart.signedDistance; // 0 — it is in the hyperplane * slice.embedPoint(chart.coordinates); // back to [1, 2, 3, 4, 0.25] * * const offPlane = [1, 2, 3, 4, 1.25]; * const away = slice.projectPointToChart(offPlane); * away.signedDistance; // 1 — kept, not discarded * away.coordinates.join(); // '1,2,3,4' — the same chart point as above * ``` */ export declare class HyperplaneSliceN { /** Dimension of the space the hyperplane lives in, inferred from the normal. */ readonly ambientDim: number; /** `ambientDim - 1`: a hyperplane has codimension one. */ readonly chartDim: number; /** Unit normal. Reorient it through {@link setNormal}, never by writing it. */ readonly normal: VecN; /** Signed distance of the hyperplane from the origin along the normal. */ offset: number; /** `chartDim` orthonormal in-plane rows, each of length `ambientDim`. */ readonly basis: readonly Float64Array[]; /** * Builds the chart from a normal, whose length fixes the ambient dimension. * * The frame is the canonical one: reproducible from the normal alone. Use * {@link setNormal} with `frame: 'continuous'` to reorient without snapping. * * @param options - The hyperplane's normal direction and its signed offset * from the origin along that direction. */ constructor({ normal, offset }: HyperplaneSliceNOptions); /** * Slice orthogonal to a coordinate axis. The chart is then exactly the * remaining axes, in ascending order. */ static axisAligned(ambientDim: number, hiddenAxis: number, offset?: number): HyperplaneSliceN; /** * Reorients the hyperplane, keeping its ambient dimension. The normal is * normalized and the chart basis recomputed **in place**, so a consumer * holding `normal` or a `basis` row sees the new frame on its next update. * * The two policies are different answers, not a default and a fallback: * `canonical` is reproducible from the normal alone, while `continuous` * depends on the frame it came from and is what a slowly rotating hyperplane * wants so its chart does not snap. * * The policy is passed directly rather than in an options bag, which is the * one place this class deliberately reads differently from * {@link HyperplaneSlice4}: there is exactly one choice to make, and a bag * whose only member is that choice adds a name a caller has to learn without * telling them anything. `HyperplaneSlice4.setNormal` keeps its bag unchanged. * * @param normal - The new normal; its dimension must match this chart's. * @param frame - How to choose the reoriented chart basis. */ setNormal(normal: VecN | ArrayLike, frame?: SliceFrameUpdatePolicy): this; /** Signed ambient distance from `point` to the hyperplane. */ signedDistance(point: ArrayLike): number; /** * Orthogonally project an ambient point into the chart, keeping the discarded * normal component explicitly. * * For a point on the hyperplane, `embedPoint(result.coordinates)` recovers the * input. For any other point, add `result.signedDistance * normal` to that * embedded point to reconstruct it — nothing is lost silently. */ projectPointToChart(point: ArrayLike): { /** `chartDim` coordinates of the point's orthogonal projection. */ readonly coordinates: readonly number[]; /** Signed ambient distance from the point to the hyperplane. */ readonly signedDistance: number; }; /** Embed one chart point back into ambient space. */ embedPoint(point: ArrayLike): number[]; } /** * Marching tetrahedra in ℝ⁴: intersects tetrahedral 3-cells with a * hyperplane, emitting a triangle-soup cross-section surface as **ambient * 4D points** (all lying in the hyperplane). Use this form when the * section should be re-projected like any other 4D geometry — e.g. * rendering the cut inside a perspective projection; use * `sliceTetrahedra` for the section in the slice's own 3D display frame. * * Degeneracy policy: signed distances within `epsilon` of the hyperplane * snap to zero and count as non-negative (canonical tie-break), so * on-plane vertices interpolate exactly to themselves and cells lying * entirely in the hyperplane are suppressed rather than emitted twice. * Triangle winding is not globally consistent — render double-sided. * * This R4 stream is deliberately unchanged, and it is **not** the oriented * path. Within one tetrahedron its quad fan happens to be coherently wound, but * the class is a historical artefact of the fan order rather than a promise: * nothing here says which way a section faces, and the two triangles of one * quad carry no relationship to a neighbouring cell's. When a caller needs an * orientation it can reason with — a section that is the oriented boundary of * each parent's below-plane region, so that reversing the normal reverses the * facing and oriented area or flux integrals accumulate instead of cancelling — * use {@link sectionSimplexGroupN}, which states exactly what it promises on * its `cells` field. That function supersedes this one for new work; this pair * remains because existing R4 buffers depend on its byte layout. * * @param worldPositions packed 4D vertex coordinates (post-transform) * @param tets flat tetra vertex indices (4 per cell) * @param slice the hyperplane * @param outPositions output for packed 4D triangle vertices; must hold at * least `(tets.length / 4) * 24` floats (2 triangles × * 3 vertices × 4 coords per tetra worst case) * @param outProvenance optional per-triangle provenance: the source tetra * index (position in `tets` / 4) of each emitted * triangle; must hold `(tets.length / 4) * 2` entries * @param outVertexProvenance optional source edge and interpolation parameter * for every emitted vertex; buffers must hold six * vertices per source tetrahedron * @returns number of vertices written (a multiple of 3) */ export declare function sliceTetrahedraAmbient(worldPositions: Float64Array, tets: Uint32Array, slice: HyperplaneSlice4, outPositions: Float64Array, epsilon?: number, outProvenance?: Uint32Array, outVertexProvenance?: SliceVertexProvenanceBuffers): number; /** * Marching tetrahedra with output in the slice's own 3D display frame: * each ambient crossing point is expressed in the hyperplane's orthonormal * basis, ready for direct 3D rendering. Same degeneracy policy, winding * caveat, and output layout contract as `sliceTetrahedraAmbient`, but 3 floats * per vertex (buffer must hold `(tets.length / 4) * 18`). For an oriented * section in any dimension, see {@link sectionSimplexGroupN}. */ export declare function sliceTetrahedra(worldPositions: Float64Array, tets: Uint32Array, slice: HyperplaneSlice4, outPositions: Float32Array, epsilon?: number, outProvenance?: Uint32Array, outVertexProvenance?: SliceVertexProvenanceBuffers): number; //# sourceMappingURL=slice.d.ts.map