|
|
|
@ -4,18 +4,6 @@ use dxf::entities::LwPolyline;
|
|
|
|
use hex_color::HexColor;
|
|
|
|
use hex_color::HexColor;
|
|
|
|
use simple_xml_builder::XMLElement;
|
|
|
|
use simple_xml_builder::XMLElement;
|
|
|
|
|
|
|
|
|
|
|
|
fn normalize_angle(angle: f64) -> f64 {
|
|
|
|
|
|
|
|
let mut norm = angle % 360.0;
|
|
|
|
|
|
|
|
if norm < 0.0 {
|
|
|
|
|
|
|
|
norm += 360.0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
norm
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn qet_start_angle(angle: f64) -> f64 {
|
|
|
|
|
|
|
|
normalize_angle(270.0 - angle)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Arc {
|
|
|
|
pub struct Arc {
|
|
|
|
//need to brush up on my Rust scoping rules, isn't there a way to make this pub to just the module?
|
|
|
|
//need to brush up on my Rust scoping rules, isn't there a way to make this pub to just the module?
|
|
|
|
@ -86,7 +74,7 @@ impl Arc {
|
|
|
|
y: -arc.center.y - arc.radius,
|
|
|
|
y: -arc.center.y - arc.radius,
|
|
|
|
height: arc.radius * 2.0,
|
|
|
|
height: arc.radius * 2.0,
|
|
|
|
width: arc.radius * 2.0,
|
|
|
|
width: arc.radius * 2.0,
|
|
|
|
start: qet_start_angle(normalize_angle(start_angle)),
|
|
|
|
start: if start_angle < 0.0 { -start_angle } else { start_angle },
|
|
|
|
angle: if angle_span < 0.0 { -angle_span } else { angle_span },
|
|
|
|
angle: if angle_span < 0.0 { -angle_span } else { angle_span },
|
|
|
|
|
|
|
|
|
|
|
|
//in the original code antialias is always set to false...I'm guessing for performance
|
|
|
|
//in the original code antialias is always set to false...I'm guessing for performance
|
|
|
|
@ -191,133 +179,94 @@ impl TryFrom<&LwPolyline> for Arc {
|
|
|
|
impl Arc {
|
|
|
|
impl Arc {
|
|
|
|
/// 从DXF LwPolyline实体创建Arc,支持自定义颜色
|
|
|
|
/// 从DXF LwPolyline实体创建Arc,支持自定义颜色
|
|
|
|
pub fn try_from_lwpolyline_with_color(lwpolyline: &LwPolyline, color: HexColor) -> Result<Self, String> {
|
|
|
|
pub fn try_from_lwpolyline_with_color(lwpolyline: &LwPolyline, color: HexColor) -> Result<Self, String> {
|
|
|
|
|
|
|
|
// 检查顶点数量,必须是2个顶点才能形成圆弧
|
|
|
|
if lwpolyline.vertices.len() != 2 {
|
|
|
|
if lwpolyline.vertices.len() != 2 {
|
|
|
|
return Err(format!(
|
|
|
|
return Err(format!("Arc conversion requires exactly 2 vertices, got {}", lwpolyline.vertices.len()));
|
|
|
|
"Arc conversion requires exactly 2 vertices, got {}",
|
|
|
|
|
|
|
|
lwpolyline.vertices.len()
|
|
|
|
|
|
|
|
));
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let v1 = &lwpolyline.vertices[0];
|
|
|
|
let v1 = &lwpolyline.vertices[0];
|
|
|
|
let v2 = &lwpolyline.vertices[1];
|
|
|
|
let v2 = &lwpolyline.vertices[1];
|
|
|
|
|
|
|
|
|
|
|
|
if v1.bulge.abs() <= f64::EPSILON {
|
|
|
|
// 检查第一个顶点是否有bulge值
|
|
|
|
|
|
|
|
if v1.bulge == 0.0 {
|
|
|
|
return Err("No bulge value found for arc conversion".to_string());
|
|
|
|
return Err("No bulge value found for arc conversion".to_string());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let style = if lwpolyline.thickness > 0.1 {
|
|
|
|
// 计算弦长和中点
|
|
|
|
format!(
|
|
|
|
let chord_length = ((v2.x - v1.x).powi(2) + (v2.y - v1.y).powi(2)).sqrt();
|
|
|
|
"line-style:normal;line-weight:normal;filling:none;color:{}",
|
|
|
|
let mid_x = (v1.x + v2.x) / 2.0;
|
|
|
|
color.display_rgb()
|
|
|
|
let mid_y = (v1.y + v2.y) / 2.0;
|
|
|
|
)
|
|
|
|
|
|
|
|
} else if lwpolyline.constant_width > 1.0 {
|
|
|
|
// 根据bulge值计算圆弧参数
|
|
|
|
format!(
|
|
|
|
// bulge = tan(angle/4),其中angle是圆弧的包含角
|
|
|
|
"line-style:normal;line-weight:hight;filling:none;color:{}",
|
|
|
|
let bulge = v1.bulge;
|
|
|
|
color.display_rgb()
|
|
|
|
let included_angle = 4.0 * bulge.atan(); // 圆弧包含角(弧度)
|
|
|
|
)
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// 计算半径
|
|
|
|
format!(
|
|
|
|
let radius = chord_length / (2.0 * (included_angle / 2.0).sin());
|
|
|
|
"line-style:normal;line-weight:thin;filling:none;color:{}",
|
|
|
|
|
|
|
|
color.display_rgb()
|
|
|
|
// 计算圆心位置
|
|
|
|
)
|
|
|
|
// 弦的方向向量
|
|
|
|
};
|
|
|
|
let chord_dx = v2.x - v1.x;
|
|
|
|
|
|
|
|
let chord_dy = v2.y - v1.y;
|
|
|
|
Arc::from_bulge_segment((v1.x, v1.y), (v2.x, v2.y), v1.bulge, style)
|
|
|
|
|
|
|
|
}
|
|
|
|
// 弦的垂直方向(向左旋转90度)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl Arc {
|
|
|
|
|
|
|
|
/// 通过两个端点与 bulge 值生成圆弧
|
|
|
|
|
|
|
|
pub fn from_bulge_segment(
|
|
|
|
|
|
|
|
start: (f64, f64),
|
|
|
|
|
|
|
|
end: (f64, f64),
|
|
|
|
|
|
|
|
bulge: f64,
|
|
|
|
|
|
|
|
style: String,
|
|
|
|
|
|
|
|
) -> Result<Self, String> {
|
|
|
|
|
|
|
|
if bulge.abs() <= f64::EPSILON {
|
|
|
|
|
|
|
|
return Err("Bulge value too small to construct an arc".into());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let chord_dx = end.0 - start.0;
|
|
|
|
|
|
|
|
let chord_dy = end.1 - start.1;
|
|
|
|
|
|
|
|
let chord_length = (chord_dx * chord_dx + chord_dy * chord_dy).sqrt();
|
|
|
|
|
|
|
|
if chord_length <= f64::EPSILON {
|
|
|
|
|
|
|
|
return Err("Arc chord length too small".into());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let half_angle = 2.0 * bulge.atan();
|
|
|
|
|
|
|
|
let sin_half = (half_angle).sin();
|
|
|
|
|
|
|
|
if sin_half.abs() <= f64::EPSILON {
|
|
|
|
|
|
|
|
return Err("Arc sweep too small".into());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
let radius = (chord_length / (2.0 * sin_half)).abs();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let mid_x = (start.0 + end.0) / 2.0;
|
|
|
|
|
|
|
|
let mid_y = (start.1 + end.1) / 2.0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let perp_dx = -chord_dy;
|
|
|
|
let perp_dx = -chord_dy;
|
|
|
|
let perp_dy = chord_dx;
|
|
|
|
let perp_dy = chord_dx;
|
|
|
|
let perp_length = (perp_dx * perp_dx + perp_dy * perp_dy).sqrt();
|
|
|
|
let perp_length = (perp_dx * perp_dx + perp_dy * perp_dy).sqrt();
|
|
|
|
if perp_length <= f64::EPSILON {
|
|
|
|
|
|
|
|
return Err("Cannot determine arc normal".into());
|
|
|
|
// 标准化垂直向量
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let unit_perp_x = perp_dx / perp_length;
|
|
|
|
let unit_perp_x = perp_dx / perp_length;
|
|
|
|
let unit_perp_y = perp_dy / perp_length;
|
|
|
|
let unit_perp_y = perp_dy / perp_length;
|
|
|
|
let center_offset = radius * half_angle.cos();
|
|
|
|
|
|
|
|
|
|
|
|
// 计算圆心到弦中点的距离
|
|
|
|
let (center_x, center_y) = if bulge > 0.0 {
|
|
|
|
let center_distance = radius * (included_angle / 2.0).cos();
|
|
|
|
(
|
|
|
|
|
|
|
|
mid_x + center_offset * unit_perp_x,
|
|
|
|
// 根据bulge的符号确定圆心位置
|
|
|
|
mid_y + center_offset * unit_perp_y,
|
|
|
|
let center_x = if bulge > 0.0 {
|
|
|
|
)
|
|
|
|
mid_x + center_distance * unit_perp_x
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
(
|
|
|
|
mid_x - center_distance * unit_perp_x
|
|
|
|
mid_x - center_offset * unit_perp_x,
|
|
|
|
|
|
|
|
mid_y - center_offset * unit_perp_y,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
let center_y = if bulge > 0.0 {
|
|
|
|
// QET 使用 y 轴向下的坐标系
|
|
|
|
mid_y + center_distance * unit_perp_y
|
|
|
|
let center_y_qet = -center_y;
|
|
|
|
|
|
|
|
let start_y_qet = -start.1;
|
|
|
|
|
|
|
|
let end_y_qet = -end.1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let mut start_angle =
|
|
|
|
|
|
|
|
(start_y_qet - center_y_qet).atan2(start.0 - center_x).to_degrees();
|
|
|
|
|
|
|
|
let mut end_angle = (end_y_qet - center_y_qet).atan2(end.0 - center_x).to_degrees();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
start_angle = normalize_angle(start_angle);
|
|
|
|
|
|
|
|
end_angle = normalize_angle(end_angle);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let ccw_sweep = (end_angle - start_angle).rem_euclid(360.0);
|
|
|
|
|
|
|
|
let cw_sweep = (start_angle - end_angle).rem_euclid(360.0);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let (start_angle, sweep) = if bulge < 0.0
|
|
|
|
|
|
|
|
&& ccw_sweep > 180.0
|
|
|
|
|
|
|
|
&& cw_sweep > f64::EPSILON
|
|
|
|
|
|
|
|
&& cw_sweep < ccw_sweep
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
(end_angle, cw_sweep)
|
|
|
|
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
(start_angle, ccw_sweep)
|
|
|
|
mid_y - center_distance * unit_perp_y
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
if sweep <= f64::EPSILON {
|
|
|
|
// 计算起始角度和结束角度
|
|
|
|
return Err("Arc sweep too small".into());
|
|
|
|
let start_angle = (v1.y - center_y).atan2(v1.x - center_x);
|
|
|
|
|
|
|
|
let end_angle = (v2.y - center_y).atan2(v2.x - center_x);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 将弧度转换为度数
|
|
|
|
|
|
|
|
let mut start_angle_deg = start_angle.to_degrees();
|
|
|
|
|
|
|
|
let mut end_angle_deg = end_angle.to_degrees();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 标准化角度到0-360度范围
|
|
|
|
|
|
|
|
if start_angle_deg < 0.0 {
|
|
|
|
|
|
|
|
start_angle_deg += 360.0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if end_angle_deg < 0.0 {
|
|
|
|
let start_angle = qet_start_angle(start_angle);
|
|
|
|
end_angle_deg += 360.0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 计算角度跨度
|
|
|
|
|
|
|
|
let arc_angle = included_angle.to_degrees().abs();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 创建Arc对象
|
|
|
|
Ok(Arc {
|
|
|
|
Ok(Arc {
|
|
|
|
x: center_x - radius,
|
|
|
|
x: center_x - radius, // 边界框左上角x坐标
|
|
|
|
y: center_y_qet - radius,
|
|
|
|
y: -center_y - radius, // 边界框左上角y坐标(y轴翻转)
|
|
|
|
width: radius * 2.0,
|
|
|
|
width: radius * 2.0, // 边界框宽度
|
|
|
|
height: radius * 2.0,
|
|
|
|
height: radius * 2.0, // 边界框高度
|
|
|
|
start: start_angle,
|
|
|
|
start: start_angle_deg, // 起始角度(度)
|
|
|
|
angle: sweep,
|
|
|
|
angle: arc_angle, // 圆弧角度跨度(度)
|
|
|
|
style,
|
|
|
|
style: if lwpolyline.thickness > 0.1 {
|
|
|
|
|
|
|
|
format!("line-style:normal;line-weight:normal;filling:none;color:{}", color.display_rgb())
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
format!("line-style:normal;line-weight:thin;filling:none;color:{}", color.display_rgb())
|
|
|
|
|
|
|
|
},
|
|
|
|
antialias: false,
|
|
|
|
antialias: false,
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|