use crate::json_support::vec3_or as read_vec3;
use crate::geometry3d::{cross3 as cross, dot3 as dot, sub3 as sub};
pub mod constraint_glyphs;
pub mod dimensions;
pub mod doc;
pub mod external_ref;
pub mod handdraw;
pub mod infer;
pub mod session;
pub mod solve;
pub mod spline;
pub mod tessellate;
pub mod trim;
pub use doc::{SketchConstraint, SketchDiagnostics, SketchDoc, SketchGeometry, SketchPoint};
pub use external_ref::{classify_uv, EdgeLink, ExternalRef};
pub use session::{
constraint_ref, entity_ref_eq, geometry_ref, point_ref, refs_equal, SketchSession,
};
pub use solve::SketchSolverSettings;
pub use tessellate::SketchTessellation;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct PlaneFrame {
pub origin: [f64; 3],
pub x_axis: [f64; 3],
pub y_axis: [f64; 3],
pub z_axis: [f64; 3],
}
impl PlaneFrame {
pub fn xy() -> Self {
Self {
origin: [0.0, 0.0, 0.0],
x_axis: [1.0, 0.0, 0.0],
y_axis: [0.0, 1.0, 0.0],
z_axis: [0.0, 0.0, 1.0],
}
}
pub fn xz() -> Self {
Self::from_normal([0.0, 0.0, 0.0], [0.0, -1.0, 0.0])
}
pub fn yz() -> Self {
Self::from_normal([0.0, 0.0, 0.0], [1.0, 0.0, 0.0])
}
pub fn from_normal(origin: [f64; 3], normal: [f64; 3]) -> Self {
let identity = Self {
origin,
..Self::xy()
};
let Some(z) = normalize(normal) else {
return identity;
};
let world_up = [0.0, 1.0, 0.0];
let ref_up = if dot(z, world_up).abs() > 0.9 {
[1.0, 0.0, 0.0]
} else {
world_up
};
let Some(x) = normalize(cross(ref_up, z)) else {
return identity;
};
let Some(y) = normalize(cross(z, x)) else {
return identity;
};
Self {
origin,
x_axis: x,
y_axis: y,
z_axis: z,
}
}
pub fn from_basis_json(basis: &serde_json::Value) -> Self {
Self {
origin: read_vec3(basis.get("origin"), [0.0, 0.0, 0.0]),
x_axis: read_vec3(basis.get("x"), [1.0, 0.0, 0.0]),
y_axis: read_vec3(basis.get("y"), [0.0, 1.0, 0.0]),
z_axis: read_vec3(basis.get("z"), [0.0, 0.0, 1.0]),
}
}
pub fn to_world(&self, u: f64, v: f64) -> [f64; 3] {
[
self.origin[0] + self.x_axis[0] * u + self.y_axis[0] * v,
self.origin[1] + self.x_axis[1] * u + self.y_axis[1] * v,
self.origin[2] + self.x_axis[2] * u + self.y_axis[2] * v,
]
}
pub fn to_uv(&self, world: [f64; 3]) -> (f64, f64) {
let d = [
world[0] - self.origin[0],
world[1] - self.origin[1],
world[2] - self.origin[2],
];
(dot(d, self.x_axis), dot(d, self.y_axis))
}
}
impl Default for PlaneFrame {
fn default() -> Self {
Self::xy()
}
}
pub fn ray_plane_uv(plane: &PlaneFrame, origin: [f64; 3], dir: [f64; 3]) -> Option<(f64, f64)> {
let n = plane.z_axis;
let denom = dot(dir, n);
if denom.abs() < 1e-9 {
return None; }
let t = dot(sub(plane.origin, origin), n) / denom;
if t <= 0.0 {
return None; }
let hit = [
origin[0] + t * dir[0],
origin[1] + t * dir[1],
origin[2] + t * dir[2],
];
let w = sub(hit, plane.origin);
Some((dot(w, plane.x_axis), dot(w, plane.y_axis)))
}
pub(crate) fn constraint_base_color(
colors: &crate::style::SketchColors,
diag: &SketchDiagnostics,
id: &serde_json::Value,
) -> u32 {
if diag.constraint_conflicting(id) {
colors.conflict
} else {
colors.constraint
}
}
fn normalize(v: [f64; 3]) -> Option<[f64; 3]> {
let len = (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]).sqrt();
if len.is_finite() && len > 1e-12 {
Some([v[0] / len, v[1] / len, v[2] / len])
} else {
None
}
}