1use crate::entities::{Entity, EntityCommon};
4use crate::types::{BoundingBox3D, Color, Handle, LineWeight, Transform, Transparency, Vector2, Vector3};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9pub enum HatchPatternType {
10 UserDefined = 0,
12 Predefined = 1,
14 Custom = 2,
16}
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
21pub enum HatchStyleType {
22 Normal = 0,
24 Outer = 1,
26 Ignore = 2,
28}
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
33pub struct BoundaryPathFlags {
34 bits: u32,
35}
36
37impl BoundaryPathFlags {
38 pub const DEFAULT: Self = Self { bits: 0 };
39 pub const EXTERNAL: Self = Self { bits: 1 };
40 pub const POLYLINE: Self = Self { bits: 2 };
41 pub const DERIVED: Self = Self { bits: 4 };
42 pub const TEXTBOX: Self = Self { bits: 8 };
43 pub const OUTERMOST: Self = Self { bits: 16 };
44 pub const NOT_CLOSED: Self = Self { bits: 32 };
45 pub const SELF_INTERSECTING: Self = Self { bits: 64 };
46 pub const TEXT_ISLAND: Self = Self { bits: 128 };
47 pub const DUPLICATE: Self = Self { bits: 256 };
48
49 pub fn new() -> Self {
50 Self::DEFAULT
51 }
52
53 pub fn from_bits(bits: u32) -> Self {
55 Self { bits }
56 }
57
58 pub fn bits(&self) -> u32 {
60 self.bits
61 }
62
63 pub fn is_external(&self) -> bool {
64 self.bits & 1 != 0
65 }
66
67 pub fn is_polyline(&self) -> bool {
68 self.bits & 2 != 0
69 }
70
71 pub fn is_derived(&self) -> bool {
72 self.bits & 4 != 0
73 }
74
75 pub fn is_outermost(&self) -> bool {
76 self.bits & 16 != 0
77 }
78
79 pub fn is_not_closed(&self) -> bool {
80 self.bits & 32 != 0
81 }
82
83 pub fn set_external(&mut self, value: bool) {
84 if value {
85 self.bits |= 1;
86 } else {
87 self.bits &= !1;
88 }
89 }
90
91 pub fn set_polyline(&mut self, value: bool) {
92 if value {
93 self.bits |= 2;
94 } else {
95 self.bits &= !2;
96 }
97 }
98
99 pub fn set_derived(&mut self, value: bool) {
100 if value {
101 self.bits |= 4;
102 } else {
103 self.bits &= !4;
104 }
105 }
106}
107
108impl Default for BoundaryPathFlags {
109 fn default() -> Self {
110 Self::new()
111 }
112}
113
114#[derive(Debug, Clone, Copy, PartialEq, Eq)]
116#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
117pub enum EdgeType {
118 Polyline = 0,
119 Line = 1,
120 CircularArc = 2,
121 EllipticArc = 3,
122 Spline = 4,
123}
124
125#[derive(Debug, Clone, PartialEq)]
127#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
128pub struct LineEdge {
129 pub start: Vector2,
131 pub end: Vector2,
133}
134
135#[derive(Debug, Clone, PartialEq)]
137#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
138pub struct CircularArcEdge {
139 pub center: Vector2,
141 pub radius: f64,
143 pub start_angle: f64,
145 pub end_angle: f64,
147 pub counter_clockwise: bool,
149}
150
151#[derive(Debug, Clone, PartialEq)]
153#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
154pub struct EllipticArcEdge {
155 pub center: Vector2,
157 pub major_axis_endpoint: Vector2,
159 pub minor_axis_ratio: f64,
161 pub start_angle: f64,
163 pub end_angle: f64,
165 pub counter_clockwise: bool,
167}
168
169#[derive(Debug, Clone, PartialEq)]
171#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
172pub struct SplineEdge {
173 pub degree: i32,
175 pub rational: bool,
177 pub periodic: bool,
179 pub knots: Vec<f64>,
181 pub control_points: Vec<Vector3>,
183 pub fit_points: Vec<Vector2>,
185 pub start_tangent: Vector2,
187 pub end_tangent: Vector2,
189}
190
191#[derive(Debug, Clone, PartialEq)]
193#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
194pub struct PolylineEdge {
195 pub vertices: Vec<Vector3>,
197 pub is_closed: bool,
199}
200
201impl PolylineEdge {
202 pub fn new(vertices: Vec<Vector2>, is_closed: bool) -> Self {
204 Self {
205 vertices: vertices.into_iter().map(|v| Vector3::new(v.x, v.y, 0.0)).collect(),
206 is_closed,
207 }
208 }
209
210 pub fn add_vertex(&mut self, point: Vector2, bulge: f64) {
212 self.vertices.push(Vector3::new(point.x, point.y, bulge));
213 }
214
215 pub fn has_bulge(&self) -> bool {
217 self.vertices.iter().any(|v| v.z.abs() > 1e-10)
218 }
219}
220
221#[derive(Debug, Clone, PartialEq)]
223#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
224pub enum BoundaryEdge {
225 Line(LineEdge),
226 CircularArc(CircularArcEdge),
227 EllipticArc(EllipticArcEdge),
228 Spline(SplineEdge),
229 Polyline(PolylineEdge),
230}
231
232#[derive(Debug, Clone, PartialEq)]
234#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
235pub struct BoundaryPath {
236 pub flags: BoundaryPathFlags,
238 pub edges: Vec<BoundaryEdge>,
240 pub boundary_handles: Vec<Handle>,
242}
243
244impl BoundaryPath {
245 pub fn new() -> Self {
247 Self {
248 flags: BoundaryPathFlags::new(),
249 edges: Vec::new(),
250 boundary_handles: Vec::new(),
251 }
252 }
253
254 pub fn with_flags(flags: BoundaryPathFlags) -> Self {
256 Self {
257 flags,
258 edges: Vec::new(),
259 boundary_handles: Vec::new(),
260 }
261 }
262
263 pub fn external() -> Self {
265 let mut path = Self::new();
266 path.flags.set_external(true);
267 path
268 }
269
270 pub fn add_edge(&mut self, edge: BoundaryEdge) {
272 if matches!(edge, BoundaryEdge::Polyline(_)) {
274 self.flags.set_polyline(true);
275 }
276 self.edges.push(edge);
277 }
278
279 pub fn add_boundary_handle(&mut self, handle: Handle) {
281 self.boundary_handles.push(handle);
282 }
283
284 pub fn is_polyline(&self) -> bool {
286 self.edges.len() == 1 && matches!(self.edges[0], BoundaryEdge::Polyline(_))
287 }
288}
289
290impl Default for BoundaryPath {
291 fn default() -> Self {
292 Self::new()
293 }
294}
295
296#[derive(Debug, Clone, PartialEq)]
298#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
299pub struct HatchPatternLine {
300 pub angle: f64,
302 pub base_point: Vector2,
304 pub offset: Vector2,
306 pub dash_lengths: Vec<f64>,
308}
309
310#[derive(Debug, Clone, PartialEq)]
312#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
313pub struct HatchPattern {
314 pub name: String,
316 pub description: String,
318 pub lines: Vec<HatchPatternLine>,
320}
321
322impl HatchPattern {
323 pub fn new(name: impl Into<String>) -> Self {
325 Self {
326 name: name.into(),
327 description: String::new(),
328 lines: Vec::new(),
329 }
330 }
331
332 pub fn solid() -> Self {
334 Self::new("SOLID")
335 }
336
337 pub fn add_line(&mut self, line: HatchPatternLine) {
339 self.lines.push(line);
340 }
341
342 pub fn update(&mut self, _base_point: Vector2, angle: f64, scale: f64) {
344 for line in &mut self.lines {
345 line.angle += angle;
346 line.offset = line.offset * scale;
347 for dash in &mut line.dash_lengths {
348 *dash *= scale;
349 }
350 }
351 }
352}
353
354#[derive(Debug, Clone, PartialEq)]
356#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
357pub struct GradientColorEntry {
358 pub value: f64,
360 pub color: Color,
362}
363
364#[derive(Debug, Clone, PartialEq)]
366#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
367pub struct HatchGradientPattern {
368 pub enabled: bool,
370 pub reserved: i32,
372 pub angle: f64,
374 pub shift: f64,
376 pub is_single_color: bool,
378 pub color_tint: f64,
380 pub colors: Vec<GradientColorEntry>,
382 pub name: String,
384}
385
386impl HatchGradientPattern {
387 pub fn new() -> Self {
388 Self {
389 enabled: false,
390 reserved: 0,
391 angle: 0.0,
392 shift: 0.0,
393 is_single_color: false,
394 color_tint: 0.0,
395 colors: Vec::new(),
396 name: String::new(),
397 }
398 }
399
400 pub fn is_enabled(&self) -> bool {
402 self.enabled
403 }
404
405 pub fn add_color(&mut self, value: f64, color: Color) {
407 self.colors.push(GradientColorEntry { value, color });
408 }
409}
410
411impl Default for HatchGradientPattern {
412 fn default() -> Self {
413 Self::new()
414 }
415}
416
417#[derive(Debug, Clone, PartialEq)]
421#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
422pub struct Hatch {
423 pub common: EntityCommon,
424 pub elevation: f64,
426 pub normal: Vector3,
428 pub pattern: HatchPattern,
430 pub is_solid: bool,
432 pub is_associative: bool,
434 pub pattern_type: HatchPatternType,
436 pub pattern_angle: f64,
438 pub pattern_scale: f64,
440 pub is_double: bool,
442 pub style: HatchStyleType,
444 pub paths: Vec<BoundaryPath>,
446 pub seed_points: Vec<Vector2>,
448 pub pixel_size: f64,
450 pub gradient_color: HatchGradientPattern,
452}
453
454impl Hatch {
455 pub fn new() -> Self {
457 Self {
458 common: EntityCommon::default(),
459 elevation: 0.0,
460 normal: Vector3::new(0.0, 0.0, 1.0),
461 pattern: HatchPattern::solid(),
462 is_solid: true,
463 is_associative: false,
464 pattern_type: HatchPatternType::Predefined,
465 pattern_angle: 0.0,
466 pattern_scale: 1.0,
467 is_double: false,
468 style: HatchStyleType::Normal,
469 paths: Vec::new(),
470 seed_points: Vec::new(),
471 pixel_size: 0.0,
472 gradient_color: HatchGradientPattern::new(),
473 }
474 }
475
476 pub fn solid() -> Self {
478 let mut hatch = Self::new();
479 hatch.is_solid = true;
480 hatch.pattern = HatchPattern::solid();
481 hatch
482 }
483
484 pub fn with_pattern(pattern: HatchPattern) -> Self {
486 let mut hatch = Self::new();
487 hatch.is_solid = false;
488 hatch.pattern = pattern;
489 hatch
490 }
491
492 pub fn with_pattern_angle(mut self, angle: f64) -> Self {
494 self.pattern_angle = angle;
495 self.pattern.update(Vector2::new(0.0, 0.0), angle, self.pattern_scale);
496 self
497 }
498
499 pub fn with_pattern_scale(mut self, scale: f64) -> Self {
501 self.pattern_scale = scale;
502 self.pattern.update(Vector2::new(0.0, 0.0), self.pattern_angle, scale);
503 self
504 }
505
506 pub fn with_normal(mut self, normal: Vector3) -> Self {
508 self.normal = normal;
509 self
510 }
511
512 pub fn with_elevation(mut self, elevation: f64) -> Self {
514 self.elevation = elevation;
515 self
516 }
517
518 pub fn add_path(&mut self, path: BoundaryPath) {
520 self.paths.push(path);
521 }
522
523 pub fn add_seed_point(&mut self, point: Vector2) {
525 self.seed_points.push(point);
526 }
527
528 pub fn set_pattern_angle(&mut self, angle: f64) {
530 self.pattern_angle = angle;
531 self.pattern.update(Vector2::new(0.0, 0.0), angle, self.pattern_scale);
532 }
533
534 pub fn set_pattern_scale(&mut self, scale: f64) {
536 self.pattern_scale = scale;
537 self.pattern.update(Vector2::new(0.0, 0.0), self.pattern_angle, scale);
538 }
539
540 pub fn path_count(&self) -> usize {
542 self.paths.len()
543 }
544
545 pub fn has_paths(&self) -> bool {
547 !self.paths.is_empty()
548 }
549}
550
551impl Default for Hatch {
552 fn default() -> Self {
553 Self::new()
554 }
555}
556
557impl Entity for Hatch {
558 fn handle(&self) -> Handle {
559 self.common.handle
560 }
561
562 fn set_handle(&mut self, handle: Handle) {
563 self.common.handle = handle;
564 }
565
566 fn layer(&self) -> &str {
567 &self.common.layer
568 }
569
570 fn set_layer(&mut self, layer: String) {
571 self.common.layer = layer;
572 }
573
574 fn color(&self) -> Color {
575 self.common.color
576 }
577
578 fn set_color(&mut self, color: Color) {
579 self.common.color = color;
580 }
581
582 fn line_weight(&self) -> LineWeight {
583 self.common.line_weight
584 }
585
586 fn set_line_weight(&mut self, weight: LineWeight) {
587 self.common.line_weight = weight;
588 }
589
590 fn transparency(&self) -> Transparency {
591 self.common.transparency
592 }
593
594 fn set_transparency(&mut self, transparency: Transparency) {
595 self.common.transparency = transparency;
596 }
597
598 fn is_invisible(&self) -> bool {
599 self.common.invisible
600 }
601
602 fn set_invisible(&mut self, invisible: bool) {
603 self.common.invisible = invisible;
604 }
605
606 fn bounding_box(&self) -> BoundingBox3D {
607 let mut all_points = Vec::new();
609
610 for path in &self.paths {
611 for edge in &path.edges {
612 match edge {
613 BoundaryEdge::Line(line) => {
614 all_points.push(Vector3::new(line.start.x, line.start.y, self.elevation));
615 all_points.push(Vector3::new(line.end.x, line.end.y, self.elevation));
616 }
617 BoundaryEdge::CircularArc(arc) => {
618 all_points.push(Vector3::new(arc.center.x - arc.radius, arc.center.y - arc.radius, self.elevation));
620 all_points.push(Vector3::new(arc.center.x + arc.radius, arc.center.y + arc.radius, self.elevation));
621 }
622 BoundaryEdge::EllipticArc(ellipse) => {
623 let major_len = ellipse.major_axis_endpoint.length();
625 all_points.push(Vector3::new(ellipse.center.x - major_len, ellipse.center.y - major_len, self.elevation));
626 all_points.push(Vector3::new(ellipse.center.x + major_len, ellipse.center.y + major_len, self.elevation));
627 }
628 BoundaryEdge::Spline(spline) => {
629 for cp in &spline.control_points {
630 all_points.push(Vector3::new(cp.x, cp.y, self.elevation));
631 }
632 }
633 BoundaryEdge::Polyline(poly) => {
634 for v in &poly.vertices {
635 all_points.push(Vector3::new(v.x, v.y, self.elevation));
636 }
637 }
638 }
639 }
640 }
641
642 if all_points.is_empty() {
643 BoundingBox3D::new(Vector3::new(0.0, 0.0, 0.0), Vector3::new(0.0, 0.0, 0.0))
644 } else {
645 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)))
646 }
647 }
648
649 fn translate(&mut self, offset: Vector3) {
650 super::translate::translate_hatch(self, offset);
651 }
652
653 fn entity_type(&self) -> &'static str {
654 "HATCH"
655 }
656
657 fn apply_transform(&mut self, transform: &Transform) {
658 super::transform::transform_hatch(self, transform);
659 }
660
661 fn apply_mirror(&mut self, transform: &crate::types::Transform) {
662 super::mirror::mirror_hatch(self, transform);
663 }
664}