use rustc_hash::FxHashMap as HashMap;
use serde::Deserialize;
use serde_json::{Map, Value};
use std::sync::atomic::{AtomicU64, Ordering};
static GLOBAL_DISTANCE_SOLVE_CYCLE_ID: AtomicU64 = AtomicU64::new(0);
const POINT_LINE_DISTANCE_TYPE: &str = "↥";
#[derive(Clone, Copy, Debug)]
pub struct SketchSolverSettings {
pub tolerance: f64,
pub distance_slide_threshold_ratio: f64,
pub distance_slide_step_ratio: f64,
pub distance_slide_min_step: f64,
pub newton_polish: bool,
}
impl Default for SketchSolverSettings {
fn default() -> Self {
Self {
tolerance: 0.00001,
distance_slide_threshold_ratio: 0.10,
distance_slide_step_ratio: 0.10,
distance_slide_min_step: 0.001,
newton_polish: true,
}
}
}
#[derive(Debug, Deserialize)]
pub struct SolveSketchRequest {
pub sketch: Value,
#[serde(default)]
pub iterations: Option<u32>,
#[serde(default)]
pub remove_implied_duplicates: bool,
#[serde(default)]
pub tolerance: Option<f64>,
#[serde(default)]
pub distance_slide_threshold_ratio: Option<f64>,
#[serde(default)]
pub distance_slide_step_ratio: Option<f64>,
#[serde(default)]
pub distance_slide_min_step: Option<f64>,
#[serde(default)]
pub polish: Option<bool>,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
enum PointKey {
Num(u64),
Str(String),
Bool(bool),
Null,
}
fn point_key(value: &Value) -> PointKey {
match value {
Value::Number(number) => match number.as_f64() {
Some(float) => PointKey::Num(sig_bits(float)),
None => PointKey::Null,
},
Value::String(text) => PointKey::Str(text.clone()),
Value::Bool(flag) => PointKey::Bool(*flag),
_ => PointKey::Null,
}
}
const TANGENT_TYPE: &str = "⌒";
const CONCENTRIC_TYPE: &str = "◎";
const EQUAL_RADIUS_TYPE: &str = "⊜";
const COLLINEAR_TYPE: &str = "⋰";
const SYMMETRIC_TYPE: &str = "⋈";
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
enum CType {
Horizontal,
Vertical,
Distance,
PointLine,
EqualDistance,
Parallel,
Perpendicular,
Angle,
Coincident,
PointOnLine,
Midpoint,
Ground,
Tangent,
Concentric,
EqualRadius,
Collinear,
Symmetric,
#[default]
Other,
}
fn ctype_from(type_str: &str) -> CType {
match type_str {
"━" => CType::Horizontal,
"│" => CType::Vertical,
"⟺" => CType::Distance,
POINT_LINE_DISTANCE_TYPE => CType::PointLine,
"⇌" => CType::EqualDistance,
"∥" => CType::Parallel,
"⟂" => CType::Perpendicular,
"∠" => CType::Angle,
"≡" => CType::Coincident,
"⏛" => CType::PointOnLine,
"⋯" => CType::Midpoint,
"⏚" => CType::Ground,
TANGENT_TYPE => CType::Tangent,
CONCENTRIC_TYPE => CType::Concentric,
EQUAL_RADIUS_TYPE => CType::EqualRadius,
COLLINEAR_TYPE => CType::Collinear,
SYMMETRIC_TYPE => CType::Symmetric,
_ => CType::Other,
}
}
fn is_distance_ctype(ctype: CType) -> bool {
matches!(ctype, CType::Distance | CType::PointLine)
}
fn is_spline_geometry_type(type_str: &str) -> bool {
type_str == "bezier" || type_str == "spline"
}
type SigEntry = Option<(u64, u64, bool)>;
#[derive(Clone, Copy, Debug, Default)]
enum PrevSolve {
#[default]
Missing,
Num(f64),
OtherType,
}
#[derive(Clone, Debug, Default)]
enum PrevPoints {
#[default]
Missing,
PresentNonString,
Str(String),
Bits(Vec<SigEntry>, String),
}
#[derive(Debug, Default)]
struct HotConstraint {
raw: Map<String, Value>,
ctype: CType,
type_str: Option<String>,
point_ids: Vec<Value>,
point_idx: Vec<Option<usize>>,
points_written: bool,
temporary: bool,
value_num: Option<f64>,
value_parsed: f64,
value_nullish: bool,
value_cv: f64,
value_written: bool,
status_solved: bool,
status_written: bool,
error: Option<Value>,
error_written: bool,
prev_solve: PrevSolve,
prev_solve_written: bool,
prev_points: PrevPoints,
prev_points_written: bool,
req_target: Option<f64>,
req_target_written: bool,
app_target: Option<f64>,
app_target_written: bool,
throttle_true: bool,
throttle_truthy: bool,
throttle_written: bool,
pass_token: Option<String>,
pass_token_written: bool,
line_sign: f64,
line_sign_written: bool,
}
enum Filter<'a> {
All,
Type(CType, &'a str),
}
#[derive(Clone, Debug)]
struct EnginePoint {
id: Value,
x: f64,
y: f64,
fixed: bool,
construction: bool,
external_reference: bool,
}
type CResult = Result<Value, String>;
struct Engine {
points: Vec<EnginePoint>,
geometries: Vec<Value>,
constraints: Vec<HotConstraint>,
settings: SketchSolverSettings,
pass_token: String,
sig_scratch_before: Vec<SigEntry>,
sig_scratch_after: Vec<SigEntry>,
}
#[path = "sketch_solver/jsnum.rs"]
mod jsnum;
pub use jsnum::fmt_id as sketch_id_key;
#[path = "sketch_solver/hot_constraint.rs"]
mod hot_constraint;
#[path = "sketch_solver/engine.rs"]
mod engine;
#[path = "sketch_solver/constraints_basic.rs"]
mod constraints_basic;
#[path = "sketch_solver/constraints_shape.rs"]
mod constraints_shape;
#[path = "sketch_solver/dof_polish.rs"]
mod dof_polish;
#[path = "sketch_solver/api.rs"]
mod api;
use jsnum::*;
pub use api::{solve_sketch, solve_sketch_from_json};