|
|
|
|
@ -1,3 +1,9 @@
|
|
|
|
|
//! 多边形、样条离散和填充四边形转换。
|
|
|
|
|
//!
|
|
|
|
|
//! 普通 POLYLINE/LWPOLYLINE 直接保存顶点;SPLINE 按命令行采样步数离散;
|
|
|
|
|
//! SOLID/TRACE 恢复 DXF 特有的顶点顺序后输出填充多边形。带 bulge 的混合
|
|
|
|
|
//! 多段线通常由上层 `qelmt::mod` 按线段和圆弧拆分。
|
|
|
|
|
|
|
|
|
|
use super::{two_dec, ScaleEntity};
|
|
|
|
|
use dxf::entities::{LwPolyline, Polyline, Solid, Spline, Trace};
|
|
|
|
|
use dxf::Point as DxfPoint;
|
|
|
|
|
@ -5,24 +11,23 @@ use hex_color::HexColor;
|
|
|
|
|
use simple_xml_builder::XMLElement;
|
|
|
|
|
use std::ops::{Add, Mul};
|
|
|
|
|
|
|
|
|
|
//wait Why do I have a coordinate AND a Point struct, that are
|
|
|
|
|
//essentially the same. It's been a couple of months, but I'm not
|
|
|
|
|
//seeing why I would have done this....almost makes me wondering
|
|
|
|
|
//if I started, then stopped, and then didn't realize where I left off
|
|
|
|
|
//and started again but used a different name...?
|
|
|
|
|
//Might need to take a closer look and clean this up.
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
/// 最终写入 QET `<polygon>` 的坐标。
|
|
|
|
|
pub struct Coordinate {
|
|
|
|
|
pub x: f64,
|
|
|
|
|
pub y: f64,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Copy, Clone, Debug)]
|
|
|
|
|
/// `bspline` crate 计算控制点时使用的轻量二维向量。
|
|
|
|
|
///
|
|
|
|
|
/// 它与 `Coordinate` 外形相似,但实现了样条求值所需的加法和数乘。
|
|
|
|
|
pub struct Point {
|
|
|
|
|
pub x: f64,
|
|
|
|
|
pub y: f64,
|
|
|
|
|
}
|
|
|
|
|
impl Point {
|
|
|
|
|
/// 创建样条计算点。
|
|
|
|
|
pub fn new(x: f64, y: f64) -> Point {
|
|
|
|
|
Point { x, y }
|
|
|
|
|
}
|
|
|
|
|
@ -47,6 +52,7 @@ impl Add for Point {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
/// QET 多边形,包括顶点、闭合状态和绘制样式。
|
|
|
|
|
pub struct Polygon {
|
|
|
|
|
style: String,
|
|
|
|
|
antialias: bool,
|
|
|
|
|
@ -66,8 +72,7 @@ impl From<&Polyline> for Polygon {
|
|
|
|
|
})
|
|
|
|
|
.collect(),
|
|
|
|
|
closed: poly.is_closed(),
|
|
|
|
|
//in the original code antialias is always set to false...I'm guessing for performance
|
|
|
|
|
//reasons...I'm trying to think if there is a time we might want to turn it on?
|
|
|
|
|
// 与现有 ELMT 输出保持一致,基础图元默认关闭抗锯齿。
|
|
|
|
|
antialias: false,
|
|
|
|
|
style: "line-style:normal;filling:none;color:black".into(),
|
|
|
|
|
}
|
|
|
|
|
@ -81,7 +86,7 @@ impl From<&LwPolyline> for Polygon {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Polygon {
|
|
|
|
|
/// 从DXF LwPolyline实体创建Polygon,支持自定义颜色
|
|
|
|
|
/// 从 LWPOLYLINE 顶点创建多边形并保留闭合标志和颜色。
|
|
|
|
|
pub fn from_lwpolyline_with_color(poly: &LwPolyline, color: HexColor) -> Self {
|
|
|
|
|
Polygon {
|
|
|
|
|
coordinates: poly
|
|
|
|
|
@ -93,8 +98,7 @@ impl Polygon {
|
|
|
|
|
})
|
|
|
|
|
.collect(),
|
|
|
|
|
closed: poly.is_closed(),
|
|
|
|
|
//in the original code antialias is always set to false...I'm guessing for performance
|
|
|
|
|
//reasons...I'm trying to think if there is a time we might want to turn it on?
|
|
|
|
|
// 与现有 ELMT 输出保持一致,基础图元默认关闭抗锯齿。
|
|
|
|
|
antialias: false,
|
|
|
|
|
style: format!(
|
|
|
|
|
"line-style:normal;filling:none;color:{}",
|
|
|
|
|
@ -106,6 +110,7 @@ impl Polygon {
|
|
|
|
|
|
|
|
|
|
impl From<(&Spline, u32)> for Polygon {
|
|
|
|
|
fn from((spline, spline_step): (&Spline, u32)) -> Self {
|
|
|
|
|
// 将 dxf crate 的控制点和节点向量复制为 bspline crate 所需的类型。
|
|
|
|
|
let mut i: usize = 0;
|
|
|
|
|
let mut points: Vec<Point> = Vec::new();
|
|
|
|
|
for _a in &spline.control_points {
|
|
|
|
|
@ -129,10 +134,7 @@ impl From<(&Spline, u32)> for Polygon {
|
|
|
|
|
let step: f64 =
|
|
|
|
|
(curr_spline.knot_domain().1 - curr_spline.knot_domain().0) / (spline_step as f64);
|
|
|
|
|
|
|
|
|
|
//there is probably a way to clean up some of this logic and use iterators
|
|
|
|
|
//although it looks like step_by doesn't work on a f64 range...hmmm
|
|
|
|
|
//but I haven't inspected it too closely, and for now am pretty much just duplicating
|
|
|
|
|
//it as antonioaja had it
|
|
|
|
|
// 浮点参数域不能使用整数 step_by,因此显式递增参数并采样曲线。
|
|
|
|
|
let coordinates = {
|
|
|
|
|
let mut coords = Vec::with_capacity(
|
|
|
|
|
((curr_spline.knot_domain().1 - curr_spline.knot_domain().0) / step) as usize + 1,
|
|
|
|
|
@ -153,8 +155,7 @@ impl From<(&Spline, u32)> for Polygon {
|
|
|
|
|
Polygon {
|
|
|
|
|
coordinates,
|
|
|
|
|
closed: spline.is_closed(),
|
|
|
|
|
//in the original code antialias is always set to false...I'm guessing for performance
|
|
|
|
|
//reasons...I'm trying to think if there is a time we might want to turn it on?
|
|
|
|
|
// 与现有 ELMT 输出保持一致,基础图元默认关闭抗锯齿。
|
|
|
|
|
antialias: false,
|
|
|
|
|
style: "line-style:normal;filling:none;color:black".into(),
|
|
|
|
|
}
|
|
|
|
|
@ -185,6 +186,7 @@ impl From<&Trace> for Polygon {
|
|
|
|
|
|
|
|
|
|
impl From<&Polygon> for XMLElement {
|
|
|
|
|
fn from(poly: &Polygon) -> Self {
|
|
|
|
|
// QET 用 x1/y1、x2/y2... 属性序列表示多边形顶点。
|
|
|
|
|
let mut poly_xml: XMLElement = XMLElement::new("polygon");
|
|
|
|
|
|
|
|
|
|
for (count, coord) in poly.coordinates.iter().enumerate() {
|
|
|
|
|
@ -192,7 +194,7 @@ impl From<&Polygon> for XMLElement {
|
|
|
|
|
poly_xml.add_attribute(format!("y{}", (count + 1)), two_dec(coord.y));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//closed defaults to true, don't need to write it out unless it's false
|
|
|
|
|
// closed=true 是 QET 默认值,仅开放多边形需要显式写 false。
|
|
|
|
|
if !poly.closed {
|
|
|
|
|
poly_xml.add_attribute("closed", poly.closed);
|
|
|
|
|
}
|
|
|
|
|
@ -228,6 +230,7 @@ impl Polygon {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 让上层统一追加线型或颜色样式。
|
|
|
|
|
pub fn update_line_style<F>(&mut self, update_fn: F)
|
|
|
|
|
where
|
|
|
|
|
F: FnOnce(&mut String),
|
|
|
|
|
@ -235,6 +238,7 @@ impl Polygon {
|
|
|
|
|
update_fn(&mut self.style);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 把毫米线宽归一化后写入样式串。
|
|
|
|
|
pub fn update_line_width_mm(&mut self, line_width_mm: f64) {
|
|
|
|
|
super::update_style_line_width_mm(&mut self.style, line_width_mm);
|
|
|
|
|
}
|
|
|
|
|
@ -250,8 +254,7 @@ impl ScaleEntity for Polygon {
|
|
|
|
|
|
|
|
|
|
fn left_bound(&self) -> f64 {
|
|
|
|
|
let min_coord = self.coordinates.iter().min_by(|c1, c2| {
|
|
|
|
|
//if we get a None for the compare, then just returns Greater which will ignore it
|
|
|
|
|
//for finding the minimum
|
|
|
|
|
// NaN 无法比较;把它排到后面,使正常坐标仍可确定最小边界。
|
|
|
|
|
c1.x.partial_cmp(&c2.x)
|
|
|
|
|
.unwrap_or(std::cmp::Ordering::Greater)
|
|
|
|
|
});
|
|
|
|
|
@ -265,8 +268,7 @@ impl ScaleEntity for Polygon {
|
|
|
|
|
|
|
|
|
|
fn right_bound(&self) -> f64 {
|
|
|
|
|
let max_coord = self.coordinates.iter().max_by(|c1, c2| {
|
|
|
|
|
//if we get a None for the compare, then just returns Less which will ignore it
|
|
|
|
|
//for finding the maximum
|
|
|
|
|
// NaN 无法比较;把它排到前面,使正常坐标仍可确定最大边界。
|
|
|
|
|
c1.x.partial_cmp(&c2.x).unwrap_or(std::cmp::Ordering::Less)
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
@ -279,8 +281,7 @@ impl ScaleEntity for Polygon {
|
|
|
|
|
|
|
|
|
|
fn top_bound(&self) -> f64 {
|
|
|
|
|
let min_coord = self.coordinates.iter().min_by(|c1, c2| {
|
|
|
|
|
//if we get a None for the compare, then just returns Greater which will ignore it
|
|
|
|
|
//for finding the minimum
|
|
|
|
|
// NaN 无法比较;把它排到后面,使正常坐标仍可确定最小边界。
|
|
|
|
|
c1.y.partial_cmp(&c2.y)
|
|
|
|
|
.unwrap_or(std::cmp::Ordering::Greater)
|
|
|
|
|
});
|
|
|
|
|
@ -294,8 +295,7 @@ impl ScaleEntity for Polygon {
|
|
|
|
|
|
|
|
|
|
fn bot_bound(&self) -> f64 {
|
|
|
|
|
let max_coord = self.coordinates.iter().max_by(|c1, c2| {
|
|
|
|
|
//if we get a None for the compare, then just returns Less which will ignore it
|
|
|
|
|
//for finding the maximum
|
|
|
|
|
// NaN 无法比较;把它排到前面,使正常坐标仍可确定最大边界。
|
|
|
|
|
c1.y.partial_cmp(&c2.y).unwrap_or(std::cmp::Ordering::Less)
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
@ -308,8 +308,7 @@ impl ScaleEntity for Polygon {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// 判断LwPolyline是否为带圆角的四边形
|
|
|
|
|
/// 条件:恰好8个顶点,闭合,每个顶点的bulge值要么为0(直线段)要么非0(圆弧段)
|
|
|
|
|
/// 判断 LWPOLYLINE 是否符合“四条直边 + 四个圆角”的八顶点特征。
|
|
|
|
|
pub fn is_rounded_rectangle(lwpolyline: &LwPolyline) -> bool {
|
|
|
|
|
// 检查顶点数量
|
|
|
|
|
if lwpolyline.vertices.len() != 8 {
|
|
|
|
|
@ -331,6 +330,7 @@ pub fn decompose_rounded_rectangle(lwpolyline: &LwPolyline) -> Vec<super::Object
|
|
|
|
|
decompose_rounded_rectangle_with_color(lwpolyline, HexColor::BLACK)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 按每个起点的 bulge 把圆角矩形拆成带指定颜色的线段或圆弧。
|
|
|
|
|
pub fn decompose_rounded_rectangle_with_color(lwpolyline: &LwPolyline, color: HexColor) -> Vec<super::Objects> {
|
|
|
|
|
let mut objects = Vec::new();
|
|
|
|
|
let vertices = &lwpolyline.vertices;
|
|
|
|
|
@ -364,11 +364,12 @@ pub fn decompose_rounded_rectangle_with_color(lwpolyline: &LwPolyline, color: He
|
|
|
|
|
objects
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 根据bulge值创建圆弧
|
|
|
|
|
/// 使用黑色样式从一段 bulge 几何创建圆弧。
|
|
|
|
|
fn create_arc_from_bulge(start_x: f64, start_y: f64, end_x: f64, end_y: f64, bulge: f64) -> Option<super::Arc> {
|
|
|
|
|
create_arc_from_bulge_with_color(start_x, start_y, end_x, end_y, bulge, HexColor::BLACK)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 根据弦端点和 bulge 恢复圆心、半径、起始角和角度跨度。
|
|
|
|
|
fn create_arc_from_bulge_with_color(start_x: f64, start_y: f64, end_x: f64, end_y: f64, bulge: f64, color: HexColor) -> Option<super::Arc> {
|
|
|
|
|
if bulge.abs() <= 1e-10 {
|
|
|
|
|
return None;
|
|
|
|
|
|