dxfscan 0.1.0

Binary DXF parser with typed entity data and lookup indices
Documentation
// SPDX-License-Identifier: ISC
use crate::point::Point3;
use crate::value::GroupValue;
use alloc::vec::Vec;

/// A SPLINE entity (NURBS curve).
#[derive(Debug, Clone, Default)]
pub struct Spline {
    /// Spline degree.
    ///
    /// Typically 3 for a cubic B-spline. The number of knots must be
    /// `control_points.len() + degree + 1`.
    pub degree: i16,
    /// Spline flags.
    ///
    /// `0b1` = closed, `0b10` = periodic, `0b100` = rational,
    /// `0b1000` = planar, `0b1_0000` = linear (planar bit is also set).
    pub flags: i16,
    /// Knot values.
    ///
    /// A non-decreasing sequence defining the parameter domain. The
    /// length must be `control_points.len() + degree + 1`.
    pub knot_values: Vec<f64>,
    /// Control point weights.
    ///
    /// One weight per control point. Empty for non-rational splines
    /// (all weights implicitly 1.0).
    pub weights: Vec<f64>,
    /// Control points.
    pub control_points: Vec<Point3>,
    /// Fit points.
    ///
    /// Points that the spline is constrained to pass through. These
    /// are an alternative to control points for defining the curve;
    /// both may be present.
    pub fit_points: Vec<Point3>,
    /// Start tangent direction.
    pub start_tangent: Point3,
    /// End tangent direction.
    pub end_tangent: Point3,
    /// Knot tolerance.
    pub knot_tolerance: f64,
    /// Control point tolerance.
    pub control_point_tolerance: f64,
    /// Fit tolerance.
    pub fit_tolerance: f64,
    /// In-progress control point being accumulated.
    current_pt: Option<Point3>,
    /// In-progress fit point being accumulated.
    current_fit_pt: Option<Point3>,
}

impl Spline {
    /// Returns `true` if the closed flag (`0b1`) is set.
    pub fn is_closed(&self) -> bool {
        self.flags & 1 != 0
    }
}

impl Spline {
    pub(crate) fn feed(&mut self, code: u16, val: &GroupValue) {
        match code {
            70 => {
                if let Some(v) = val.as_i16() {
                    self.flags = v;
                }
            }
            71 => {
                if let Some(v) = val.as_i16() {
                    self.degree = v;
                }
            }
            10 => {
                if let Some(pt) = self.current_pt.take() {
                    self.control_points.push(pt);
                }
                self.current_pt = Some(Point3 {
                    x: val.as_f64().unwrap_or(0.0),
                    ..Default::default()
                });
            }
            20 => {
                if let Some(ref mut pt) = self.current_pt {
                    pt.y = val.as_f64().unwrap_or(0.0);
                }
            }
            30 => {
                if let Some(ref mut pt) = self.current_pt {
                    pt.z = val.as_f64().unwrap_or(0.0);
                }
            }
            11 => {
                if let Some(pt) = self.current_fit_pt.take() {
                    self.fit_points.push(pt);
                }
                self.current_fit_pt = Some(Point3 {
                    x: val.as_f64().unwrap_or(0.0),
                    ..Default::default()
                });
            }
            21 => {
                if let Some(ref mut pt) = self.current_fit_pt {
                    pt.y = val.as_f64().unwrap_or(0.0);
                }
            }
            31 => {
                if let Some(ref mut pt) = self.current_fit_pt {
                    pt.z = val.as_f64().unwrap_or(0.0);
                }
            }
            _ => {
                if let Some(v) = val.as_f64() {
                    match code {
                        12 => self.start_tangent.x = v,
                        22 => self.start_tangent.y = v,
                        32 => self.start_tangent.z = v,
                        13 => self.end_tangent.x = v,
                        23 => self.end_tangent.y = v,
                        33 => self.end_tangent.z = v,
                        40 => self.knot_values.push(v),
                        41 => self.weights.push(v),
                        42 => self.knot_tolerance = v,
                        43 => self.control_point_tolerance = v,
                        44 => self.fit_tolerance = v,
                        _ => {}
                    }
                }
            }
        }
    }

    /// Flushes in-progress points. Called by the scanner after the last pair.
    pub(crate) fn finish(&mut self) {
        if let Some(pt) = self.current_pt.take() {
            self.control_points.push(pt);
        }
        if let Some(pt) = self.current_fit_pt.take() {
            self.fit_points.push(pt);
        }
    }
}