use crate::entities::{Entity, EntityCommon};
use crate::types::{BoundingBox3D, Color, Handle, LineWeight, Transform, Transparency, Vector2, Vector3};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum HatchPatternType {
UserDefined = 0,
Predefined = 1,
Custom = 2,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum HatchStyleType {
Normal = 0,
Outer = 1,
Ignore = 2,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BoundaryPathFlags {
bits: u32,
}
impl BoundaryPathFlags {
pub const DEFAULT: Self = Self { bits: 0 };
pub const EXTERNAL: Self = Self { bits: 1 };
pub const POLYLINE: Self = Self { bits: 2 };
pub const DERIVED: Self = Self { bits: 4 };
pub const TEXTBOX: Self = Self { bits: 8 };
pub const OUTERMOST: Self = Self { bits: 16 };
pub const NOT_CLOSED: Self = Self { bits: 32 };
pub const SELF_INTERSECTING: Self = Self { bits: 64 };
pub const TEXT_ISLAND: Self = Self { bits: 128 };
pub const DUPLICATE: Self = Self { bits: 256 };
pub fn new() -> Self {
Self::DEFAULT
}
pub fn from_bits(bits: u32) -> Self {
Self { bits }
}
pub fn bits(&self) -> u32 {
self.bits
}
pub fn is_external(&self) -> bool {
self.bits & 1 != 0
}
pub fn is_polyline(&self) -> bool {
self.bits & 2 != 0
}
pub fn is_derived(&self) -> bool {
self.bits & 4 != 0
}
pub fn is_outermost(&self) -> bool {
self.bits & 16 != 0
}
pub fn is_not_closed(&self) -> bool {
self.bits & 32 != 0
}
pub fn set_external(&mut self, value: bool) {
if value {
self.bits |= 1;
} else {
self.bits &= !1;
}
}
pub fn set_polyline(&mut self, value: bool) {
if value {
self.bits |= 2;
} else {
self.bits &= !2;
}
}
pub fn set_derived(&mut self, value: bool) {
if value {
self.bits |= 4;
} else {
self.bits &= !4;
}
}
}
impl Default for BoundaryPathFlags {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum EdgeType {
Polyline = 0,
Line = 1,
CircularArc = 2,
EllipticArc = 3,
Spline = 4,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct LineEdge {
pub start: Vector2,
pub end: Vector2,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CircularArcEdge {
pub center: Vector2,
pub radius: f64,
pub start_angle: f64,
pub end_angle: f64,
pub counter_clockwise: bool,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct EllipticArcEdge {
pub center: Vector2,
pub major_axis_endpoint: Vector2,
pub minor_axis_ratio: f64,
pub start_angle: f64,
pub end_angle: f64,
pub counter_clockwise: bool,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct SplineEdge {
pub degree: i32,
pub rational: bool,
pub periodic: bool,
pub knots: Vec<f64>,
pub control_points: Vec<Vector3>,
pub fit_points: Vec<Vector2>,
pub start_tangent: Vector2,
pub end_tangent: Vector2,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PolylineEdge {
pub vertices: Vec<Vector3>,
pub is_closed: bool,
}
impl PolylineEdge {
pub fn new(vertices: Vec<Vector2>, is_closed: bool) -> Self {
Self {
vertices: vertices.into_iter().map(|v| Vector3::new(v.x, v.y, 0.0)).collect(),
is_closed,
}
}
pub fn add_vertex(&mut self, point: Vector2, bulge: f64) {
self.vertices.push(Vector3::new(point.x, point.y, bulge));
}
pub fn has_bulge(&self) -> bool {
self.vertices.iter().any(|v| v.z.abs() > 1e-10)
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum BoundaryEdge {
Line(LineEdge),
CircularArc(CircularArcEdge),
EllipticArc(EllipticArcEdge),
Spline(SplineEdge),
Polyline(PolylineEdge),
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BoundaryPath {
pub flags: BoundaryPathFlags,
pub edges: Vec<BoundaryEdge>,
pub boundary_handles: Vec<Handle>,
}
impl BoundaryPath {
pub fn new() -> Self {
Self {
flags: BoundaryPathFlags::new(),
edges: Vec::new(),
boundary_handles: Vec::new(),
}
}
pub fn with_flags(flags: BoundaryPathFlags) -> Self {
Self {
flags,
edges: Vec::new(),
boundary_handles: Vec::new(),
}
}
pub fn external() -> Self {
let mut path = Self::new();
path.flags.set_external(true);
path
}
pub fn add_edge(&mut self, edge: BoundaryEdge) {
if matches!(edge, BoundaryEdge::Polyline(_)) {
self.flags.set_polyline(true);
}
self.edges.push(edge);
}
pub fn add_boundary_handle(&mut self, handle: Handle) {
self.boundary_handles.push(handle);
}
pub fn is_polyline(&self) -> bool {
self.edges.len() == 1 && matches!(self.edges[0], BoundaryEdge::Polyline(_))
}
}
impl Default for BoundaryPath {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct HatchPatternLine {
pub angle: f64,
pub base_point: Vector2,
pub offset: Vector2,
pub dash_lengths: Vec<f64>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct HatchPattern {
pub name: String,
pub description: String,
pub lines: Vec<HatchPatternLine>,
}
impl HatchPattern {
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
description: String::new(),
lines: Vec::new(),
}
}
pub fn solid() -> Self {
Self::new("SOLID")
}
pub fn add_line(&mut self, line: HatchPatternLine) {
self.lines.push(line);
}
pub fn update(&mut self, _base_point: Vector2, angle: f64, scale: f64) {
for line in &mut self.lines {
line.angle += angle;
line.offset = line.offset * scale;
for dash in &mut line.dash_lengths {
*dash *= scale;
}
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct GradientColorEntry {
pub value: f64,
pub color: Color,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct HatchGradientPattern {
pub enabled: bool,
pub reserved: i32,
pub angle: f64,
pub shift: f64,
pub is_single_color: bool,
pub color_tint: f64,
pub colors: Vec<GradientColorEntry>,
pub name: String,
}
impl HatchGradientPattern {
pub fn new() -> Self {
Self {
enabled: false,
reserved: 0,
angle: 0.0,
shift: 0.0,
is_single_color: false,
color_tint: 0.0,
colors: Vec::new(),
name: String::new(),
}
}
pub fn is_enabled(&self) -> bool {
self.enabled
}
pub fn add_color(&mut self, value: f64, color: Color) {
self.colors.push(GradientColorEntry { value, color });
}
}
impl Default for HatchGradientPattern {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Hatch {
pub common: EntityCommon,
pub elevation: f64,
pub normal: Vector3,
pub pattern: HatchPattern,
pub is_solid: bool,
pub is_associative: bool,
pub pattern_type: HatchPatternType,
pub pattern_angle: f64,
pub pattern_scale: f64,
pub is_double: bool,
pub style: HatchStyleType,
pub paths: Vec<BoundaryPath>,
pub seed_points: Vec<Vector2>,
pub pixel_size: f64,
pub gradient_color: HatchGradientPattern,
}
impl Hatch {
pub fn new() -> Self {
Self {
common: EntityCommon::default(),
elevation: 0.0,
normal: Vector3::new(0.0, 0.0, 1.0),
pattern: HatchPattern::solid(),
is_solid: true,
is_associative: false,
pattern_type: HatchPatternType::Predefined,
pattern_angle: 0.0,
pattern_scale: 1.0,
is_double: false,
style: HatchStyleType::Normal,
paths: Vec::new(),
seed_points: Vec::new(),
pixel_size: 0.0,
gradient_color: HatchGradientPattern::new(),
}
}
pub fn solid() -> Self {
let mut hatch = Self::new();
hatch.is_solid = true;
hatch.pattern = HatchPattern::solid();
hatch
}
pub fn with_pattern(pattern: HatchPattern) -> Self {
let mut hatch = Self::new();
hatch.is_solid = false;
hatch.pattern = pattern;
hatch
}
pub fn with_pattern_angle(mut self, angle: f64) -> Self {
self.pattern_angle = angle;
self.pattern.update(Vector2::new(0.0, 0.0), angle, self.pattern_scale);
self
}
pub fn with_pattern_scale(mut self, scale: f64) -> Self {
self.pattern_scale = scale;
self.pattern.update(Vector2::new(0.0, 0.0), self.pattern_angle, scale);
self
}
pub fn with_normal(mut self, normal: Vector3) -> Self {
self.normal = normal;
self
}
pub fn with_elevation(mut self, elevation: f64) -> Self {
self.elevation = elevation;
self
}
pub fn add_path(&mut self, path: BoundaryPath) {
self.paths.push(path);
}
pub fn add_seed_point(&mut self, point: Vector2) {
self.seed_points.push(point);
}
pub fn set_pattern_angle(&mut self, angle: f64) {
self.pattern_angle = angle;
self.pattern.update(Vector2::new(0.0, 0.0), angle, self.pattern_scale);
}
pub fn set_pattern_scale(&mut self, scale: f64) {
self.pattern_scale = scale;
self.pattern.update(Vector2::new(0.0, 0.0), self.pattern_angle, scale);
}
pub fn path_count(&self) -> usize {
self.paths.len()
}
pub fn has_paths(&self) -> bool {
!self.paths.is_empty()
}
}
impl Default for Hatch {
fn default() -> Self {
Self::new()
}
}
impl Entity for Hatch {
fn handle(&self) -> Handle {
self.common.handle
}
fn set_handle(&mut self, handle: Handle) {
self.common.handle = handle;
}
fn layer(&self) -> &str {
&self.common.layer
}
fn set_layer(&mut self, layer: String) {
self.common.layer = layer;
}
fn color(&self) -> Color {
self.common.color
}
fn set_color(&mut self, color: Color) {
self.common.color = color;
}
fn line_weight(&self) -> LineWeight {
self.common.line_weight
}
fn set_line_weight(&mut self, weight: LineWeight) {
self.common.line_weight = weight;
}
fn transparency(&self) -> Transparency {
self.common.transparency
}
fn set_transparency(&mut self, transparency: Transparency) {
self.common.transparency = transparency;
}
fn is_invisible(&self) -> bool {
self.common.invisible
}
fn set_invisible(&mut self, invisible: bool) {
self.common.invisible = invisible;
}
fn bounding_box(&self) -> BoundingBox3D {
let mut all_points = Vec::new();
for path in &self.paths {
for edge in &path.edges {
match edge {
BoundaryEdge::Line(line) => {
all_points.push(Vector3::new(line.start.x, line.start.y, self.elevation));
all_points.push(Vector3::new(line.end.x, line.end.y, self.elevation));
}
BoundaryEdge::CircularArc(arc) => {
all_points.push(Vector3::new(arc.center.x - arc.radius, arc.center.y - arc.radius, self.elevation));
all_points.push(Vector3::new(arc.center.x + arc.radius, arc.center.y + arc.radius, self.elevation));
}
BoundaryEdge::EllipticArc(ellipse) => {
let major_len = ellipse.major_axis_endpoint.length();
all_points.push(Vector3::new(ellipse.center.x - major_len, ellipse.center.y - major_len, self.elevation));
all_points.push(Vector3::new(ellipse.center.x + major_len, ellipse.center.y + major_len, self.elevation));
}
BoundaryEdge::Spline(spline) => {
for cp in &spline.control_points {
all_points.push(Vector3::new(cp.x, cp.y, self.elevation));
}
}
BoundaryEdge::Polyline(poly) => {
for v in &poly.vertices {
all_points.push(Vector3::new(v.x, v.y, self.elevation));
}
}
}
}
}
if all_points.is_empty() {
BoundingBox3D::new(Vector3::new(0.0, 0.0, 0.0), Vector3::new(0.0, 0.0, 0.0))
} else {
BoundingBox3D::from_points(&all_points).unwrap_or_else(|| BoundingBox3D::new(Vector3::new(0.0, 0.0, 0.0), Vector3::new(0.0, 0.0, 0.0)))
}
}
fn translate(&mut self, offset: Vector3) {
super::translate::translate_hatch(self, offset);
}
fn entity_type(&self) -> &'static str {
"HATCH"
}
fn apply_transform(&mut self, transform: &Transform) {
super::transform::transform_hatch(self, transform);
}
fn apply_mirror(&mut self, transform: &crate::types::Transform) {
super::mirror::mirror_hatch(self, transform);
}
}