diff --git a/README.md b/README.md index 63e6263..c5fad92 100644 --- a/README.md +++ b/README.md @@ -158,5 +158,12 @@ dimensions, strides, and allocations bounded. See [`crates/libremetaverse-imaging-skia/README.md`](crates/libremetaverse-imaging-skia/README.md) for native prerequisites, cache/source-build controls, licenses, and packaging details. Default workspace builds do not compile or link Skia. +Primitive profile and path meshing is implemented in native Rust from the +pinned `PrimMesher.cs` reference. It covers checked linear, flexible, and +circular extrusion; profile cuts and hollows; twist, taper, shear, skew, +radius, and revolutions; cap and side winding; normals, UVs, viewer faces, and +prim-face indexing. Geometry allocation and index arithmetic are bounded, and +identical finite inputs produce deterministic mesh output on every supported +platform. The controlled audit aggregates every expected failure by standardized C# member ID and rejects unrelated fixture, assertion, compile, or symbol errors. diff --git a/api/SHIM-COVERAGE.md b/api/SHIM-COVERAGE.md index c523751..fe89f76 100644 --- a/api/SHIM-COVERAGE.md +++ b/api/SHIM-COVERAGE.md @@ -8,7 +8,7 @@ Generated by `python3 tools/generate_api_shims.py`; do not edit by hand. | `LibreMetaverse.Imaging.Abstractions` | 3 | 20 | native implementation: 3 types / 20 members; no generated shims remain | | `LibreMetaverse.Imaging.Skia` | 1 | 3 | native implementation: 1 type / 3 members; no generated shims remain | | `LibreMetaverse.LslTools` | 164 | 768 | callable failure-only shim | -| `LibreMetaverse.PrimMesher` | 17 | 207 | callable failure-only shim | +| `LibreMetaverse.PrimMesher` | 17 | 207 | native implementation: 9 types / 155 members; remaining surface is callable failure-only shims | | `LibreMetaverse.RLV` | 28 | 499 | callable failure-only shim | | `LibreMetaverse.Rendering.MeshFoundry` | 1 | 14 | callable failure-only shim | | `LibreMetaverse.Rendering.Simple` | 1 | 6 | callable failure-only shim | diff --git a/crates/libremetaverse-prim-mesher/src/generated.rs b/crates/libremetaverse-prim-mesher/src/generated.rs index 9a415d7..5b4f608 100644 --- a/crates/libremetaverse-prim-mesher/src/generated.rs +++ b/crates/libremetaverse-prim-mesher/src/generated.rs @@ -6,128 +6,35 @@ #![allow(non_snake_case)] /// C# type: `T:LibreMetaverse.PrimMesher.Coord`. -pub struct Coord { - /// C# member: `F:LibreMetaverse.PrimMesher.Coord.X`. - pub x: f32, - /// C# member: `F:LibreMetaverse.PrimMesher.Coord.Y`. - pub y: f32, - /// C# member: `F:LibreMetaverse.PrimMesher.Coord.Z`. - pub z: f32, -} -impl Coord { - /// C# member: `M:LibreMetaverse.PrimMesher.Coord.#ctor(System.Single,System.Single,System.Single)`. - pub fn new(x: f32, y: f32, z: f32) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.PrimMesher.Coord.#ctor(System.Single,System.Single,System.Single)", - ) - } - /// C# member: `M:LibreMetaverse.PrimMesher.Coord.Cross(LibreMetaverse.PrimMesher.Coord,LibreMetaverse.PrimMesher.Coord)`. - pub fn cross( - c1: libremetaverse_prim_mesher::Coord, - c2: libremetaverse_prim_mesher::Coord, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.PrimMesher.Coord.Cross(LibreMetaverse.PrimMesher.Coord,LibreMetaverse.PrimMesher.Coord)", - ) - } - /// C# member: `M:LibreMetaverse.PrimMesher.Coord.Invert`. - pub fn invert(&mut self) -> Result { - libremetaverse_types::not_implemented("M:LibreMetaverse.PrimMesher.Coord.Invert") - } - /// C# member: `M:LibreMetaverse.PrimMesher.Coord.Length`. - pub fn length(&self) -> Result { - libremetaverse_types::not_implemented("M:LibreMetaverse.PrimMesher.Coord.Length") - } - /// C# member: `M:LibreMetaverse.PrimMesher.Coord.Normalize`. - pub fn normalize(&mut self) -> Result { - libremetaverse_types::not_implemented("M:LibreMetaverse.PrimMesher.Coord.Normalize") - } - /// C# member: `M:LibreMetaverse.PrimMesher.Coord.ToString`. - pub fn to_string(&self) -> String { - libremetaverse_types::unimplemented_api!("M:LibreMetaverse.PrimMesher.Coord.ToString") - } - /// C# member: `M:LibreMetaverse.PrimMesher.Coord.op_Addition(LibreMetaverse.PrimMesher.Coord,LibreMetaverse.PrimMesher.Coord)`. - pub fn add( - v: libremetaverse_prim_mesher::Coord, - a: libremetaverse_prim_mesher::Coord, - ) -> libremetaverse_prim_mesher::Coord { - libremetaverse_types::unimplemented_api!( - "M:LibreMetaverse.PrimMesher.Coord.op_Addition(LibreMetaverse.PrimMesher.Coord,LibreMetaverse.PrimMesher.Coord)" - ) - } - /// C# member: `M:LibreMetaverse.PrimMesher.Coord.op_Multiply(LibreMetaverse.PrimMesher.Coord,LibreMetaverse.PrimMesher.Coord)`. - pub fn mul_with_coord_coord( - v: libremetaverse_prim_mesher::Coord, - m: libremetaverse_prim_mesher::Coord, - ) -> libremetaverse_prim_mesher::Coord { - libremetaverse_types::unimplemented_api!( - "M:LibreMetaverse.PrimMesher.Coord.op_Multiply(LibreMetaverse.PrimMesher.Coord,LibreMetaverse.PrimMesher.Coord)" - ) - } - /// C# member: `M:LibreMetaverse.PrimMesher.Coord.op_Multiply(LibreMetaverse.PrimMesher.Coord,LibreMetaverse.PrimMesher.Quat)`. - pub fn mul_with_coord_quat( - v: libremetaverse_prim_mesher::Coord, - q: libremetaverse_prim_mesher::Quat, - ) -> libremetaverse_prim_mesher::Coord { - libremetaverse_types::unimplemented_api!( - "M:LibreMetaverse.PrimMesher.Coord.op_Multiply(LibreMetaverse.PrimMesher.Coord,LibreMetaverse.PrimMesher.Quat)" - ) - } -} +/// C# member: `F:LibreMetaverse.PrimMesher.Coord.X`. +/// C# member: `F:LibreMetaverse.PrimMesher.Coord.Y`. +/// C# member: `F:LibreMetaverse.PrimMesher.Coord.Z`. +/// C# member: `M:LibreMetaverse.PrimMesher.Coord.#ctor(System.Single,System.Single,System.Single)`. +/// C# member: `M:LibreMetaverse.PrimMesher.Coord.Cross(LibreMetaverse.PrimMesher.Coord,LibreMetaverse.PrimMesher.Coord)`. +/// C# member: `M:LibreMetaverse.PrimMesher.Coord.Invert`. +/// C# member: `M:LibreMetaverse.PrimMesher.Coord.Length`. +/// C# member: `M:LibreMetaverse.PrimMesher.Coord.Normalize`. +/// C# member: `M:LibreMetaverse.PrimMesher.Coord.ToString`. +/// C# member: `M:LibreMetaverse.PrimMesher.Coord.op_Addition(LibreMetaverse.PrimMesher.Coord,LibreMetaverse.PrimMesher.Coord)`. +/// C# member: `M:LibreMetaverse.PrimMesher.Coord.op_Multiply(LibreMetaverse.PrimMesher.Coord,LibreMetaverse.PrimMesher.Coord)`. +/// C# member: `M:LibreMetaverse.PrimMesher.Coord.op_Multiply(LibreMetaverse.PrimMesher.Coord,LibreMetaverse.PrimMesher.Quat)`. +pub use crate::prim_mesher::Coord; /// C# type: `T:LibreMetaverse.PrimMesher.Face`. -pub struct Face { - /// C# member: `F:LibreMetaverse.PrimMesher.Face.n1`. - pub n1: i32, - /// C# member: `F:LibreMetaverse.PrimMesher.Face.n2`. - pub n2: i32, - /// C# member: `F:LibreMetaverse.PrimMesher.Face.n3`. - pub n3: i32, - /// C# member: `F:LibreMetaverse.PrimMesher.Face.primFace`. - pub prim_face: i32, - /// C# member: `F:LibreMetaverse.PrimMesher.Face.uv1`. - pub uv1: i32, - /// C# member: `F:LibreMetaverse.PrimMesher.Face.uv2`. - pub uv2: i32, - /// C# member: `F:LibreMetaverse.PrimMesher.Face.uv3`. - pub uv3: i32, - /// C# member: `F:LibreMetaverse.PrimMesher.Face.v1`. - pub v1: i32, - /// C# member: `F:LibreMetaverse.PrimMesher.Face.v2`. - pub v2: i32, - /// C# member: `F:LibreMetaverse.PrimMesher.Face.v3`. - pub v3: i32, -} -impl Face { - /// C# member: `M:LibreMetaverse.PrimMesher.Face.#ctor(System.Int32,System.Int32,System.Int32)`. - pub fn new_with_int32_int32_int32(v1: i32, v2: i32, v3: i32) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.PrimMesher.Face.#ctor(System.Int32,System.Int32,System.Int32)", - ) - } - /// C# member: `M:LibreMetaverse.PrimMesher.Face.#ctor(System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32)`. - pub fn new_with_int32_int32_int32_int32_int32_int32( - v1: i32, - v2: i32, - v3: i32, - n1: i32, - n2: i32, - n3: i32, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.PrimMesher.Face.#ctor(System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32)", - ) - } - /// C# member: `M:LibreMetaverse.PrimMesher.Face.SurfaceNormal(System.Collections.Generic.List{LibreMetaverse.PrimMesher.Coord})`. - pub fn surface_normal( - &mut self, - coord_list: Vec, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.PrimMesher.Face.SurfaceNormal(System.Collections.Generic.List{LibreMetaverse.PrimMesher.Coord})", - ) - } -} +/// C# member: `F:LibreMetaverse.PrimMesher.Face.n1`. +/// C# member: `F:LibreMetaverse.PrimMesher.Face.n2`. +/// C# member: `F:LibreMetaverse.PrimMesher.Face.n3`. +/// C# member: `F:LibreMetaverse.PrimMesher.Face.primFace`. +/// C# member: `F:LibreMetaverse.PrimMesher.Face.uv1`. +/// C# member: `F:LibreMetaverse.PrimMesher.Face.uv2`. +/// C# member: `F:LibreMetaverse.PrimMesher.Face.uv3`. +/// C# member: `F:LibreMetaverse.PrimMesher.Face.v1`. +/// C# member: `F:LibreMetaverse.PrimMesher.Face.v2`. +/// C# member: `F:LibreMetaverse.PrimMesher.Face.v3`. +/// C# member: `M:LibreMetaverse.PrimMesher.Face.#ctor(System.Int32,System.Int32,System.Int32)`. +/// C# member: `M:LibreMetaverse.PrimMesher.Face.#ctor(System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32)`. +/// C# member: `M:LibreMetaverse.PrimMesher.Face.SurfaceNormal(System.Collections.Generic.List{LibreMetaverse.PrimMesher.Coord})`. +pub use crate::prim_mesher::Face; /// C# type: `T:LibreMetaverse.PrimMesher.ObjMesh`. pub struct ObjMesh { @@ -166,73 +73,34 @@ impl ObjMesh { } /// C# type: `T:LibreMetaverse.PrimMesher.Path`. -pub struct Path { - /// C# member: `F:LibreMetaverse.PrimMesher.Path.dimpleBegin`. - pub dimple_begin: f32, - /// C# member: `F:LibreMetaverse.PrimMesher.Path.dimpleEnd`. - pub dimple_end: f32, - /// C# member: `F:LibreMetaverse.PrimMesher.Path.holeSizeX`. - pub hole_size_x: f32, - /// C# member: `F:LibreMetaverse.PrimMesher.Path.holeSizeY`. - pub hole_size_y: f32, - /// C# member: `F:LibreMetaverse.PrimMesher.Path.pathCutBegin`. - pub path_cut_begin: f32, - /// C# member: `F:LibreMetaverse.PrimMesher.Path.pathCutEnd`. - pub path_cut_end: f32, - /// C# member: `F:LibreMetaverse.PrimMesher.Path.pathNodes`. - pub path_nodes: Vec, - /// C# member: `F:LibreMetaverse.PrimMesher.Path.radius`. - pub radius: f32, - /// C# member: `F:LibreMetaverse.PrimMesher.Path.revolutions`. - pub revolutions: f32, - /// C# member: `F:LibreMetaverse.PrimMesher.Path.skew`. - pub skew: f32, - /// C# member: `F:LibreMetaverse.PrimMesher.Path.stepsPerRevolution`. - pub steps_per_revolution: i32, - /// C# member: `F:LibreMetaverse.PrimMesher.Path.taperX`. - pub taper_x: f32, - /// C# member: `F:LibreMetaverse.PrimMesher.Path.taperY`. - pub taper_y: f32, - /// C# member: `F:LibreMetaverse.PrimMesher.Path.topShearX`. - pub top_shear_x: f32, - /// C# member: `F:LibreMetaverse.PrimMesher.Path.topShearY`. - pub top_shear_y: f32, - /// C# member: `F:LibreMetaverse.PrimMesher.Path.twistBegin`. - pub twist_begin: f32, - /// C# member: `F:LibreMetaverse.PrimMesher.Path.twistEnd`. - pub twist_end: f32, -} -impl Path { - /// C# member: `M:LibreMetaverse.PrimMesher.Path.#ctor`. - pub fn new() -> Result { - libremetaverse_types::not_implemented("M:LibreMetaverse.PrimMesher.Path.#ctor") - } - /// C# member: `M:LibreMetaverse.PrimMesher.Path.Create(LibreMetaverse.PrimMesher.PathType,System.Int32)`. - pub fn create( - &self, - path_type: libremetaverse_prim_mesher::PathType, - steps: i32, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.PrimMesher.Path.Create(LibreMetaverse.PrimMesher.PathType,System.Int32)", - ) - } -} +/// C# member: `F:LibreMetaverse.PrimMesher.Path.dimpleBegin`. +/// C# member: `F:LibreMetaverse.PrimMesher.Path.dimpleEnd`. +/// C# member: `F:LibreMetaverse.PrimMesher.Path.holeSizeX`. +/// C# member: `F:LibreMetaverse.PrimMesher.Path.holeSizeY`. +/// C# member: `F:LibreMetaverse.PrimMesher.Path.pathCutBegin`. +/// C# member: `F:LibreMetaverse.PrimMesher.Path.pathCutEnd`. +/// C# member: `F:LibreMetaverse.PrimMesher.Path.pathNodes`. +/// C# member: `F:LibreMetaverse.PrimMesher.Path.radius`. +/// C# member: `F:LibreMetaverse.PrimMesher.Path.revolutions`. +/// C# member: `F:LibreMetaverse.PrimMesher.Path.skew`. +/// C# member: `F:LibreMetaverse.PrimMesher.Path.stepsPerRevolution`. +/// C# member: `F:LibreMetaverse.PrimMesher.Path.taperX`. +/// C# member: `F:LibreMetaverse.PrimMesher.Path.taperY`. +/// C# member: `F:LibreMetaverse.PrimMesher.Path.topShearX`. +/// C# member: `F:LibreMetaverse.PrimMesher.Path.topShearY`. +/// C# member: `F:LibreMetaverse.PrimMesher.Path.twistBegin`. +/// C# member: `F:LibreMetaverse.PrimMesher.Path.twistEnd`. +/// C# member: `M:LibreMetaverse.PrimMesher.Path.#ctor`. +/// C# member: `M:LibreMetaverse.PrimMesher.Path.Create(LibreMetaverse.PrimMesher.PathType,System.Int32)`. +pub use crate::prim_mesher::Path; /// C# type: `T:LibreMetaverse.PrimMesher.PathNode`. -pub struct PathNode { - /// C# member: `F:LibreMetaverse.PrimMesher.PathNode.percentOfPath`. - pub percent_of_path: f32, - /// C# member: `F:LibreMetaverse.PrimMesher.PathNode.position`. - pub position: libremetaverse_prim_mesher::Coord, - /// C# member: `F:LibreMetaverse.PrimMesher.PathNode.rotation`. - pub rotation: libremetaverse_prim_mesher::Quat, - /// C# member: `F:LibreMetaverse.PrimMesher.PathNode.xScale`. - pub x_scale: f32, - /// C# member: `F:LibreMetaverse.PrimMesher.PathNode.yScale`. - pub y_scale: f32, -} -impl PathNode {} +/// C# member: `F:LibreMetaverse.PrimMesher.PathNode.percentOfPath`. +/// C# member: `F:LibreMetaverse.PrimMesher.PathNode.position`. +/// C# member: `F:LibreMetaverse.PrimMesher.PathNode.rotation`. +/// C# member: `F:LibreMetaverse.PrimMesher.PathNode.xScale`. +/// C# member: `F:LibreMetaverse.PrimMesher.PathNode.yScale`. +pub use crate::prim_mesher::PathNode; /// C# type: `T:LibreMetaverse.PrimMesher.PathType`. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] @@ -247,372 +115,97 @@ pub enum PathType { } /// C# type: `T:LibreMetaverse.PrimMesher.PrimMesh`. -pub struct PrimMesh { - /// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.calcVertexNormals`. - pub calc_vertex_normals: bool, - /// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.coords`. - pub coords: Vec, - /// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.dimpleBegin`. - pub dimple_begin: f32, - /// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.dimpleEnd`. - pub dimple_end: f32, - /// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.errorMessage`. - pub error_message: String, - /// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.faces`. - pub faces: Vec, - /// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.holeSizeX`. - pub hole_size_x: f32, - /// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.holeSizeY`. - pub hole_size_y: f32, - /// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.normals`. - pub normals: Vec, - /// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.numPrimFaces`. - pub num_prim_faces: i32, - /// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.pathCutBegin`. - pub path_cut_begin: f32, - /// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.pathCutEnd`. - pub path_cut_end: f32, - /// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.radius`. - pub radius: f32, - /// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.revolutions`. - pub revolutions: f32, - /// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.skew`. - pub skew: f32, - /// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.sphereMode`. - pub sphere_mode: bool, - /// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.stepsPerRevolution`. - pub steps_per_revolution: i32, - /// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.taperX`. - pub taper_x: f32, - /// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.taperY`. - pub taper_y: f32, - /// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.topShearX`. - pub top_shear_x: f32, - /// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.topShearY`. - pub top_shear_y: f32, - /// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.twistBegin`. - pub twist_begin: i32, - /// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.twistEnd`. - pub twist_end: i32, - /// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.viewerFaces`. - pub viewer_faces: Vec, - /// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.viewerMode`. - pub viewer_mode: bool, -} -impl PrimMesh { - /// C# member: `M:LibreMetaverse.PrimMesher.PrimMesh.#ctor(System.Int32,System.Single,System.Single,System.Single,System.Int32)`. - pub fn new( - sides: i32, - profile_start: f32, - profile_end: f32, - hollow: f32, - hollow_sides: i32, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.PrimMesher.PrimMesh.#ctor(System.Int32,System.Single,System.Single,System.Single,System.Int32)", - ) - } - /// C# member: `M:LibreMetaverse.PrimMesher.PrimMesh.AddPos(System.Single,System.Single,System.Single)`. - pub fn add_pos(&mut self, x: f32, y: f32, z: f32) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.PrimMesher.PrimMesh.AddPos(System.Single,System.Single,System.Single)", - ) - } - /// C# member: `M:LibreMetaverse.PrimMesher.PrimMesh.AddRot(LibreMetaverse.PrimMesher.Quat)`. - pub fn add_rot(&mut self, q: libremetaverse_prim_mesher::Quat) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.PrimMesher.PrimMesh.AddRot(LibreMetaverse.PrimMesher.Quat)", - ) - } - /// C# member: `M:LibreMetaverse.PrimMesher.PrimMesh.CalcNormals`. - pub fn calc_normals(&mut self) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented("M:LibreMetaverse.PrimMesher.PrimMesh.CalcNormals") - } - /// C# member: `M:LibreMetaverse.PrimMesher.PrimMesh.Copy`. - pub fn copy(&self) -> Result { - libremetaverse_types::not_implemented("M:LibreMetaverse.PrimMesher.PrimMesh.Copy") - } - /// C# member: `M:LibreMetaverse.PrimMesher.PrimMesh.DumpRaw(System.String,System.String,System.String)`. - pub fn dump_raw(&self, path: String, name: String, title: String) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.PrimMesher.PrimMesh.DumpRaw(System.String,System.String,System.String)", - ) - } - /// C# member: `M:LibreMetaverse.PrimMesher.PrimMesh.Extrude(LibreMetaverse.PrimMesher.PathType)`. - pub fn extrude( - &mut self, - path_type: libremetaverse_prim_mesher::PathType, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.PrimMesher.PrimMesh.Extrude(LibreMetaverse.PrimMesher.PathType)", - ) - } - /// C# member: `M:LibreMetaverse.PrimMesher.PrimMesh.GetVertexIndexer`. - pub fn get_vertex_indexer( - &self, - ) -> Result, crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.PrimMesher.PrimMesh.GetVertexIndexer", - ) - } - /// C# member: `M:LibreMetaverse.PrimMesher.PrimMesh.ParamsToDisplayString`. - pub fn params_to_display_string(&self) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.PrimMesher.PrimMesh.ParamsToDisplayString", - ) - } - /// C# member: `M:LibreMetaverse.PrimMesher.PrimMesh.Scale(System.Single,System.Single,System.Single)`. - pub fn scale(&mut self, x: f32, y: f32, z: f32) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.PrimMesher.PrimMesh.Scale(System.Single,System.Single,System.Single)", - ) - } - /// C# member: `M:LibreMetaverse.PrimMesher.PrimMesh.SurfaceNormal(System.Int32)`. - pub fn surface_normal( - &self, - face_index: i32, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.PrimMesher.PrimMesh.SurfaceNormal(System.Int32)", - ) - } - /// C# member: `P:LibreMetaverse.PrimMesher.PrimMesh.HasHollow`. - pub fn has_hollow(&self) -> bool { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.PrimMesher.PrimMesh.HasHollow") - } - /// Setter for C# member: `P:LibreMetaverse.PrimMesher.PrimMesh.HasHollow`. - pub fn set_has_hollow(&mut self, value: bool) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.PrimMesher.PrimMesh.HasHollow") - } - /// C# member: `P:LibreMetaverse.PrimMesher.PrimMesh.HasProfileCut`. - pub fn has_profile_cut(&self) -> bool { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.PrimMesher.PrimMesh.HasProfileCut" - ) - } - /// Setter for C# member: `P:LibreMetaverse.PrimMesher.PrimMesh.HasProfileCut`. - pub fn set_has_profile_cut(&mut self, value: bool) { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.PrimMesher.PrimMesh.HasProfileCut" - ) - } - /// C# member: `P:LibreMetaverse.PrimMesher.PrimMesh.ProfileHollowFaceNumber`. - pub fn profile_hollow_face_number(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.PrimMesher.PrimMesh.ProfileHollowFaceNumber" - ) - } - /// Setter for C# member: `P:LibreMetaverse.PrimMesher.PrimMesh.ProfileHollowFaceNumber`. - pub fn set_profile_hollow_face_number(&mut self, value: i32) { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.PrimMesher.PrimMesh.ProfileHollowFaceNumber" - ) - } - /// C# member: `P:LibreMetaverse.PrimMesher.PrimMesh.ProfileOuterFaceNumber`. - pub fn profile_outer_face_number(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.PrimMesher.PrimMesh.ProfileOuterFaceNumber" - ) - } - /// Setter for C# member: `P:LibreMetaverse.PrimMesher.PrimMesh.ProfileOuterFaceNumber`. - pub fn set_profile_outer_face_number(&mut self, value: i32) { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.PrimMesher.PrimMesh.ProfileOuterFaceNumber" - ) - } -} +/// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.calcVertexNormals`. +/// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.coords`. +/// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.dimpleBegin`. +/// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.dimpleEnd`. +/// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.errorMessage`. +/// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.faces`. +/// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.holeSizeX`. +/// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.holeSizeY`. +/// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.normals`. +/// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.numPrimFaces`. +/// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.pathCutBegin`. +/// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.pathCutEnd`. +/// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.radius`. +/// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.revolutions`. +/// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.skew`. +/// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.sphereMode`. +/// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.stepsPerRevolution`. +/// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.taperX`. +/// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.taperY`. +/// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.topShearX`. +/// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.topShearY`. +/// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.twistBegin`. +/// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.twistEnd`. +/// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.viewerFaces`. +/// C# member: `F:LibreMetaverse.PrimMesher.PrimMesh.viewerMode`. +/// C# member: `M:LibreMetaverse.PrimMesher.PrimMesh.#ctor(System.Int32,System.Single,System.Single,System.Single,System.Int32)`. +/// C# member: `M:LibreMetaverse.PrimMesher.PrimMesh.AddPos(System.Single,System.Single,System.Single)`. +/// C# member: `M:LibreMetaverse.PrimMesher.PrimMesh.AddRot(LibreMetaverse.PrimMesher.Quat)`. +/// C# member: `M:LibreMetaverse.PrimMesher.PrimMesh.CalcNormals`. +/// C# member: `M:LibreMetaverse.PrimMesher.PrimMesh.Copy`. +/// C# member: `M:LibreMetaverse.PrimMesher.PrimMesh.DumpRaw(System.String,System.String,System.String)`. +/// C# member: `M:LibreMetaverse.PrimMesher.PrimMesh.Extrude(LibreMetaverse.PrimMesher.PathType)`. +/// C# member: `M:LibreMetaverse.PrimMesher.PrimMesh.GetVertexIndexer`. +/// C# member: `M:LibreMetaverse.PrimMesher.PrimMesh.ParamsToDisplayString`. +/// C# member: `M:LibreMetaverse.PrimMesher.PrimMesh.Scale(System.Single,System.Single,System.Single)`. +/// C# member: `M:LibreMetaverse.PrimMesher.PrimMesh.SurfaceNormal(System.Int32)`. +/// C# member: `P:LibreMetaverse.PrimMesher.PrimMesh.HasHollow`. +/// C# member: `P:LibreMetaverse.PrimMesher.PrimMesh.HasProfileCut`. +/// C# member: `P:LibreMetaverse.PrimMesher.PrimMesh.ProfileHollowFaceNumber`. +/// C# member: `P:LibreMetaverse.PrimMesher.PrimMesh.ProfileOuterFaceNumber`. +pub use crate::prim_mesher::PrimMesh; /// C# type: `T:LibreMetaverse.PrimMesher.Profile`. -pub struct Profile { - /// C# member: `F:LibreMetaverse.PrimMesher.Profile.bottomFaceNumber`. - pub bottom_face_number: i32, - /// C# member: `F:LibreMetaverse.PrimMesher.Profile.calcVertexNormals`. - pub calc_vertex_normals: bool, - /// C# member: `F:LibreMetaverse.PrimMesher.Profile.coords`. - pub coords: Vec, - /// C# member: `F:LibreMetaverse.PrimMesher.Profile.cut1CoordIndices`. - pub cut1_coord_indices: Vec, - /// C# member: `F:LibreMetaverse.PrimMesher.Profile.cut2CoordIndices`. - pub cut2_coord_indices: Vec, - /// C# member: `F:LibreMetaverse.PrimMesher.Profile.cutNormal1`. - pub cut_normal1: libremetaverse_prim_mesher::Coord, - /// C# member: `F:LibreMetaverse.PrimMesher.Profile.cutNormal2`. - pub cut_normal2: libremetaverse_prim_mesher::Coord, - /// C# member: `F:LibreMetaverse.PrimMesher.Profile.errorMessage`. - pub error_message: String, - /// C# member: `F:LibreMetaverse.PrimMesher.Profile.faceNormal`. - pub face_normal: libremetaverse_prim_mesher::Coord, - /// C# member: `F:LibreMetaverse.PrimMesher.Profile.faceNumbers`. - pub face_numbers: Vec, - /// C# member: `F:LibreMetaverse.PrimMesher.Profile.faceUVs`. - pub face_u_vs: Vec, - /// C# member: `F:LibreMetaverse.PrimMesher.Profile.faces`. - pub faces: Vec, - /// C# member: `F:LibreMetaverse.PrimMesher.Profile.hollowCoordIndices`. - pub hollow_coord_indices: Vec, - /// C# member: `F:LibreMetaverse.PrimMesher.Profile.hollowFaceNumber`. - pub hollow_face_number: i32, - /// C# member: `F:LibreMetaverse.PrimMesher.Profile.numHollowVerts`. - pub num_hollow_verts: i32, - /// C# member: `F:LibreMetaverse.PrimMesher.Profile.numOuterVerts`. - pub num_outer_verts: i32, - /// C# member: `F:LibreMetaverse.PrimMesher.Profile.numPrimFaces`. - pub num_prim_faces: i32, - /// C# member: `F:LibreMetaverse.PrimMesher.Profile.outerCoordIndices`. - pub outer_coord_indices: Vec, - /// C# member: `F:LibreMetaverse.PrimMesher.Profile.outerFaceNumber`. - pub outer_face_number: i32, - /// C# member: `F:LibreMetaverse.PrimMesher.Profile.us`. - pub us: Vec, - /// C# member: `F:LibreMetaverse.PrimMesher.Profile.vertexNormals`. - pub vertex_normals: Vec, -} -impl Profile { - /// C# member: `M:LibreMetaverse.PrimMesher.Profile.#ctor`. - pub fn new_with_constructor() -> Result { - libremetaverse_types::not_implemented("M:LibreMetaverse.PrimMesher.Profile.#ctor") - } - /// C# member: `M:LibreMetaverse.PrimMesher.Profile.#ctor(System.Int32,System.Single,System.Single,System.Single,System.Int32,System.Boolean,System.Boolean)`. - pub fn new_with_int32_single_single_single_int32_boolean_boolean( - sides: i32, - profile_start: f32, - profile_end: f32, - hollow: f32, - hollow_sides: i32, - create_faces: bool, - calc_vertex_normals: bool, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.PrimMesher.Profile.#ctor(System.Int32,System.Single,System.Single,System.Single,System.Int32,System.Boolean,System.Boolean)", - ) - } - /// C# member: `M:LibreMetaverse.PrimMesher.Profile.AddPos(LibreMetaverse.PrimMesher.Coord)`. - pub fn add_pos_with_coord( - &self, - v: libremetaverse_prim_mesher::Coord, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.PrimMesher.Profile.AddPos(LibreMetaverse.PrimMesher.Coord)", - ) - } - /// C# member: `M:LibreMetaverse.PrimMesher.Profile.AddPos(System.Single,System.Single,System.Single)`. - pub fn add_pos_with_single_single_single( - &self, - x: f32, - y: f32, - z: f32, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.PrimMesher.Profile.AddPos(System.Single,System.Single,System.Single)", - ) - } - /// C# member: `M:LibreMetaverse.PrimMesher.Profile.AddRot(LibreMetaverse.PrimMesher.Quat)`. - pub fn add_rot(&self, q: libremetaverse_prim_mesher::Quat) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.PrimMesher.Profile.AddRot(LibreMetaverse.PrimMesher.Quat)", - ) - } - /// C# member: `M:LibreMetaverse.PrimMesher.Profile.AddValue2FaceNormalIndices(System.Int32)`. - pub fn add_value2_face_normal_indices(&self, num: i32) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.PrimMesher.Profile.AddValue2FaceNormalIndices(System.Int32)", - ) - } - /// C# member: `M:LibreMetaverse.PrimMesher.Profile.AddValue2FaceVertexIndices(System.Int32)`. - pub fn add_value2_face_vertex_indices(&self, num: i32) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.PrimMesher.Profile.AddValue2FaceVertexIndices(System.Int32)", - ) - } - /// C# member: `M:LibreMetaverse.PrimMesher.Profile.Copy`. - pub fn copy_with_method(&self) -> Result { - libremetaverse_types::not_implemented("M:LibreMetaverse.PrimMesher.Profile.Copy") - } - /// C# member: `M:LibreMetaverse.PrimMesher.Profile.Copy(System.Boolean)`. - pub fn copy_with_boolean( - &self, - need_faces: bool, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.PrimMesher.Profile.Copy(System.Boolean)", - ) - } - /// C# member: `M:LibreMetaverse.PrimMesher.Profile.DumpRaw(System.String,System.String,System.String)`. - pub fn dump_raw(&self, path: String, name: String, title: String) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.PrimMesher.Profile.DumpRaw(System.String,System.String,System.String)", - ) - } - /// C# member: `M:LibreMetaverse.PrimMesher.Profile.FlipNormals`. - pub fn flip_normals(&self) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented("M:LibreMetaverse.PrimMesher.Profile.FlipNormals") - } - /// C# member: `M:LibreMetaverse.PrimMesher.Profile.MakeFaceUVs`. - pub fn make_face_u_vs(&self) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented("M:LibreMetaverse.PrimMesher.Profile.MakeFaceUVs") - } - /// C# member: `M:LibreMetaverse.PrimMesher.Profile.Scale(System.Single,System.Single)`. - pub fn scale(&self, x: f32, y: f32) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.PrimMesher.Profile.Scale(System.Single,System.Single)", - ) - } -} +/// C# member: `F:LibreMetaverse.PrimMesher.Profile.bottomFaceNumber`. +/// C# member: `F:LibreMetaverse.PrimMesher.Profile.calcVertexNormals`. +/// C# member: `F:LibreMetaverse.PrimMesher.Profile.coords`. +/// C# member: `F:LibreMetaverse.PrimMesher.Profile.cut1CoordIndices`. +/// C# member: `F:LibreMetaverse.PrimMesher.Profile.cut2CoordIndices`. +/// C# member: `F:LibreMetaverse.PrimMesher.Profile.cutNormal1`. +/// C# member: `F:LibreMetaverse.PrimMesher.Profile.cutNormal2`. +/// C# member: `F:LibreMetaverse.PrimMesher.Profile.errorMessage`. +/// C# member: `F:LibreMetaverse.PrimMesher.Profile.faceNormal`. +/// C# member: `F:LibreMetaverse.PrimMesher.Profile.faceNumbers`. +/// C# member: `F:LibreMetaverse.PrimMesher.Profile.faceUVs`. +/// C# member: `F:LibreMetaverse.PrimMesher.Profile.faces`. +/// C# member: `F:LibreMetaverse.PrimMesher.Profile.hollowCoordIndices`. +/// C# member: `F:LibreMetaverse.PrimMesher.Profile.hollowFaceNumber`. +/// C# member: `F:LibreMetaverse.PrimMesher.Profile.numHollowVerts`. +/// C# member: `F:LibreMetaverse.PrimMesher.Profile.numOuterVerts`. +/// C# member: `F:LibreMetaverse.PrimMesher.Profile.numPrimFaces`. +/// C# member: `F:LibreMetaverse.PrimMesher.Profile.outerCoordIndices`. +/// C# member: `F:LibreMetaverse.PrimMesher.Profile.outerFaceNumber`. +/// C# member: `F:LibreMetaverse.PrimMesher.Profile.us`. +/// C# member: `F:LibreMetaverse.PrimMesher.Profile.vertexNormals`. +/// C# member: `M:LibreMetaverse.PrimMesher.Profile.#ctor`. +/// C# member: `M:LibreMetaverse.PrimMesher.Profile.#ctor(System.Int32,System.Single,System.Single,System.Single,System.Int32,System.Boolean,System.Boolean)`. +/// C# member: `M:LibreMetaverse.PrimMesher.Profile.AddPos(LibreMetaverse.PrimMesher.Coord)`. +/// C# member: `M:LibreMetaverse.PrimMesher.Profile.AddPos(System.Single,System.Single,System.Single)`. +/// C# member: `M:LibreMetaverse.PrimMesher.Profile.AddRot(LibreMetaverse.PrimMesher.Quat)`. +/// C# member: `M:LibreMetaverse.PrimMesher.Profile.AddValue2FaceNormalIndices(System.Int32)`. +/// C# member: `M:LibreMetaverse.PrimMesher.Profile.AddValue2FaceVertexIndices(System.Int32)`. +/// C# member: `M:LibreMetaverse.PrimMesher.Profile.Copy`. +/// C# member: `M:LibreMetaverse.PrimMesher.Profile.Copy(System.Boolean)`. +/// C# member: `M:LibreMetaverse.PrimMesher.Profile.DumpRaw(System.String,System.String,System.String)`. +/// C# member: `M:LibreMetaverse.PrimMesher.Profile.FlipNormals`. +/// C# member: `M:LibreMetaverse.PrimMesher.Profile.MakeFaceUVs`. +/// C# member: `M:LibreMetaverse.PrimMesher.Profile.Scale(System.Single,System.Single)`. +pub use crate::prim_mesher::Profile; /// C# type: `T:LibreMetaverse.PrimMesher.Quat`. -pub struct Quat { - /// C# member: `F:LibreMetaverse.PrimMesher.Quat.W`. - pub w: f32, - /// C# member: `F:LibreMetaverse.PrimMesher.Quat.X`. - pub x: f32, - /// C# member: `F:LibreMetaverse.PrimMesher.Quat.Y`. - pub y: f32, - /// C# member: `F:LibreMetaverse.PrimMesher.Quat.Z`. - pub z: f32, -} -impl Quat { - /// C# member: `M:LibreMetaverse.PrimMesher.Quat.#ctor(LibreMetaverse.PrimMesher.Coord,System.Single)`. - pub fn new_with_coord_single( - axis: libremetaverse_prim_mesher::Coord, - angle: f32, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.PrimMesher.Quat.#ctor(LibreMetaverse.PrimMesher.Coord,System.Single)", - ) - } - /// C# member: `M:LibreMetaverse.PrimMesher.Quat.#ctor(System.Single,System.Single,System.Single,System.Single)`. - pub fn new_with_single_single_single_single( - x: f32, - y: f32, - z: f32, - w: f32, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.PrimMesher.Quat.#ctor(System.Single,System.Single,System.Single,System.Single)", - ) - } - /// C# member: `M:LibreMetaverse.PrimMesher.Quat.Length`. - pub fn length(&self) -> Result { - libremetaverse_types::not_implemented("M:LibreMetaverse.PrimMesher.Quat.Length") - } - /// C# member: `M:LibreMetaverse.PrimMesher.Quat.Normalize`. - pub fn normalize(&mut self) -> Result { - libremetaverse_types::not_implemented("M:LibreMetaverse.PrimMesher.Quat.Normalize") - } - /// C# member: `M:LibreMetaverse.PrimMesher.Quat.ToString`. - pub fn to_string(&self) -> String { - libremetaverse_types::unimplemented_api!("M:LibreMetaverse.PrimMesher.Quat.ToString") - } - /// C# member: `M:LibreMetaverse.PrimMesher.Quat.op_Multiply(LibreMetaverse.PrimMesher.Quat,LibreMetaverse.PrimMesher.Quat)`. - pub fn mul( - q1: libremetaverse_prim_mesher::Quat, - q2: libremetaverse_prim_mesher::Quat, - ) -> libremetaverse_prim_mesher::Quat { - libremetaverse_types::unimplemented_api!( - "M:LibreMetaverse.PrimMesher.Quat.op_Multiply(LibreMetaverse.PrimMesher.Quat,LibreMetaverse.PrimMesher.Quat)" - ) - } -} +/// C# member: `F:LibreMetaverse.PrimMesher.Quat.W`. +/// C# member: `F:LibreMetaverse.PrimMesher.Quat.X`. +/// C# member: `F:LibreMetaverse.PrimMesher.Quat.Y`. +/// C# member: `F:LibreMetaverse.PrimMesher.Quat.Z`. +/// C# member: `M:LibreMetaverse.PrimMesher.Quat.#ctor(LibreMetaverse.PrimMesher.Coord,System.Single)`. +/// C# member: `M:LibreMetaverse.PrimMesher.Quat.#ctor(System.Single,System.Single,System.Single,System.Single)`. +/// C# member: `M:LibreMetaverse.PrimMesher.Quat.Length`. +/// C# member: `M:LibreMetaverse.PrimMesher.Quat.Normalize`. +/// C# member: `M:LibreMetaverse.PrimMesher.Quat.ToString`. +/// C# member: `M:LibreMetaverse.PrimMesher.Quat.op_Multiply(LibreMetaverse.PrimMesher.Quat,LibreMetaverse.PrimMesher.Quat)`. +pub use crate::prim_mesher::Quat; /// C# type: `T:LibreMetaverse.PrimMesher.SculptMap`. pub struct SculptMap { @@ -795,24 +388,11 @@ pub enum SculptMeshSculptType { } /// C# type: `T:LibreMetaverse.PrimMesher.UVCoord`. -pub struct UVCoord { - /// C# member: `F:LibreMetaverse.PrimMesher.UVCoord.U`. - pub u: f32, - /// C# member: `F:LibreMetaverse.PrimMesher.UVCoord.V`. - pub v: f32, -} -impl UVCoord { - /// C# member: `M:LibreMetaverse.PrimMesher.UVCoord.#ctor(System.Single,System.Single)`. - pub fn new(u: f32, v: f32) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.PrimMesher.UVCoord.#ctor(System.Single,System.Single)", - ) - } - /// C# member: `M:LibreMetaverse.PrimMesher.UVCoord.Flip`. - pub fn flip(&mut self) -> Result { - libremetaverse_types::not_implemented("M:LibreMetaverse.PrimMesher.UVCoord.Flip") - } -} +/// C# member: `F:LibreMetaverse.PrimMesher.UVCoord.U`. +/// C# member: `F:LibreMetaverse.PrimMesher.UVCoord.V`. +/// C# member: `M:LibreMetaverse.PrimMesher.UVCoord.#ctor(System.Single,System.Single)`. +/// C# member: `M:LibreMetaverse.PrimMesher.UVCoord.Flip`. +pub use crate::prim_mesher::UVCoord; /// C# type: `T:LibreMetaverse.PrimMesher.VertexIndexer`. pub struct VertexIndexer { @@ -839,66 +419,25 @@ impl VertexIndexer { } /// C# type: `T:LibreMetaverse.PrimMesher.ViewerFace`. -pub struct ViewerFace { - /// C# member: `F:LibreMetaverse.PrimMesher.ViewerFace.coordIndex1`. - pub coord_index1: i32, - /// C# member: `F:LibreMetaverse.PrimMesher.ViewerFace.coordIndex2`. - pub coord_index2: i32, - /// C# member: `F:LibreMetaverse.PrimMesher.ViewerFace.coordIndex3`. - pub coord_index3: i32, - /// C# member: `F:LibreMetaverse.PrimMesher.ViewerFace.n1`. - pub n1: libremetaverse_prim_mesher::Coord, - /// C# member: `F:LibreMetaverse.PrimMesher.ViewerFace.n2`. - pub n2: libremetaverse_prim_mesher::Coord, - /// C# member: `F:LibreMetaverse.PrimMesher.ViewerFace.n3`. - pub n3: libremetaverse_prim_mesher::Coord, - /// C# member: `F:LibreMetaverse.PrimMesher.ViewerFace.primFaceNumber`. - pub prim_face_number: i32, - /// C# member: `F:LibreMetaverse.PrimMesher.ViewerFace.uv1`. - pub uv1: libremetaverse_prim_mesher::UVCoord, - /// C# member: `F:LibreMetaverse.PrimMesher.ViewerFace.uv2`. - pub uv2: libremetaverse_prim_mesher::UVCoord, - /// C# member: `F:LibreMetaverse.PrimMesher.ViewerFace.uv3`. - pub uv3: libremetaverse_prim_mesher::UVCoord, - /// C# member: `F:LibreMetaverse.PrimMesher.ViewerFace.v1`. - pub v1: libremetaverse_prim_mesher::Coord, - /// C# member: `F:LibreMetaverse.PrimMesher.ViewerFace.v2`. - pub v2: libremetaverse_prim_mesher::Coord, - /// C# member: `F:LibreMetaverse.PrimMesher.ViewerFace.v3`. - pub v3: libremetaverse_prim_mesher::Coord, -} -impl ViewerFace { - /// C# member: `M:LibreMetaverse.PrimMesher.ViewerFace.#ctor(System.Int32)`. - pub fn new(prim_face_number: i32) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.PrimMesher.ViewerFace.#ctor(System.Int32)", - ) - } - /// C# member: `M:LibreMetaverse.PrimMesher.ViewerFace.AddPos(System.Single,System.Single,System.Single)`. - pub fn add_pos(&mut self, x: f32, y: f32, z: f32) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.PrimMesher.ViewerFace.AddPos(System.Single,System.Single,System.Single)", - ) - } - /// C# member: `M:LibreMetaverse.PrimMesher.ViewerFace.AddRot(LibreMetaverse.PrimMesher.Quat)`. - pub fn add_rot(&mut self, q: libremetaverse_prim_mesher::Quat) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.PrimMesher.ViewerFace.AddRot(LibreMetaverse.PrimMesher.Quat)", - ) - } - /// C# member: `M:LibreMetaverse.PrimMesher.ViewerFace.CalcSurfaceNormal`. - pub fn calc_surface_normal(&mut self) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.PrimMesher.ViewerFace.CalcSurfaceNormal", - ) - } - /// C# member: `M:LibreMetaverse.PrimMesher.ViewerFace.Scale(System.Single,System.Single,System.Single)`. - pub fn scale(&mut self, x: f32, y: f32, z: f32) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.PrimMesher.ViewerFace.Scale(System.Single,System.Single,System.Single)", - ) - } -} +/// C# member: `F:LibreMetaverse.PrimMesher.ViewerFace.coordIndex1`. +/// C# member: `F:LibreMetaverse.PrimMesher.ViewerFace.coordIndex2`. +/// C# member: `F:LibreMetaverse.PrimMesher.ViewerFace.coordIndex3`. +/// C# member: `F:LibreMetaverse.PrimMesher.ViewerFace.n1`. +/// C# member: `F:LibreMetaverse.PrimMesher.ViewerFace.n2`. +/// C# member: `F:LibreMetaverse.PrimMesher.ViewerFace.n3`. +/// C# member: `F:LibreMetaverse.PrimMesher.ViewerFace.primFaceNumber`. +/// C# member: `F:LibreMetaverse.PrimMesher.ViewerFace.uv1`. +/// C# member: `F:LibreMetaverse.PrimMesher.ViewerFace.uv2`. +/// C# member: `F:LibreMetaverse.PrimMesher.ViewerFace.uv3`. +/// C# member: `F:LibreMetaverse.PrimMesher.ViewerFace.v1`. +/// C# member: `F:LibreMetaverse.PrimMesher.ViewerFace.v2`. +/// C# member: `F:LibreMetaverse.PrimMesher.ViewerFace.v3`. +/// C# member: `M:LibreMetaverse.PrimMesher.ViewerFace.#ctor(System.Int32)`. +/// C# member: `M:LibreMetaverse.PrimMesher.ViewerFace.AddPos(System.Single,System.Single,System.Single)`. +/// C# member: `M:LibreMetaverse.PrimMesher.ViewerFace.AddRot(LibreMetaverse.PrimMesher.Quat)`. +/// C# member: `M:LibreMetaverse.PrimMesher.ViewerFace.CalcSurfaceNormal`. +/// C# member: `M:LibreMetaverse.PrimMesher.ViewerFace.Scale(System.Single,System.Single,System.Single)`. +pub use crate::prim_mesher::ViewerFace; /// C# type: `T:LibreMetaverse.PrimMesher.ViewerPolygon`. pub struct ViewerPolygon { diff --git a/crates/libremetaverse-prim-mesher/src/lib.rs b/crates/libremetaverse-prim-mesher/src/lib.rs index aada151..fbc76aa 100644 --- a/crates/libremetaverse-prim-mesher/src/lib.rs +++ b/crates/libremetaverse-prim-mesher/src/lib.rs @@ -3,6 +3,7 @@ extern crate self as libremetaverse_prim_mesher; mod generated; +mod prim_mesher; pub use generated::*; pub use libremetaverse_imaging as imaging; diff --git a/crates/libremetaverse-prim-mesher/src/prim_mesher.rs b/crates/libremetaverse-prim-mesher/src/prim_mesher.rs new file mode 100644 index 0000000..126c343 --- /dev/null +++ b/crates/libremetaverse-prim-mesher/src/prim_mesher.rs @@ -0,0 +1,2083 @@ +//! Native port of `PrimMesher.cs` profile and extrusion geometry. + +#![allow(clippy::cast_possible_truncation)] // Reference index and step calculations narrow explicitly. +#![allow(clippy::cast_precision_loss)] // C# geometry converts integer sides and steps to Single. +#![allow(clippy::cast_sign_loss)] // Bounds checks precede C# float-to-index conversions. +#![allow(clippy::approx_constant)] // Decimal compatibility constants are intentional. +#![allow(clippy::float_cmp)] // Exact parameter branches are part of the golden algorithm. +#![allow(clippy::inconsistent_struct_constructor)] // Field order is fixed by the public API map. +#![allow(clippy::inherent_to_string)] // The mapped C# methods are named ToString. +#![allow(clippy::inherent_to_string_shadow_display)] // Coord also needs Display for raw output. +#![allow(clippy::many_single_char_names)] // Vector and UV formulas follow the reference notation. +#![allow(clippy::missing_errors_doc)] // Result shapes are fixed by the compatibility mapping. +#![allow(clippy::must_use_candidate)] // Attributes are not part of the mapped C# surface. +#![allow(clippy::needless_pass_by_value)] // Owned value parameters preserve mapped signatures. +#![allow(clippy::should_implement_trait)] // Operator entry points have fixed generated names. +#![allow(clippy::similar_names)] // Profile start/stop and step names mirror the reference. +#![allow(clippy::struct_excessive_bools)] // PrimMesh exposes the reference mode flags. +#![allow(clippy::struct_field_names)] // Angle.angle is the corresponding C# field. +#![allow(clippy::too_many_arguments)] // Constructor and extrusion helpers mirror fixed APIs. +#![allow(clippy::too_many_lines)] // The extrusion sequence remains reviewable against C#. + +use crate::{Error, PathType, VertexIndexer, ViewerPolygon, ViewerVertex}; +use std::collections::HashMap; +use std::f32::consts::{PI, TAU}; +use std::fmt; +use std::fs::File; +use std::io::{BufWriter, Write}; +use std::path::PathBuf; + +const MAG_THRESHOLD: f32 = 0.000_000_1; +const MAX_PROFILE_VERTICES: usize = 65_536; +const MAX_PATH_NODES: usize = 65_536; +const MAX_MESH_VERTICES: usize = 16_777_216; + +#[derive(Clone, Copy, Debug, Default, PartialEq)] +pub struct Coord { + pub x: f32, + pub y: f32, + pub z: f32, +} + +impl Coord { + pub fn new(x: f32, y: f32, z: f32) -> Result { + Ok(Self { x, y, z }) + } + pub fn length(&self) -> Result { + Ok((self.x * self.x + self.y * self.y + self.z * self.z).sqrt()) + } + pub fn invert(&mut self) -> Result { + self.x = -self.x; + self.y = -self.y; + self.z = -self.z; + Ok(*self) + } + pub fn normalize(&mut self) -> Result { + let mag = self.length()?; + if mag > MAG_THRESHOLD { + let inverse = 1.0 / mag; + self.x *= inverse; + self.y *= inverse; + self.z *= inverse; + } else { + self.x = 0.0; + self.y = 0.0; + self.z = 0.0; + } + Ok(*self) + } + pub fn cross(c1: Self, c2: Self) -> Result { + Ok(Self::cross_raw(c1, c2)) + } + fn cross_raw(c1: Self, c2: Self) -> Self { + Self { + x: c1.y * c2.z - c2.y * c1.z, + y: c1.z * c2.x - c2.z * c1.x, + z: c1.x * c2.y - c2.x * c1.y, + } + } + pub fn add(v: Self, a: Self) -> Self { + Self { + x: v.x + a.x, + y: v.y + a.y, + z: v.z + a.z, + } + } + pub fn mul_with_coord_coord(v: Self, m: Self) -> Self { + Self { + x: v.x * m.x, + y: v.y * m.y, + z: v.z * m.z, + } + } + pub fn mul_with_coord_quat(v: Self, q: Quat) -> Self { + Self { + x: q.w * q.w * v.x + 2.0 * q.y * q.w * v.z - 2.0 * q.z * q.w * v.y + + q.x * q.x * v.x + + 2.0 * q.y * q.x * v.y + + 2.0 * q.z * q.x * v.z + - q.z * q.z * v.x + - q.y * q.y * v.x, + y: 2.0 * q.x * q.y * v.x + + q.y * q.y * v.y + + 2.0 * q.z * q.y * v.z + + 2.0 * q.w * q.z * v.x + - q.z * q.z * v.y + + q.w * q.w * v.y + - 2.0 * q.x * q.w * v.z + - q.x * q.x * v.y, + z: 2.0 * q.x * q.z * v.x + 2.0 * q.y * q.z * v.y + q.z * q.z * v.z + - 2.0 * q.w * q.y * v.x + - q.y * q.y * v.z + + 2.0 * q.w * q.x * v.y + - q.x * q.x * v.z + + q.w * q.w * v.z, + } + } +} + +impl fmt::Display for Coord { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{} {} {}", self.x, self.y, self.z) + } +} +impl Coord { + pub fn to_string(&self) -> String { + format!("{self}") + } +} + +#[derive(Clone, Copy, Debug, Default, PartialEq)] +pub struct Quat { + pub w: f32, + pub x: f32, + pub y: f32, + pub z: f32, +} +impl Quat { + pub fn new_with_single_single_single_single( + x: f32, + y: f32, + z: f32, + w: f32, + ) -> Result { + Ok(Self { x, y, z, w }) + } + pub fn new_with_coord_single(mut axis: Coord, mut angle: f32) -> Result { + axis.normalize()?; + angle *= 0.5; + let mut value = Self { + x: axis.x * angle.sin(), + y: axis.y * angle.sin(), + z: axis.z * angle.sin(), + w: angle.cos(), + }; + value.normalize()?; + Ok(value) + } + pub fn length(&self) -> Result { + Ok((self.x * self.x + self.y * self.y + self.z * self.z + self.w * self.w).sqrt()) + } + pub fn normalize(&mut self) -> Result { + let mag = self.length()?; + if mag > MAG_THRESHOLD { + let i = 1.0 / mag; + self.x *= i; + self.y *= i; + self.z *= i; + self.w *= i; + } else { + self.x = 0.0; + self.y = 0.0; + self.z = 0.0; + self.w = 1.0; + } + Ok(*self) + } + pub fn mul(q1: Self, q2: Self) -> Self { + Self { + x: q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y, + y: q1.w * q2.y - q1.x * q2.z + q1.y * q2.w + q1.z * q2.x, + z: q1.w * q2.z + q1.x * q2.y - q1.y * q2.x + q1.z * q2.w, + w: q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z, + } + } + pub fn to_string(&self) -> String { + format!( + "< X: {}, Y: {}, Z: {}, W: {}>", + self.x, self.y, self.z, self.w + ) + } +} + +#[derive(Clone, Copy, Debug, Default, PartialEq)] +pub struct UVCoord { + pub u: f32, + pub v: f32, +} +impl UVCoord { + pub fn new(u: f32, v: f32) -> Result { + Ok(Self { u, v }) + } + pub fn flip(&mut self) -> Result { + self.u = 1.0 - self.u; + self.v = 1.0 - self.v; + Ok(*self) + } +} + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub struct Face { + pub n1: i32, + pub n2: i32, + pub n3: i32, + pub prim_face: i32, + pub uv1: i32, + pub uv2: i32, + pub uv3: i32, + pub v1: i32, + pub v2: i32, + pub v3: i32, +} +impl Face { + fn vertices(v1: i32, v2: i32, v3: i32) -> Self { + Self { + v1, + v2, + v3, + ..Self::default() + } + } + pub fn new_with_int32_int32_int32(v1: i32, v2: i32, v3: i32) -> Result { + Ok(Self::vertices(v1, v2, v3)) + } + pub fn new_with_int32_int32_int32_int32_int32_int32( + v1: i32, + v2: i32, + v3: i32, + n1: i32, + n2: i32, + n3: i32, + ) -> Result { + Ok(Self { + v1, + v2, + v3, + n1, + n2, + n3, + ..Self::default() + }) + } + pub fn surface_normal(&mut self, coords: Vec) -> Result { + surface_normal_for(&coords, *self) + } +} + +#[derive(Clone, Copy, Debug, Default, PartialEq)] +pub struct ViewerFace { + pub coord_index1: i32, + pub coord_index2: i32, + pub coord_index3: i32, + pub n1: Coord, + pub n2: Coord, + pub n3: Coord, + pub prim_face_number: i32, + pub uv1: UVCoord, + pub uv2: UVCoord, + pub uv3: UVCoord, + pub v1: Coord, + pub v2: Coord, + pub v3: Coord, +} +impl ViewerFace { + pub fn new(prim_face_number: i32) -> Result { + Ok(Self { + prim_face_number, + coord_index1: -1, + coord_index2: -1, + coord_index3: -1, + ..Self::default() + }) + } + pub fn scale(&mut self, x: f32, y: f32, z: f32) -> Result<(), Error> { + let m = Coord { x, y, z }; + self.v1 = Coord::mul_with_coord_coord(self.v1, m); + self.v2 = Coord::mul_with_coord_coord(self.v2, m); + self.v3 = Coord::mul_with_coord_coord(self.v3, m); + Ok(()) + } + pub fn add_pos(&mut self, x: f32, y: f32, z: f32) -> Result<(), Error> { + let p = Coord { x, y, z }; + self.v1 = Coord::add(self.v1, p); + self.v2 = Coord::add(self.v2, p); + self.v3 = Coord::add(self.v3, p); + Ok(()) + } + pub fn add_rot(&mut self, q: Quat) -> Result<(), Error> { + self.v1 = Coord::mul_with_coord_quat(self.v1, q); + self.v2 = Coord::mul_with_coord_quat(self.v2, q); + self.v3 = Coord::mul_with_coord_quat(self.v3, q); + self.n1 = Coord::mul_with_coord_quat(self.n1, q); + self.n2 = Coord::mul_with_coord_quat(self.n2, q); + self.n3 = Coord::mul_with_coord_quat(self.n3, q); + Ok(()) + } + pub fn calc_surface_normal(&mut self) -> Result<(), Error> { + let e1 = Coord { + x: self.v2.x - self.v1.x, + y: self.v2.y - self.v1.y, + z: self.v2.z - self.v1.z, + }; + let e2 = Coord { + x: self.v3.x - self.v1.x, + y: self.v3.y - self.v1.y, + z: self.v3.z - self.v1.z, + }; + let mut n = Coord::cross_raw(e1, e2); + n.normalize()?; + self.n1 = n; + self.n2 = n; + self.n3 = n; + Ok(()) + } +} + +#[derive(Clone, Copy, Debug, Default)] +struct Angle { + angle: f32, + x: f32, + y: f32, +} + +fn make_angles(sides: i32, start: f32, stop: f32) -> Result<(Vec, Vec), Error> { + if sides < 1 || stop <= start || !start.is_finite() || !stop.is_finite() { + return Err(Error::Argument); + } + let sides_usize = usize::try_from(sides).map_err(|_| Error::Argument)?; + if sides_usize > MAX_PROFILE_VERTICES { + return Err(Error::Argument); + } + if matches!(sides, 3 | 4 | 24) { + return make_special_angles(sides, start, stop); + } + let step = TAU / (sides as f32); + let first = (start / step).floor() as i32; + let last = ((stop / step).ceil() as i32).min(sides); + let mut angles = + Vec::with_capacity(usize::try_from(last - first + 1).map_err(|_| Error::Argument)?); + for index in first..=last { + let a = (index as f32) * step; + angles.push(Angle { + angle: a, + x: a.cos(), + y: a.sin(), + }); + } + if start > angles.first().map_or(start, |a| a.angle) { + let first = angles.first().copied().ok_or(Error::Argument)?; + let second = angles.get(1).copied().ok_or(Error::Argument)?; + angles[0] = intersect_angle(first, second, start); + } + if angles.len() > 1 { + let end = angles.len() - 1; + if stop < angles[end].angle { + angles[end] = intersect_angle(angles[end - 1], angles[end], stop); + } + } + let normals = if sides < 5 { + angles + .iter() + .map(|a| { + let previous = a.angle - step * 0.5; + let mut n = Coord { + x: previous.cos(), + y: previous.sin(), + z: 0.0, + }; + let _ = n.normalize(); + n + }) + .collect() + } else { + Vec::new() + }; + Ok((angles, normals)) +} + +fn make_special_angles( + sides: i32, + start: f32, + stop: f32, +) -> Result<(Vec, Vec), Error> { + let start = start / TAU; + let stop = stop / TAU; + let source: Vec = (0..=sides) + .map(|index| { + let angle = (index as f32) / (sides as f32); + Angle { + angle, + x: (angle * TAU).cos(), + y: (angle * TAU).sin(), + } + }) + .collect(); + let start_index = (start * sides as f32) as usize; + let mut end_index = source.len() - 1; + if stop < 1.0 { + end_index = (stop * sides as f32) as usize + 1; + } + if end_index == start_index { + end_index += 1; + } + let mut angles = source + .get(start_index..=end_index) + .ok_or(Error::Argument)? + .to_vec(); + let normal_source: Vec = if sides < 5 { + (0..=sides) + .map(|index| { + let angle = ((index as f32) + 0.5) * TAU / sides as f32; + Coord { + x: angle.cos(), + y: angle.sin(), + z: 0.0, + } + }) + .collect() + } else { + Vec::new() + }; + let normals = if sides < 5 { + normal_source[start_index..=end_index].to_vec() + } else { + Vec::new() + }; + if start > 0.0 { + let first = angles[0]; + let second = angles[1]; + angles[0] = interpolate_angle(start, first, second); + } + if stop < 1.0 { + let last = angles.len() - 1; + angles[last] = interpolate_angle(stop, angles[last - 1], angles[last]); + } + Ok((angles, normals)) +} + +fn interpolate_angle(point: f32, first: Angle, second: Angle) -> Angle { + let ratio = (point - first.angle) / (second.angle - first.angle); + Angle { + angle: point, + x: first.x + ratio * (second.x - first.x), + y: first.y + ratio * (second.y - first.y), + } +} + +fn intersect_angle(p1: Angle, p2: Angle, angle: f32) -> Angle { + let x3 = 0.0_f64; + let y3 = 0.0_f64; + let x4 = f64::from(angle.cos()); + let y4 = f64::from(angle.sin()); + let x1 = f64::from(p1.x); + let y1 = f64::from(p1.y); + let x2 = f64::from(p2.x); + let y2 = f64::from(p2.y); + let denominator = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1); + if denominator == 0.0 { + return Angle { + angle, + x: p1.x, + y: p1.y, + }; + } + let numerator = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3); + let u = numerator / denominator; + Angle { + angle, + x: (x1 + u * (x2 - x1)) as f32, + y: (y1 + u * (y2 - y1)) as f32, + } +} + +fn idx(value: i32, len: usize) -> Result { + let value = usize::try_from(value).map_err(|_| Error::IndexOutOfRange)?; + if value >= len { + Err(Error::IndexOutOfRange) + } else { + Ok(value) + } +} +fn surface_normal_for(coords: &[Coord], face: Face) -> Result { + let c1 = coords[idx(face.v1, coords.len())?]; + let c2 = coords[idx(face.v2, coords.len())?]; + let c3 = coords[idx(face.v3, coords.len())?]; + let e1 = Coord { + x: c2.x - c1.x, + y: c2.y - c1.y, + z: c2.z - c1.z, + }; + let e2 = Coord { + x: c3.x - c1.x, + y: c3.y - c1.y, + z: c3.z - c1.z, + }; + let mut n = Coord::cross_raw(e1, e2); + n.normalize()?; + Ok(n) +} + +#[derive(Clone, Debug, Default)] +pub struct Profile { + pub bottom_face_number: i32, + pub calc_vertex_normals: bool, + pub coords: Vec, + pub cut1_coord_indices: Vec, + pub cut2_coord_indices: Vec, + pub cut_normal1: Coord, + pub cut_normal2: Coord, + pub error_message: String, + pub face_normal: Coord, + pub face_numbers: Vec, + pub face_u_vs: Vec, + pub faces: Vec, + pub hollow_coord_indices: Vec, + pub hollow_face_number: i32, + pub num_hollow_verts: i32, + pub num_outer_verts: i32, + pub num_prim_faces: i32, + pub outer_coord_indices: Vec, + pub outer_face_number: i32, + pub us: Vec, + pub vertex_normals: Vec, +} + +impl Profile { + pub fn new_with_constructor() -> Result { + Ok(Self { + face_normal: Coord { + x: 0.0, + y: 0.0, + z: 1.0, + }, + hollow_face_number: -1, + outer_face_number: -1, + ..Self::default() + }) + } + + #[allow(clippy::too_many_arguments)] + pub fn new_with_int32_single_single_single_int32_boolean_boolean( + sides: i32, + profile_start: f32, + profile_end: f32, + hollow: f32, + hollow_sides: i32, + create_faces: bool, + calc_vertex_normals: bool, + ) -> Result { + let mut value = Self::new_with_constructor()?; + value.calc_vertex_normals = calc_vertex_normals; + let (angles, outer_normals) = make_angles(sides, profile_start * TAU, profile_end * TAU)?; + let has_hollow = hollow > 0.0; + let has_cut = profile_start > 0.0 || profile_end < 1.0; + let simple = sides < 5 && !has_hollow && !has_cut; + let (x_scale, y_scale) = if sides == 4 { + (0.707_107, 0.707_107) + } else { + (0.5, 0.5) + }; + value.num_outer_verts = i32::try_from(angles.len()).map_err(|_| Error::Argument)?; + let hollow_angles = if has_hollow { + if hollow_sides == sides { + angles.clone() + } else { + make_angles(hollow_sides, profile_start * TAU, profile_end * TAU)?.0 + } + } else { + Vec::new() + }; + value.num_hollow_verts = i32::try_from(hollow_angles.len()).map_err(|_| Error::Argument)?; + let total = angles + .len() + .checked_add(hollow_angles.len()) + .and_then(|n| n.checked_add(1)) + .ok_or(Error::Argument)?; + if total > MAX_PROFILE_VERTICES { + return Err(Error::Argument); + } + + if !has_hollow && !simple { + value.coords.push(Coord::default()); + if calc_vertex_normals { + value.vertex_normals.push(value.face_normal); + } + value.us.push(0.0); + } + for (i, angle) in angles.iter().enumerate() { + value.coords.push(Coord { + x: angle.x * x_scale, + y: angle.y * y_scale, + z: 0.0, + }); + if calc_vertex_normals { + value + .outer_coord_indices + .push(i32::try_from(value.coords.len() - 1).map_err(|_| Error::Argument)?); + value.vertex_normals.push(if sides < 5 { + outer_normals.get(i).copied().unwrap_or(Coord { + x: angle.x, + y: angle.y, + z: 0.0, + }) + } else { + Coord { + x: angle.x, + y: angle.y, + z: 0.0, + } + }); + value.us.push(angle.angle); + } + if !has_hollow && !simple && create_faces && angle.angle > 0.0001 { + let index = i32::try_from(i).map_err(|_| Error::Argument)?; + value.faces.push(Face::vertices(0, index, index + 1)); + } + } + + if has_hollow { + let mut inner = Vec::with_capacity(hollow_angles.len()); + let mut inner_normals = Vec::with_capacity(hollow_angles.len()); + let mut inner_us = Vec::with_capacity(hollow_angles.len()); + for (i, angle) in hollow_angles.iter().enumerate() { + inner.push(Coord { + x: hollow * x_scale * angle.x, + y: hollow * y_scale * angle.y, + z: 0.0, + }); + if calc_vertex_normals { + let mut normal = if hollow_sides < 5 { + make_angles(hollow_sides, profile_start * TAU, profile_end * TAU)? + .1 + .get(i) + .copied() + .unwrap_or(Coord { + x: angle.x, + y: angle.y, + z: 0.0, + }) + } else { + Coord { + x: angle.x, + y: angle.y, + z: 0.0, + } + }; + normal.x = -normal.x; + normal.y = -normal.y; + normal.z = -normal.z; + inner_normals.push(normal); + inner_us.push(if hollow_sides == 4 { + angle.angle * hollow * 0.707_107 + } else { + angle.angle * hollow + }); + } + } + inner.reverse(); + inner_normals.reverse(); + inner_us.reverse(); + if create_faces { + triangulate_ring(&angles, &hollow_angles, &mut value.faces)?; + } + for coord in inner { + value.coords.push(coord); + if calc_vertex_normals { + value + .hollow_coord_indices + .push(i32::try_from(value.coords.len() - 1).map_err(|_| Error::Argument)?); + } + } + value.vertex_normals.extend(inner_normals); + value.us.extend(inner_us); + } + + if simple && create_faces { + if sides == 3 { + value.faces.push(Face::vertices(0, 1, 2)); + } else if sides == 4 { + value.faces.push(Face::vertices(0, 1, 2)); + value.faces.push(Face::vertices(0, 2, 3)); + } + } + + if calc_vertex_normals && has_cut { + value.initialize_cut_normals(has_hollow)?; + } + value.make_face_u_vs()?; + if calc_vertex_normals { + value.assign_face_numbers(sides, has_hollow, has_cut); + } + Ok(value) + } + + fn initialize_cut_normals(&mut self, has_hollow: bool) -> Result<(), Error> { + let last_outer = usize::try_from(self.num_outer_verts - 1).map_err(|_| Error::Argument)?; + if has_hollow { + self.cut1_coord_indices.extend([ + 0, + i32::try_from(self.coords.len() - 1).map_err(|_| Error::Argument)?, + ]); + self.cut2_coord_indices.extend([ + i32::try_from(last_outer + 1).map_err(|_| Error::Argument)?, + i32::try_from(last_outer).map_err(|_| Error::Argument)?, + ]); + self.cut_normal1 = Coord { + x: self.coords[0].y - self.coords[self.coords.len() - 1].y, + y: -(self.coords[0].x - self.coords[self.coords.len() - 1].x), + z: 0.0, + }; + self.cut_normal2 = Coord { + x: self.coords[last_outer + 1].y - self.coords[last_outer].y, + y: -(self.coords[last_outer + 1].x - self.coords[last_outer].x), + z: 0.0, + }; + } else { + self.cut1_coord_indices.extend([0, 1]); + self.cut2_coord_indices + .extend([i32::try_from(last_outer).map_err(|_| Error::Argument)?, 0]); + let first = *self.vertex_normals.get(1).ok_or(Error::IndexOutOfRange)?; + let last = *self + .vertex_normals + .get(self.vertex_normals.len().saturating_sub(2)) + .ok_or(Error::IndexOutOfRange)?; + self.cut_normal1 = Coord { + x: first.y, + y: -first.x, + z: 0.0, + }; + self.cut_normal2 = Coord { + x: -last.y, + y: last.x, + z: 0.0, + }; + } + self.cut_normal1.normalize()?; + self.cut_normal2.normalize()?; + Ok(()) + } + + fn assign_face_numbers(&mut self, sides: i32, has_hollow: bool, has_cut: bool) { + let mut face = 1; + self.outer_face_number = face; + if has_cut && !has_hollow { + self.face_numbers.push(-1); + } + for i in 0..self.num_outer_verts - 1 { + if sides < 5 && i <= sides { + self.face_numbers.push(face); + face += 1; + } else { + self.face_numbers.push(face); + } + } + if has_cut { + self.face_numbers.push(-1); + } else { + self.face_numbers.push(face); + face += 1; + } + if sides > 4 && (has_hollow || has_cut) { + face += 1; + } + if sides < 5 && (has_hollow || has_cut) && self.num_outer_verts < sides { + face += 1; + } + if has_hollow { + for _ in 0..self.num_hollow_verts { + self.face_numbers.push(face); + } + self.hollow_face_number = face; + face += 1; + } + self.bottom_face_number = face; + face += 1; + if has_hollow && has_cut { + self.face_numbers.push(face); + face += 1; + } + for number in &mut self.face_numbers { + if *number == -1 { + *number = face; + face += 1; + } + } + self.num_prim_faces = face; + } + + pub fn make_face_u_vs(&mut self) -> Result<(), Error> { + self.face_u_vs = self + .coords + .iter() + .map(|c| UVCoord { + u: 1.0 - (0.5 + c.x), + v: 1.0 - (0.5 - c.y), + }) + .collect(); + Ok(()) + } + pub fn copy_with_method(&self) -> Result { + self.copy_with_boolean(true) + } + pub fn copy_with_boolean(&self, need_faces: bool) -> Result { + let mut copy = self.clone(); + if !need_faces { + copy.faces.clear(); + } + Ok(copy) + } + pub fn add_pos_with_coord(&mut self, v: Coord) -> Result<(), Error> { + self.add_pos_with_single_single_single(v.x, v.y, v.z) + } + pub fn add_pos_with_single_single_single( + &mut self, + x: f32, + y: f32, + z: f32, + ) -> Result<(), Error> { + for c in &mut self.coords { + c.x += x; + c.y += y; + c.z += z; + } + Ok(()) + } + pub fn add_rot(&mut self, q: Quat) -> Result<(), Error> { + for c in &mut self.coords { + *c = Coord::mul_with_coord_quat(*c, q); + } + if self.calc_vertex_normals { + for n in &mut self.vertex_normals { + *n = Coord::mul_with_coord_quat(*n, q); + } + self.face_normal = Coord::mul_with_coord_quat(self.face_normal, q); + self.cut_normal1 = Coord::mul_with_coord_quat(self.cut_normal1, q); + self.cut_normal2 = Coord::mul_with_coord_quat(self.cut_normal2, q); + } + Ok(()) + } + pub fn scale(&mut self, x: f32, y: f32) -> Result<(), Error> { + for c in &mut self.coords { + c.x *= x; + c.y *= y; + } + Ok(()) + } + pub fn flip_normals(&mut self) -> Result<(), Error> { + for f in &mut self.faces { + std::mem::swap(&mut f.v1, &mut f.v3); + } + if self.calc_vertex_normals { + if let Some(n) = self.vertex_normals.last_mut() { + n.z = -n.z; + } + } + self.face_normal.x = -self.face_normal.x; + self.face_normal.y = -self.face_normal.y; + self.face_normal.z = -self.face_normal.z; + for uv in &mut self.face_u_vs { + uv.v = 1.0 - uv.v; + } + Ok(()) + } + pub fn add_value2_face_vertex_indices(&mut self, num: i32) -> Result<(), Error> { + for f in &mut self.faces { + f.v1 = f.v1.checked_add(num).ok_or(Error::Argument)?; + f.v2 = f.v2.checked_add(num).ok_or(Error::Argument)?; + f.v3 = f.v3.checked_add(num).ok_or(Error::Argument)?; + } + Ok(()) + } + pub fn add_value2_face_normal_indices(&mut self, num: i32) -> Result<(), Error> { + if self.calc_vertex_normals { + for f in &mut self.faces { + f.n1 = f.n1.checked_add(num).ok_or(Error::Argument)?; + f.n2 = f.n2.checked_add(num).ok_or(Error::Argument)?; + f.n3 = f.n3.checked_add(num).ok_or(Error::Argument)?; + } + } + Ok(()) + } + pub fn dump_raw(&self, path: String, name: String, title: String) -> Result<(), Error> { + dump_raw_geometry(&self.coords, &self.faces, path, name, title) + } +} + +fn triangulate_ring(outer: &[Angle], inner: &[Angle], faces: &mut Vec) -> Result<(), Error> { + let outer_count = outer.len(); + let inner_count = inner.len(); + let total = outer_count + .checked_add(inner_count) + .ok_or(Error::Argument)?; + if outer_count == inner_count { + for i in 0..outer_count.saturating_sub(1) { + faces.push(Face::vertices( + i32::try_from(i).map_err(|_| Error::Argument)?, + i32::try_from(i + 1).map_err(|_| Error::Argument)?, + i32::try_from(total - i - 1).map_err(|_| Error::Argument)?, + )); + faces.push(Face::vertices( + i32::try_from(i + 1).map_err(|_| Error::Argument)?, + i32::try_from(total - i - 2).map_err(|_| Error::Argument)?, + i32::try_from(total - i - 1).map_err(|_| Error::Argument)?, + )); + } + return Ok(()); + } + let mut oi = 0usize; + let mut hi = 0usize; + while oi + 1 < outer_count || hi + 1 < inner_count { + let outer_next = outer.get(oi + 1).map_or(f32::INFINITY, |a| a.angle); + let inner_next = inner.get(hi + 1).map_or(f32::INFINITY, |a| a.angle); + let inner_current = total - hi - 1; + if outer_next <= inner_next { + faces.push(Face::vertices( + i32::try_from(oi).map_err(|_| Error::Argument)?, + i32::try_from(oi + 1).map_err(|_| Error::Argument)?, + i32::try_from(inner_current).map_err(|_| Error::Argument)?, + )); + oi += 1; + } else { + faces.push(Face::vertices( + i32::try_from(oi).map_err(|_| Error::Argument)?, + i32::try_from(inner_current - 1).map_err(|_| Error::Argument)?, + i32::try_from(inner_current).map_err(|_| Error::Argument)?, + )); + hi += 1; + } + } + Ok(()) +} + +#[derive(Clone, Copy, Debug, Default, PartialEq)] +pub struct PathNode { + pub percent_of_path: f32, + pub position: Coord, + pub rotation: Quat, + pub x_scale: f32, + pub y_scale: f32, +} + +#[derive(Clone, Debug)] +pub struct Path { + pub dimple_begin: f32, + pub dimple_end: f32, + pub hole_size_x: f32, + pub hole_size_y: f32, + pub path_cut_begin: f32, + pub path_cut_end: f32, + pub path_nodes: Vec, + pub radius: f32, + pub revolutions: f32, + pub skew: f32, + pub steps_per_revolution: i32, + pub taper_x: f32, + pub taper_y: f32, + pub top_shear_x: f32, + pub top_shear_y: f32, + pub twist_begin: f32, + pub twist_end: f32, +} +impl Default for Path { + fn default() -> Self { + Self { + dimple_begin: 0.0, + dimple_end: 1.0, + hole_size_x: 1.0, + hole_size_y: 0.25, + path_cut_begin: 0.0, + path_cut_end: 1.0, + path_nodes: Vec::new(), + radius: 0.0, + revolutions: 1.0, + skew: 0.0, + steps_per_revolution: 24, + taper_x: 0.0, + taper_y: 0.0, + top_shear_x: 0.0, + top_shear_y: 0.0, + twist_begin: 0.0, + twist_end: 0.0, + } + } +} +impl Path { + pub fn new() -> Result { + Ok(Self::default()) + } + pub fn create(&mut self, path_type: PathType, mut steps: i32) -> Result<(), Error> { + if steps < 1 + || self.path_cut_end <= self.path_cut_begin + || !self.path_cut_begin.is_finite() + || !self.path_cut_end.is_finite() + { + return Err(Error::Argument); + } + self.taper_x = self.taper_x.clamp(-0.999, 0.999); + self.taper_y = self.taper_y.clamp(-0.999, 0.999); + self.path_nodes.clear(); + if matches!(path_type, PathType::Linear | PathType::Flexible) { + let length = self.path_cut_end - self.path_cut_begin; + let twist_total = self.twist_end - self.twist_begin; + if twist_total.abs() > 0.01 { + steps = steps + .checked_add((twist_total.abs() * 3.66) as i32) + .ok_or(Error::Argument)?; + } + let count = usize::try_from(steps) + .map_err(|_| Error::Argument)? + .checked_add(1) + .ok_or(Error::Argument)?; + if count > MAX_PATH_NODES { + return Err(Error::Argument); + } + self.path_nodes.reserve(count); + let step_size = length / (steps as f32); + let percent_step = step_size * 0.999_999; + let mut percent = self.path_cut_begin; + let mut x = self.top_shear_x * self.path_cut_begin; + let mut y = self.top_shear_y * self.path_cut_begin; + let mut z = -0.5 + percent; + let dx = self.top_shear_x * length / (steps as f32); + let dy = self.top_shear_y * length / (steps as f32); + for step in 0..=steps { + let x_scale = if self.taper_x == 0.0 { + 1.0 + } else if self.taper_x > 0.0 { + 1.0 - percent * self.taper_x + } else { + 1.0 + (1.0 - percent) * self.taper_x + }; + let y_scale = if self.taper_y == 0.0 { + 1.0 + } else if self.taper_y > 0.0 { + 1.0 - percent * self.taper_y + } else { + 1.0 + (1.0 - percent) * self.taper_y + }; + let twist = self.twist_begin + twist_total * percent; + self.path_nodes.push(PathNode { + percent_of_path: percent, + position: Coord { x, y, z }, + rotation: Quat::new_with_coord_single( + Coord { + x: 0.0, + y: 0.0, + z: 1.0, + }, + twist, + )?, + x_scale, + y_scale, + }); + if step < steps { + percent += percent_step; + x += dx; + y += dy; + z += step_size; + if percent > self.path_cut_end { + break; + } + } + } + } else { + if self.steps_per_revolution < 1 || self.revolutions <= 0.0 { + return Err(Error::Argument); + } + let twist_total = self.twist_end - self.twist_begin; + if twist_total.abs() > 0.01 { + if twist_total.abs() > PI * 1.5 { + steps.checked_mul(2).ok_or(Error::Argument)?; + } + if twist_total.abs() > PI * 3.0 { + steps.checked_mul(4).ok_or(Error::Argument)?; + } + } + let y_path_scale = self.hole_size_y * 0.5; + let path_length = self.path_cut_end - self.path_cut_begin; + let total_skew = self.skew * 2.0 * path_length; + let skew_start = self.path_cut_begin * 2.0 * self.skew - self.skew; + let x_shear = self.top_shear_x * (0.25 + 0.5 * (0.5 - self.hole_size_y)); + let y_comp = 1.0 + self.top_shear_y.abs() * 0.25; + let start = TAU * self.path_cut_begin * self.revolutions - self.top_shear_y * 0.9; + let end = TAU * self.path_cut_end * self.revolutions - self.top_shear_y * 0.9; + let step_size = TAU / (self.steps_per_revolution as f32); + let estimated = ((end - start) / step_size).ceil() as usize + 2; + if estimated > MAX_PATH_NODES { + return Err(Error::Argument); + } + self.path_nodes.reserve(estimated); + let mut step = (start / step_size) as i32; + let mut angle = start; + loop { + let percent = angle / (TAU * self.revolutions); + let angle_percent = (angle - start) / (end - start); + let mut xs = (1.0 - self.skew.abs()) * self.hole_size_x; + let mut ys = self.hole_size_y; + if self.taper_x > 0.01 { + xs *= 1.0 - percent * self.taper_x; + } else if self.taper_x < -0.01 { + xs *= 1.0 + (1.0 - percent) * self.taper_x; + } + if self.taper_y > 0.01 { + ys *= 1.0 - percent * self.taper_y; + } else if self.taper_y < -0.01 { + ys *= 1.0 + (1.0 - percent) * self.taper_y; + } + let radius_scale = if self.radius > 0.001 { + 1.0 - self.radius * percent + } else if self.radius < 0.001 { + 1.0 + self.radius * (1.0 - percent) + } else { + 1.0 + }; + let twist = self.twist_begin + twist_total * percent; + let x = 0.5 * (skew_start + total_skew * angle_percent) + angle.sin() * x_shear; + let y = y_comp * angle.cos() * (0.5 - y_path_scale) * radius_scale; + let z = (angle + self.top_shear_y).sin() * (0.5 - y_path_scale) * radius_scale; + let mut rotation = Quat::new_with_coord_single( + Coord { + x: 1.0, + y: 0.0, + z: 0.0, + }, + angle + self.top_shear_y, + )?; + if twist_total != 0.0 || self.twist_begin != 0.0 { + rotation = Quat::mul( + rotation, + Quat::new_with_coord_single( + Coord { + x: 0.0, + y: 0.0, + z: 1.0, + }, + twist, + )?, + ); + } + self.path_nodes.push(PathNode { + percent_of_path: percent, + position: Coord { x, y, z }, + rotation, + x_scale: xs, + y_scale: ys, + }); + if self.path_nodes.len() > MAX_PATH_NODES { + return Err(Error::Argument); + } + if angle >= end - 0.01 { + break; + } + step = step.checked_add(1).ok_or(Error::Argument)?; + angle = step_size * (step as f32); + if angle > end { + angle = end; + } + } + } + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub struct PrimMesh { + pub calc_vertex_normals: bool, + pub coords: Vec, + pub dimple_begin: f32, + pub dimple_end: f32, + pub error_message: String, + pub faces: Vec, + pub hole_size_x: f32, + pub hole_size_y: f32, + pub normals: Vec, + pub num_prim_faces: i32, + pub path_cut_begin: f32, + pub path_cut_end: f32, + pub radius: f32, + pub revolutions: f32, + pub skew: f32, + pub sphere_mode: bool, + pub steps_per_revolution: i32, + pub taper_x: f32, + pub taper_y: f32, + pub top_shear_x: f32, + pub top_shear_y: f32, + pub twist_begin: i32, + pub twist_end: i32, + pub viewer_faces: Vec, + pub viewer_mode: bool, + sides: i32, + profile_start: f32, + profile_end: f32, + hollow: f32, + hollow_sides: i32, + normals_processed: bool, + profile_outer_face_number: i32, + profile_hollow_face_number: i32, + has_profile_cut: bool, + has_hollow: bool, +} +impl PrimMesh { + pub fn new( + mut sides: i32, + mut profile_start: f32, + mut profile_end: f32, + mut hollow: f32, + mut hollow_sides: i32, + ) -> Result { + if !profile_start.is_finite() || !profile_end.is_finite() || !hollow.is_finite() { + return Err(Error::Argument); + } + sides = sides.max(3); + hollow_sides = hollow_sides.max(3); + profile_start = profile_start.max(0.0); + profile_end = profile_end.clamp(0.02, 1.0); + if profile_start >= profile_end { + profile_start = profile_end - 0.02; + } + hollow = hollow.clamp(0.0, 0.99); + Ok(Self { + calc_vertex_normals: false, + coords: Vec::new(), + dimple_begin: 0.0, + dimple_end: 1.0, + error_message: String::new(), + faces: Vec::new(), + hole_size_x: 1.0, + hole_size_y: 0.25, + normals: Vec::new(), + num_prim_faces: 0, + path_cut_begin: 0.0, + path_cut_end: 1.0, + radius: 0.0, + revolutions: 1.0, + skew: 0.0, + sphere_mode: false, + steps_per_revolution: 24, + taper_x: 0.0, + taper_y: 0.0, + top_shear_x: 0.0, + top_shear_y: 0.0, + twist_begin: 0, + twist_end: 0, + viewer_faces: Vec::new(), + viewer_mode: false, + sides, + profile_start, + profile_end, + hollow, + hollow_sides, + normals_processed: false, + profile_outer_face_number: -1, + profile_hollow_face_number: -1, + has_profile_cut: profile_start > 0.0 || profile_end < 1.0, + has_hollow: hollow > 0.0, + }) + } + + pub fn extrude(&mut self, path_type: PathType) -> Result<(), Error> { + self.validate_parameters()?; + self.coords.clear(); + self.faces.clear(); + self.normals.clear(); + self.viewer_faces.clear(); + self.normals_processed = false; + if self.viewer_mode { + self.calc_vertex_normals = true; + } + let twist_begin = self.twist_begin as f32; + let twist_end = self.twist_end as f32; + let twist_total = twist_end - twist_begin; + let mut steps: i32 = 1; + if twist_total.abs() > 0.01 { + steps = steps + .checked_add((twist_total.abs() * 3.66) as i32) + .ok_or(Error::Argument)?; + } + let need_end_faces = if path_type == PathType::Circular { + self.path_cut_begin != 0.0 + || self.path_cut_end != 1.0 + || self.taper_x != 0.0 + || self.taper_y != 0.0 + || self.skew != 0.0 + || twist_total != 0.0 + || self.radius != 0.0 + } else { + true + }; + let mut adjusted_hollow = self.hollow; + let mut initial_rotation = 0.0; + if path_type == PathType::Circular { + match self.sides { + 3 => { + initial_rotation = PI; + if self.hollow_sides == 4 { + adjusted_hollow = adjusted_hollow.min(0.7) * 0.707; + } else { + adjusted_hollow *= 0.5; + } + } + 4 => { + initial_rotation = 0.25 * PI; + if self.hollow_sides != 4 { + adjusted_hollow *= 0.707; + } + } + _ if self.sides > 4 => { + initial_rotation = PI; + if self.hollow_sides == 4 { + adjusted_hollow = adjusted_hollow.min(0.7) / 0.7; + } + } + _ => {} + } + } else { + match self.sides { + 3 => { + if self.hollow_sides == 4 { + adjusted_hollow = adjusted_hollow.min(0.7) * 0.707; + } else { + adjusted_hollow *= 0.5; + } + } + 4 => { + initial_rotation = 1.25 * PI; + if self.hollow_sides != 4 { + adjusted_hollow *= 0.707; + } + } + 24 if self.hollow_sides == 4 => adjusted_hollow *= 1.414, + _ => {} + } + } + let mut profile = Profile::new_with_int32_single_single_single_int32_boolean_boolean( + self.sides, + self.profile_start, + self.profile_end, + adjusted_hollow, + self.hollow_sides, + true, + self.calc_vertex_normals, + )?; + self.error_message.clone_from(&profile.error_message); + let mut cut1_face = profile.bottom_face_number + 1; + let mut cut2_face = cut1_face + 1; + if !need_end_faces { + cut1_face -= 2; + cut2_face -= 2; + } + self.profile_outer_face_number = profile.outer_face_number - i32::from(!need_end_faces); + self.profile_hollow_face_number = if self.has_hollow { + profile.hollow_face_number - i32::from(!need_end_faces) + } else { + -1 + }; + let cut1_vert = if self.has_profile_cut { + if self.has_hollow { + i32::try_from(profile.coords.len() - 1).map_err(|_| Error::Argument)? + } else { + 0 + } + } else { + -1 + }; + let cut2_vert = if self.has_profile_cut { + if self.has_hollow { + profile.num_outer_verts - 1 + } else { + profile.num_outer_verts + } + } else { + -1 + }; + if initial_rotation != 0.0 { + profile.add_rot(Quat::new_with_coord_single( + Coord { + x: 0.0, + y: 0.0, + z: 1.0, + }, + initial_rotation, + )?)?; + if self.viewer_mode { + profile.make_face_u_vs()?; + } + } + let mut path = Path { + twist_begin, + twist_end, + top_shear_x: self.top_shear_x, + top_shear_y: self.top_shear_y, + path_cut_begin: self.path_cut_begin, + path_cut_end: self.path_cut_end, + dimple_begin: self.dimple_begin, + dimple_end: self.dimple_end, + skew: self.skew, + hole_size_x: self.hole_size_x, + hole_size_y: self.hole_size_y, + taper_x: self.taper_x, + taper_y: self.taper_y, + radius: self.radius, + revolutions: self.revolutions, + steps_per_revolution: self.steps_per_revolution, + ..Path::default() + }; + path.create(path_type, steps)?; + let total_vertices = path + .path_nodes + .len() + .checked_mul(profile.coords.len()) + .ok_or(Error::Argument)?; + if total_vertices > MAX_MESH_VERTICES { + return Err(Error::Argument); + } + self.coords.reserve(total_vertices); + let mut last_cut1 = Coord::default(); + let mut last_cut2 = Coord::default(); + let mut last_v = 0.0; + for (node_index, node) in path.path_nodes.iter().copied().enumerate() { + let coords_offset = self.coords.len(); + let normals_offset = self.normals.len(); + let transformed: Vec = profile + .coords + .iter() + .map(|v| { + let scaled = Coord { + x: v.x * node.x_scale, + y: v.y * node.y_scale, + z: v.z, + }; + Coord::add( + Coord::mul_with_coord_quat(scaled, node.rotation), + node.position, + ) + }) + .collect(); + self.coords.extend_from_slice(&transformed); + if self.calc_vertex_normals { + self.normals.extend( + profile + .vertex_normals + .iter() + .map(|n| Coord::mul_with_coord_quat(*n, node.rotation)), + ); + } + if node.percent_of_path < self.path_cut_begin + 0.01 + || node.percent_of_path > self.path_cut_end - 0.01 + { + for source in &profile.faces { + let mut face = *source; + face.v1 = add_index(face.v1, coords_offset)?; + face.v2 = add_index(face.v2, coords_offset)?; + face.v3 = add_index(face.v3, coords_offset)?; + if self.calc_vertex_normals { + face.n1 = add_index(face.n1, normals_offset)?; + face.n2 = add_index(face.n2, normals_offset)?; + face.n3 = add_index(face.n3, normals_offset)?; + } + self.faces.push(face); + } + } + let this_v = 1.0 - node.percent_of_path; + if node_index > 0 { + self.add_side_faces( + &profile, + coords_offset, + this_v, + last_v, + cut1_vert, + cut2_vert, + cut1_face, + cut2_face, + need_end_faces, + last_cut1, + last_cut2, + node.rotation, + )?; + } + last_cut1 = profile.cut_normal1; + last_cut2 = profile.cut_normal2; + last_v = this_v; + if need_end_faces && node_index + 1 == path.path_nodes.len() && self.viewer_mode { + for face in &profile.faces { + let v1 = idx(face.v1, transformed.len())?; + let v2 = idx(face.v2, transformed.len())?; + let v3 = idx(face.v3, transformed.len())?; + let mut viewer = ViewerFace::new(0)?; + viewer.v1 = transformed[v1]; + viewer.v2 = transformed[v2]; + viewer.v3 = transformed[v3]; + viewer.coord_index1 = add_index(face.v1, coords_offset)?; + viewer.coord_index2 = add_index(face.v2, coords_offset)?; + viewer.coord_index3 = add_index(face.v3, coords_offset)?; + let normal = profile.face_normal; + viewer.n1 = normal; + viewer.n2 = normal; + viewer.n3 = normal; + viewer.uv1 = profile.face_u_vs[v1]; + viewer.uv2 = profile.face_u_vs[v2]; + viewer.uv3 = profile.face_u_vs[v3]; + if path_type == PathType::Linear { + viewer.uv1.flip()?; + viewer.uv2.flip()?; + viewer.uv3.flip()?; + } + self.viewer_faces.push(viewer); + } + } + } + if self.viewer_mode { + self.num_prim_faces = self + .viewer_faces + .iter() + .map(|face| face.prim_face_number) + .max() + .unwrap_or(-1) + + 1; + } else { + self.num_prim_faces = profile.num_prim_faces; + } + self.validate_geometry()?; + Ok(()) + } + + #[allow(clippy::too_many_arguments)] + fn add_side_faces( + &mut self, + profile: &Profile, + offset: usize, + this_v: f32, + last_v: f32, + cut1: i32, + cut2: i32, + cut1_face: i32, + cut2_face: i32, + need_end_faces: bool, + last_cut1: Coord, + last_cut2: Coord, + rotation: Quat, + ) -> Result<(), Error> { + let count = profile.coords.len(); + let mut start = offset.checked_add(1).ok_or(Error::Argument)?; + if self.sides < 5 || self.has_profile_cut || self.has_hollow { + start -= 1; + } + let end = offset.checked_add(count).ok_or(Error::Argument)?; + for current in start..end { + let next = if current + 1 == end { + start + } else { + current + 1 + }; + let previous = current.checked_sub(count).ok_or(Error::IndexOutOfRange)?; + let previous_next = next.checked_sub(count).ok_or(Error::IndexOutOfRange)?; + let f1 = Face { + v1: to_i32(current)?, + v2: to_i32(previous)?, + v3: to_i32(next)?, + n1: to_i32(current)?, + n2: to_i32(previous)?, + n3: to_i32(next)?, + ..Face::default() + }; + let f2 = Face { + v1: to_i32(next)?, + v2: to_i32(previous)?, + v3: to_i32(previous_next)?, + n1: to_i32(next)?, + n2: to_i32(previous)?, + n3: to_i32(previous_next)?, + ..Face::default() + }; + self.faces.push(f1); + self.faces.push(f2); + if !self.viewer_mode { + continue; + } + let which = i32::try_from(current - start).map_err(|_| Error::Argument)?; + let mut prim = profile + .face_numbers + .get(usize::try_from(which).map_err(|_| Error::Argument)?) + .copied() + .or_else(|| profile.face_numbers.first().copied()) + .unwrap_or(0); + if !need_end_faces { + prim = (prim - 1).max(0); + } + let mut vf1 = ViewerFace::new(prim)?; + let mut vf2 = ViewerFace::new(prim)?; + let mut u_index = usize::try_from(which).map_err(|_| Error::Argument)?; + if !self.has_hollow && self.sides > 4 && u_index < profile.us.len().saturating_sub(1) { + u_index += 1; + } + let (mut u1, mut u2) = if profile.us.is_empty() { + (0.0, 0.0) + } else { + u_index = u_index.min(profile.us.len() - 1); + ( + profile.us[u_index], + profile.us.get(u_index + 1).copied().unwrap_or(1.0), + ) + }; + if which == cut1 || which == cut2 { + u1 = 0.0; + u2 = 1.0; + } else if self.sides < 5 && which < profile.num_outer_verts { + u1 *= self.sides as f32; + u2 *= self.sides as f32; + u2 -= u1 as i32 as f32; + u1 -= u1 as i32 as f32; + if u2 < 0.1 { + u2 = 1.0; + } + } + if self.sphere_mode && which != cut1 && which != cut2 { + u1 = u1 * 2.0 - 1.0; + u2 = u2 * 2.0 - 1.0; + if which >= profile.num_outer_verts { + u1 -= self.hollow; + u2 -= self.hollow; + } + } + vf1.uv1 = UVCoord { u: u1, v: this_v }; + vf1.uv2 = UVCoord { u: u1, v: last_v }; + vf1.uv3 = UVCoord { u: u2, v: this_v }; + vf2.uv1 = UVCoord { u: u2, v: this_v }; + vf2.uv2 = UVCoord { u: u1, v: last_v }; + vf2.uv3 = UVCoord { u: u2, v: last_v }; + assign_viewer_coords(&mut vf1, &self.coords, f1)?; + assign_viewer_coords(&mut vf2, &self.coords, f2)?; + if which == cut1 { + let current_normal = Coord::mul_with_coord_quat(profile.cut_normal1, rotation); + vf1.prim_face_number = cut1_face; + vf2.prim_face_number = cut1_face; + vf1.n1 = current_normal; + vf1.n2 = last_cut1; + vf1.n3 = last_cut1; + vf2.n1 = current_normal; + vf2.n2 = last_cut1; + vf2.n3 = current_normal; + } else if which == cut2 { + let current_normal = Coord::mul_with_coord_quat(profile.cut_normal2, rotation); + vf1.prim_face_number = cut2_face; + vf2.prim_face_number = cut2_face; + vf1.n1 = current_normal; + vf1.n2 = last_cut2; + vf1.n3 = last_cut2; + vf2.n1 = current_normal; + vf2.n2 = last_cut2; + vf2.n3 = current_normal; + } else if (self.sides < 5 && which < profile.num_outer_verts) + || (self.hollow_sides < 5 && which >= profile.num_outer_verts) + { + vf1.calc_surface_normal()?; + vf2.calc_surface_normal()?; + } else { + assign_viewer_normals(&mut vf1, &self.normals, f1)?; + assign_viewer_normals(&mut vf2, &self.normals, f2)?; + } + self.viewer_faces.push(vf1); + self.viewer_faces.push(vf2); + } + Ok(()) + } + + fn validate_parameters(&self) -> Result<(), Error> { + let values = [ + self.dimple_begin, + self.dimple_end, + self.hole_size_x, + self.hole_size_y, + self.path_cut_begin, + self.path_cut_end, + self.radius, + self.revolutions, + self.skew, + self.taper_x, + self.taper_y, + self.top_shear_x, + self.top_shear_y, + ]; + if values.iter().any(|v| !v.is_finite()) || self.path_cut_end <= self.path_cut_begin { + return Err(Error::Argument); + } + Ok(()) + } + fn validate_geometry(&self) -> Result<(), Error> { + if self + .coords + .iter() + .any(|c| !c.x.is_finite() || !c.y.is_finite() || !c.z.is_finite()) + || self + .normals + .iter() + .any(|c| !c.x.is_finite() || !c.y.is_finite() || !c.z.is_finite()) + { + return Err(Error::InvalidOperation); + } + for face in &self.faces { + idx(face.v1, self.coords.len())?; + idx(face.v2, self.coords.len())?; + idx(face.v3, self.coords.len())?; + } + Ok(()) + } + pub fn surface_normal(&self, face_index: i32) -> Result { + let face = *self + .faces + .get(idx(face_index, self.faces.len())?) + .ok_or(Error::IndexOutOfRange)?; + surface_normal_for(&self.coords, face) + } + pub fn calc_normals(&mut self) -> Result<(), Error> { + if self.normals_processed { + return Ok(()); + } + self.normals_processed = true; + if !self.calc_vertex_normals { + self.normals = Vec::with_capacity(self.faces.len()); + } + for index in 0..self.faces.len() { + let normal = self.surface_normal(i32::try_from(index).map_err(|_| Error::Argument)?)?; + self.normals.push(normal); + let normal_index = + i32::try_from(self.normals.len() - 1).map_err(|_| Error::Argument)?; + self.faces[index].n1 = normal_index; + self.faces[index].n2 = normal_index; + self.faces[index].n3 = normal_index; + } + Ok(()) + } + pub fn copy(&self) -> Result { + Ok(self.clone()) + } + pub fn add_pos(&mut self, x: f32, y: f32, z: f32) -> Result<(), Error> { + for c in &mut self.coords { + c.x += x; + c.y += y; + c.z += z; + } + for face in &mut self.viewer_faces { + face.add_pos(x, y, z)?; + } + Ok(()) + } + pub fn add_rot(&mut self, q: Quat) -> Result<(), Error> { + for c in &mut self.coords { + *c = Coord::mul_with_coord_quat(*c, q); + } + for n in &mut self.normals { + *n = Coord::mul_with_coord_quat(*n, q); + } + for face in &mut self.viewer_faces { + face.add_rot(q)?; + } + Ok(()) + } + pub fn scale(&mut self, x: f32, y: f32, z: f32) -> Result<(), Error> { + let m = Coord { x, y, z }; + for c in &mut self.coords { + *c = Coord::mul_with_coord_coord(*c, m); + } + for face in &mut self.viewer_faces { + face.scale(x, y, z)?; + } + Ok(()) + } + pub fn get_vertex_indexer(&self) -> Result, Error> { + if self.viewer_mode && !self.viewer_faces.is_empty() { + let max_face = self + .viewer_faces + .iter() + .map(|face| face.prim_face_number) + .max() + .unwrap_or(0); + let num_prim_faces = max_face.checked_add(1).ok_or(Error::Argument)?; + let face_count = usize::try_from(num_prim_faces).map_err(|_| Error::Argument)?; + if face_count > MAX_PROFILE_VERTICES { + return Err(Error::Argument); + } + let mut viewer_vertices: Vec> = + (0..face_count).map(|_| Vec::new()).collect(); + let mut viewer_polygons: Vec>> = + (0..face_count).map(|_| Some(Vec::new())).collect(); + let mut indices: Vec> = + (0..face_count).map(|_| HashMap::new()).collect(); + for face in &self.viewer_faces { + let face_index = idx(face.prim_face_number, face_count)?; + let vertices = &mut viewer_vertices[face_index]; + let map = &mut indices[face_index]; + let v1 = indexed_viewer_vertex( + map, + vertices, + face.coord_index1, + face.v1, + face.n1, + face.uv1, + )?; + let v2 = indexed_viewer_vertex( + map, + vertices, + face.coord_index2, + face.v2, + face.n2, + face.uv2, + )?; + let v3 = indexed_viewer_vertex( + map, + vertices, + face.coord_index3, + face.v3, + face.n3, + face.uv3, + )?; + viewer_polygons[face_index] + .as_mut() + .ok_or(Error::InvalidOperation)? + .push(ViewerPolygon { v1, v2, v3 }); + } + Ok(Some(VertexIndexer { + num_prim_faces, + viewer_polygons, + viewer_vertices, + })) + } else { + Ok(None) + } + } + pub fn dump_raw(&self, path: String, name: String, title: String) -> Result<(), Error> { + dump_raw_geometry(&self.coords, &self.faces, path, name, title) + } + pub fn profile_outer_face_number(&self) -> i32 { + self.profile_outer_face_number + } + pub fn set_profile_outer_face_number(&mut self, value: i32) { + self.profile_outer_face_number = value; + } + pub fn profile_hollow_face_number(&self) -> i32 { + self.profile_hollow_face_number + } + pub fn set_profile_hollow_face_number(&mut self, value: i32) { + self.profile_hollow_face_number = value; + } + pub fn has_profile_cut(&self) -> bool { + self.has_profile_cut + } + pub fn set_has_profile_cut(&mut self, value: bool) { + self.has_profile_cut = value; + } + pub fn has_hollow(&self) -> bool { + self.has_hollow + } + pub fn set_has_hollow(&mut self, value: bool) { + self.has_hollow = value; + } + pub fn params_to_display_string(&self) -> Result { + Ok(format!( + "sides..................: {}\nhollowSides..........: {}\nprofileStart.........: {}\nprofileEnd...........: {}\nhollow...............: {}\ntwistBegin...........: {}\ntwistEnd.............: {}\ntopShearX............: {}\ntopShearY............: {}\npathCutBegin.........: {}\npathCutEnd...........: {}\ndimpleBegin..........: {}\ndimpleEnd............: {}\nskew.................: {}\nholeSizeX............: {}\nholeSizeY............: {}\ntaperX...............: {}\ntaperY...............: {}\nradius...............: {}\nrevolutions..........: {}\nstepsPerRevolution...: {}\nsphereMode...........: {}\nhasProfileCut........: {}\nhasHollow............: {}\nviewerMode...........: {}", + self.sides, + self.hollow_sides, + self.profile_start, + self.profile_end, + self.hollow, + self.twist_begin, + self.twist_end, + self.top_shear_x, + self.top_shear_y, + self.path_cut_begin, + self.path_cut_end, + self.dimple_begin, + self.dimple_end, + self.skew, + self.hole_size_x, + self.hole_size_y, + self.taper_x, + self.taper_y, + self.radius, + self.revolutions, + self.steps_per_revolution, + self.sphere_mode, + self.has_profile_cut, + self.has_hollow, + self.viewer_mode + )) + } +} + +fn to_i32(value: usize) -> Result { + i32::try_from(value).map_err(|_| Error::Argument) +} +fn add_index(value: i32, offset: usize) -> Result { + value.checked_add(to_i32(offset)?).ok_or(Error::Argument) +} +fn assign_viewer_coords( + viewer: &mut ViewerFace, + coords: &[Coord], + face: Face, +) -> Result<(), Error> { + viewer.v1 = coords[idx(face.v1, coords.len())?]; + viewer.v2 = coords[idx(face.v2, coords.len())?]; + viewer.v3 = coords[idx(face.v3, coords.len())?]; + viewer.coord_index1 = face.v1; + viewer.coord_index2 = face.v2; + viewer.coord_index3 = face.v3; + Ok(()) +} +fn assign_viewer_normals( + viewer: &mut ViewerFace, + normals: &[Coord], + face: Face, +) -> Result<(), Error> { + if normals.is_empty() { + return viewer.calc_surface_normal(); + } + viewer.n1 = normals[idx(face.n1, normals.len())?]; + viewer.n2 = normals[idx(face.n2, normals.len())?]; + viewer.n3 = normals[idx(face.n3, normals.len())?]; + Ok(()) +} +fn indexed_viewer_vertex( + indices: &mut HashMap, + vertices: &mut Vec, + coord_index: i32, + v: Coord, + n: Coord, + uv: UVCoord, +) -> Result { + if let Some(index) = indices.get(&coord_index) { + return Ok(*index); + } + let index = i32::try_from(vertices.len()).map_err(|_| Error::Argument)?; + vertices.push(ViewerVertex { n, uv, v }); + indices.insert(coord_index, index); + Ok(index) +} +fn dump_raw_geometry( + coords: &[Coord], + faces: &[Face], + path: String, + name: String, + title: String, +) -> Result<(), Error> { + let mut target = PathBuf::from(path); + target.push(format!("{name}_{title}.raw")); + let file = File::create(target).map_err(|_| Error::InvalidOperation)?; + let mut writer = BufWriter::new(file); + for face in faces { + let a = coords[idx(face.v1, coords.len())?]; + let b = coords[idx(face.v2, coords.len())?]; + let c = coords[idx(face.v3, coords.len())?]; + writeln!(writer, "{a} {b} {c}").map_err(|_| Error::InvalidOperation)?; + } + writer.flush().map_err(|_| Error::InvalidOperation) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn mesh(sides: i32, hollow: f32, viewer: bool) -> PrimMesh { + let mut mesh = PrimMesh::new(sides, 0.0, 1.0, hollow, 4).expect("valid mesh"); + mesh.viewer_mode = viewer; + mesh.calc_vertex_normals = viewer; + mesh + } + + #[test] + fn golden_box_and_torus_summaries_match_reference() { + let mut box_mesh = mesh(4, 0.0, true); + box_mesh.extrude(PathType::Linear).expect("box extrusion"); + assert_eq!( + ( + box_mesh.coords.len(), + box_mesh.faces.len(), + box_mesh.viewer_faces.len(), + box_mesh.num_prim_faces, + ), + (10, 14, 12, 6) + ); + + let mut torus = mesh(24, 0.0, true); + torus.hole_size_x = 1.0; + torus.hole_size_y = 0.25; + torus.extrude(PathType::Circular).expect("torus extrusion"); + assert_eq!( + ( + torus.coords.len(), + torus.faces.len(), + torus.viewer_faces.len(), + torus.num_prim_faces, + ), + (650, 1248, 1200, 1) + ); + } + + #[test] + fn special_profile_tables_keep_normalized_u_coordinates() { + let profile = Profile::new_with_int32_single_single_single_int32_boolean_boolean( + 4, 0.0, 1.0, 0.0, 4, true, true, + ) + .expect("square profile"); + assert_eq!(profile.us, vec![0.0, 0.25, 0.5, 0.75, 1.0]); + assert!((profile.vertex_normals[0].x - std::f32::consts::FRAC_1_SQRT_2).abs() < 1e-6); + assert!((profile.vertex_normals[0].y - std::f32::consts::FRAC_1_SQRT_2).abs() < 1e-6); + } + + #[test] + fn complex_circular_extrusion_is_deterministic_finite_and_bounded() { + fn build() -> PrimMesh { + let mut mesh = PrimMesh::new(8, 0.1, 0.85, 0.35, 4).expect("valid mesh"); + mesh.viewer_mode = true; + mesh.calc_vertex_normals = true; + mesh.twist_begin = -1; + mesh.twist_end = 2; + mesh.top_shear_x = 0.2; + mesh.top_shear_y = -0.15; + mesh.path_cut_begin = 0.05; + mesh.path_cut_end = 0.9; + mesh.skew = 0.15; + mesh.taper_x = 0.25; + mesh.taper_y = -0.2; + mesh.radius = 0.2; + mesh.revolutions = 1.5; + mesh.steps_per_revolution = 32; + mesh.extrude(PathType::Circular).expect("complex extrusion"); + mesh + } + let first = build(); + let second = build(); + assert_eq!(first.coords, second.coords); + assert_eq!(first.faces, second.faces); + assert_eq!(first.viewer_faces, second.viewer_faces); + for face in &first.faces { + for index in [face.v1, face.v2, face.v3] { + assert!(usize::try_from(index).is_ok_and(|index| index < first.coords.len())); + } + } + for coord in &first.coords { + assert!(coord.x.is_finite() && coord.y.is_finite() && coord.z.is_finite()); + } + for face in &first.viewer_faces { + for value in [ + face.uv1.u, face.uv1.v, face.uv2.u, face.uv2.v, face.uv3.u, face.uv3.v, + ] { + assert!(value.is_finite()); + } + } + } + + #[test] + fn excessive_profile_and_path_sizes_are_rejected() { + assert!(matches!( + Profile::new_with_int32_single_single_single_int32_boolean_boolean( + i32::try_from(MAX_PROFILE_VERTICES + 1).expect("test limit"), + 0.0, + 1.0, + 0.0, + 4, + true, + false, + ), + Err(Error::Argument) + )); + let mut path = Path::new().expect("path"); + assert!(matches!( + path.create( + PathType::Linear, + i32::try_from(MAX_PATH_NODES).expect("test limit") + ), + Err(Error::Argument) + )); + } +} diff --git a/tests/compat/tests/rendering_shims.rs b/tests/compat/tests/rendering_shims.rs index e7c4e08..aa31367 100644 --- a/tests/compat/tests/rendering_shims.rs +++ b/tests/compat/tests/rendering_shims.rs @@ -23,16 +23,11 @@ fn mesh_and_renderer_calls_compile_with_typed_elements() { } #[test] -fn owned_constructors_fail_with_exact_member_ids() { - let Err(prim_error) = PrimMesh::new(4, 0.0, 1.0, 0.0, 4) else { - panic!("failure-only PrimMesh constructor unexpectedly returned a value"); - }; - assert_eq!( - prim_error.csharp_member(), - Some( - "M:LibreMetaverse.PrimMesher.PrimMesh.#ctor(System.Int32,System.Single,System.Single,System.Single,System.Int32)" - ) - ); +fn native_mesher_and_remaining_shims_report_their_current_status() { + let mesh = PrimMesh::new(4, 0.0, 1.0, 0.0, 4) + .expect("native PrimMesh constructor should create a checked mesh"); + assert!(mesh.coords.is_empty()); + assert!(mesh.faces.is_empty()); let Err(renderer_error) = SimpleRenderer::new() else { panic!("failure-only renderer constructor unexpectedly returned a value"); diff --git a/tests/red-suite-baseline.json b/tests/red-suite-baseline.json index 218544d..06ca542 100644 --- a/tests/red-suite-baseline.json +++ b/tests/red-suite-baseline.json @@ -11,6 +11,51 @@ "unreviewed": 0 }, "allowed_parity_passes": [ + "LibreMetaverse.Rendering.Tests/PrimMesher/CoordQuatTests.cs::CoordTests.Coord_Length_KnownVector::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/CoordQuatTests.cs::CoordTests.Coord_Length_UnitVector::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/CoordQuatTests.cs::CoordTests.Coord_Normalize_GivesUnitLength::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/CoordQuatTests.cs::CoordTests.Coord_Normalize_ZeroVector_DoesNotThrow::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/CoordQuatTests.cs::CoordTests.Coord_Normalize_ZeroVector_GivesZero::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/CoordQuatTests.cs::CoordTests.Coord_Cross_XaxisTimesYaxis_GivesZaxis::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/CoordQuatTests.cs::CoordTests.Coord_Cross_YaxisTimesXaxis_GivesNegZaxis::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/CoordQuatTests.cs::CoordTests.Coord_Add_IsCommutative::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/CoordQuatTests.cs::QuatTests.Quat_AxisAngle_IsNormalized::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/CoordQuatTests.cs::QuatTests.Quat_Identity_RotatesCoordUnchanged::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/CoordQuatTests.cs::QuatTests.Coord_RotateByQuat_90DegAroundXaxis_YbecomesZ::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/CoordQuatTests.cs::QuatTests.Coord_RotateByQuat_90DegAroundXaxis_ZbecomesNegY_BugRegression::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/CoordQuatTests.cs::QuatTests.Coord_RotateByQuat_90DegAroundZaxis_XbecomesY::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/CoordQuatTests.cs::QuatTests.Coord_RotateByQuat_90DegAroundYaxis_ZbecomesX::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/CoordQuatTests.cs::QuatTests.Coord_RotateByQuat_180DegAroundZaxis_NegatesXandY::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/CoordQuatTests.cs::QuatTests.Coord_RotateByQuat_PreservesLength::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/CoordQuatTests.cs::QuatTests.Quat_Multiply_TwoNineties_AroundZ_Gives180::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/CoordQuatTests.cs::QuatTests.Quat_Multiply_IsAssociative::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/PrimMeshTests.cs::PrimMeshTests.Extrude_Box_Linear_HasVerticesAndFaces::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/PrimMeshTests.cs::PrimMeshTests.Extrude_Box_Linear_AllFaceIndicesInRange::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/PrimMeshTests.cs::PrimMeshTests.Extrude_Cylinder_Linear_HasGeometry::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/PrimMeshTests.cs::PrimMeshTests.Extrude_Prism_Linear_HasGeometry::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/PrimMeshTests.cs::PrimMeshTests.Extrude_Torus_Circular_HasGeometry::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/PrimMeshTests.cs::PrimMeshTests.Extrude_Torus_Circular_AllFaceIndicesInRange::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/PrimMeshTests.cs::PrimMeshTests.Extrude_Box_WithHollow_HasGeometry::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/PrimMeshTests.cs::PrimMeshTests.Extrude_Box_WithProfileCut_HasGeometry::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/PrimMeshTests.cs::PrimMeshTests.Extrude_Box_ViewerMode_HasViewerFaces::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/PrimMeshTests.cs::PrimMeshTests.Extrude_Box_ViewerMode_AllViewerFaceVerticesFinite::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/PrimMeshTests.cs::PrimMeshTests.Extrude_Box_ViewerMode_NumPrimFaces_Is6::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/PrimMeshTests.cs::PrimMeshTests.Extrude_Cylinder_ViewerMode_NumPrimFaces_AtLeast2::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/PrimMeshTests.cs::PrimMeshTests.Extrude_Torus_ViewerMode_NumPrimFaces_Is1::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/PrimMeshTests.cs::PrimMeshTests.Extrude_Sphere_ViewerMode_NumPrimFaces_Is1::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/PrimMeshTests.cs::PrimMeshTests.Extrude_Torus_ViewerMode_HasViewerFaces::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/PrimMeshTests.cs::PrimMeshTests.CalcNormals_Box_ProducesOneNormalPerFace::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/PrimMeshTests.cs::PrimMeshTests.CalcNormals_Box_AllNonZeroNormalsUnitLength::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/PrimMeshTests.cs::PrimMeshTests.CalcNormals_CalledTwice_IsIdempotent::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/PrimMeshTests.cs::PrimMeshTests.VertexIndexer_Box_IsNotNull::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/PrimMeshTests.cs::PrimMeshTests.VertexIndexer_NonViewerMode_ReturnsNull::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/PrimMeshTests.cs::PrimMeshTests.VertexIndexer_Box_AllPolygonIndicesInRange::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/PrimMeshTests.cs::PrimMeshTests.Copy_Box_ProducesIndependentInstance::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/PrimMeshTests.cs::PrimMeshTests.AddPos_ShiftsAllCoords::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/PrimMeshTests.cs::PrimMeshTests.Scale_ScalesAllCoords::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/PrimMeshTests.cs::PrimMeshTests.AddRot_ByIdentity_DoesNotChangeCoords::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/PrimMeshTests.cs::PrimMeshTests.SurfaceNormal_Box_FirstFace_IsUnitLength::test", + "LibreMetaverse.Rendering.Tests/PrimMesher/PrimMeshTests.cs::PrimMeshTests.SurfaceNormal_InvalidIndex_Throws::test", "LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BAKED_TEXTURE_COUNT_Is11::test", "LibreMetaverse.Tests/BinaryLLSDTests.cs::BinarySDTests.DeserializeArray::test", "LibreMetaverse.Tests/BinaryLLSDTests.cs::BinarySDTests.DeserializeBool::test", @@ -101,5 +146,5 @@ "LibreMetaverse.Tests/XmlLLSDTests.cs::XmlSDTests.DeserializeUUID::test", "LibreMetaverse.Tests/XmlLLSDTests.cs::XmlSDTests.DeserializeUndef::test" ], - "support_passes": 119 + "support_passes": 123 } diff --git a/tools/generate_api_shims.py b/tools/generate_api_shims.py index 416c091..1e6edb0 100644 --- a/tools/generate_api_shims.py +++ b/tools/generate_api_shims.py @@ -38,6 +38,15 @@ TARGETS = { # implementations. The generated module keeps catalog markers and re-exports # the hand-written type so coverage remains deterministic. NATIVE_TYPES = { + "T:LibreMetaverse.PrimMesher.Coord": "crate::prim_mesher::Coord", + "T:LibreMetaverse.PrimMesher.Face": "crate::prim_mesher::Face", + "T:LibreMetaverse.PrimMesher.Path": "crate::prim_mesher::Path", + "T:LibreMetaverse.PrimMesher.PathNode": "crate::prim_mesher::PathNode", + "T:LibreMetaverse.PrimMesher.PrimMesh": "crate::prim_mesher::PrimMesh", + "T:LibreMetaverse.PrimMesher.Profile": "crate::prim_mesher::Profile", + "T:LibreMetaverse.PrimMesher.Quat": "crate::prim_mesher::Quat", + "T:LibreMetaverse.PrimMesher.UVCoord": "crate::prim_mesher::UVCoord", + "T:LibreMetaverse.PrimMesher.ViewerFace": "crate::prim_mesher::ViewerFace", "T:LibreMetaverse.Imaging.Skia.SkiaTextureCodec": "crate::skia_codec::SkiaTextureCodec", "T:LibreMetaverse.Imaging.ManagedImageCreator": "crate::j2k::ManagedImageCreator", "T:LibreMetaverse.Imaging.ManagedImageInterleavedExtensions": "crate::j2k::ManagedImageInterleavedExtensions",