修复部分符号的严重偏移问题

qdj
邱德佳 3 weeks ago
parent 5dd4d232e7
commit c2ab2b675f

@ -174,24 +174,63 @@ impl ScaleEntity for DynamicText {
self.align_y *= fact_y;
self.font.scale(fact_y);
self.text_height = self.text_height * fact_y;
self.reference_rectangle_width *= fact_x.abs();
}
fn left_bound(&self) -> f64 {
self.x
self.horizontal_bounds().0
}
fn right_bound(&self) -> f64 {
//todo!()
1.0
self.horizontal_bounds().1
}
fn top_bound(&self) -> f64 {
self.y
self.vertical_bounds().0
}
fn bot_bound(&self) -> f64 {
//todo!()
1.0
self.vertical_bounds().1
}
}
impl DynamicText {
// 文本的绘制位置和对齐点必须同步移动,否则归一化后文字仍会偏离图形。
pub(crate) fn translate(&mut self, offset_x: f64, offset_y: f64) {
self.x += offset_x;
self.y += offset_y;
self.align_x += offset_x;
self.align_y += offset_y;
}
fn horizontal_bounds(&self) -> (f64, f64) {
// 普通 TEXT 没有参考矩形宽度,使用文本内容估算宽度以参与整体包围盒计算。
let width = if self.reference_rectangle_width > 0.0 {
self.reference_rectangle_width
} else {
estimate_text_width(
&self.text,
self.text_height,
self.relative_x_scale_factor,
)
}
.abs();
match self.h_alignment {
HAlignment::Left => (self.x, self.x + width),
HAlignment::Center => (self.x - width / 2.0, self.x + width / 2.0),
HAlignment::Right => (self.x - width, self.x),
}
}
fn vertical_bounds(&self) -> (f64, f64) {
let height = self.text_height.abs();
match self.v_alignment {
VAlignment::Top => (self.y, self.y + height),
VAlignment::Center => (self.y - height / 2.0, self.y + height / 2.0),
VAlignment::Bottom => (self.y - height, self.y),
}
}
}
@ -523,3 +562,55 @@ fn estimate_text_width(text: &str, line_height: f64, relative_x_scale_factor: f6
base_width * scale
}
#[cfg(test)]
mod tests {
use super::{DynamicText, HAlignment, ScaleEntity, VAlignment};
use crate::qelmt::FontInfo;
use hex_color::HexColor;
use uuid::Uuid;
fn dynamic_text() -> DynamicText {
DynamicText {
text: "AB".into(),
info_name: None,
x: -50.0,
y: -40.0,
z: 0.0,
rotation: 0.0,
uuid: Uuid::new_v4(),
h_alignment: HAlignment::Left,
font: FontInfo::default(),
text_from: "UserText".into(),
v_alignment: VAlignment::Bottom,
frame: false,
text_height: 10.0,
text_width: -1.0,
relative_x_scale_factor: 1.0,
keep_visual_rotation: false,
color: HexColor::BLACK,
reference_rectangle_width: 0.0,
align_x: -50.0,
align_y: -40.0,
}
}
#[test]
fn estimated_bounds_stay_near_negative_text_coordinates() {
let text = dynamic_text();
assert_eq!((-50.0, -35.0), text.horizontal_bounds());
assert_eq!((-50.0, -40.0), text.vertical_bounds());
}
#[test]
fn scaling_updates_reference_rectangle_width() {
let mut text = dynamic_text();
text.reference_rectangle_width = 20.0;
text.scale(2.0, 3.0);
assert_eq!((-100.0, -60.0), text.horizontal_bounds());
assert_eq!((-150.0, -120.0), text.vertical_bounds());
}
}

@ -219,6 +219,8 @@ impl Definition {
let description = {
let mut description: Description = (drw, spline_step).into();
description.scale(scale_factor, scale_factor);
// DXF 实体可能位于整张 CAD 图纸的任意绝对坐标,导出元件时需要转换为局部坐标。
description.center_on_origin();
description
};
@ -382,6 +384,42 @@ impl Objects {
_ => Children { slice: [].iter() },
}
}
// 对所有图元递归应用同一平移量,保持块内部图形的相对位置不变。
fn translate(&mut self, offset_x: f64, offset_y: f64) {
match self {
Objects::Arc(arc) => {
arc.x += offset_x;
arc.y += offset_y;
}
Objects::Ellipse(ellipse) => {
ellipse.x += offset_x;
ellipse.y += offset_y;
}
Objects::Polygon(polygon) => {
for coordinate in &mut polygon.coordinates {
coordinate.x += offset_x;
coordinate.y += offset_y;
}
}
Objects::DynamicText(dynamic_text) => dynamic_text.translate(offset_x, offset_y),
Objects::Text(text) => {
text.x += offset_x;
text.y += offset_y;
}
Objects::Line(line) => {
line.x1 += offset_x;
line.y1 += offset_y;
line.x2 += offset_x;
line.y2 += offset_y;
}
Objects::Group(objects) => {
for object in objects {
object.translate(offset_x, offset_y);
}
}
}
}
}
// 结构体,包含一个 stack 字段,存储 Children 迭代器的栈,使用生命周期参数 'a 确保引用的有效性
@ -771,8 +809,7 @@ impl<'a> ObjectsBuilder<'a> {
dtext.scale(self.scale_fact.x, self.scale_fact.y);
dtext.x += self.offset.x;
dtext.y -= self.offset.y;
dtext.translate(self.offset.x, -self.offset.y);
Objects::DynamicText(dtext)
},
@ -813,8 +850,7 @@ impl<'a> ObjectsBuilder<'a> {
dtext.scale(self.scale_fact.x, self.scale_fact.y);
dtext.x += self.offset.x;
dtext.y -= self.offset.y;
dtext.translate(self.offset.x, -self.offset.y);
Objects::DynamicText(dtext)
},
@ -1057,8 +1093,7 @@ impl<'a> ObjectsBuilder<'a> {
dtext.scale(self.scale_fact.x, self.scale_fact.y);
dtext.x += self.offset.x;
dtext.y -= self.offset.y;
dtext.translate(self.offset.x, -self.offset.y);
Objects::DynamicText(dtext)
}),
@ -1117,6 +1152,18 @@ pub struct Description {
objects: Vec<Objects>,
}
impl Description {
// 将包围盒中心移动到局部原点,避免 CAD 中的插入位置泄漏到 QET 元件坐标。
fn center_on_origin(&mut self) {
let offset_x = -((self.left_bound() + self.right_bound()) / 2.0);
let offset_y = -((self.top_bound() + self.bot_bound()) / 2.0);
for object in &mut self.objects {
object.translate(offset_x, offset_y);
}
}
}
// 实现Description的ScaleEntity特征
impl ScaleEntity for Description {
fn scale(&mut self, fact_x: f64, fact_y: f64) {
@ -1149,7 +1196,7 @@ impl ScaleEntity for Description {
});
if let Some(rb) = rb {
rb.left_bound()
rb.right_bound()
} else {
0.0
}
@ -1177,7 +1224,7 @@ impl ScaleEntity for Description {
});
if let Some(bb) = bb {
bb.top_bound()
bb.bot_bound()
} else {
0.0
}
@ -1989,3 +2036,46 @@ pub(crate) fn strip_mtext_control_sequences(input: &str) -> String {
result
}
#[cfg(test)]
mod tests {
use super::{Description, Line, Objects, ScaleEntity};
#[test]
fn description_bounds_use_outer_edges_for_single_group() {
let description = Description {
objects: vec![Objects::Group(vec![Objects::Line(Line::new(
12.0,
-8.0,
42.0,
16.0,
"line-style:normal;line-weight:thin;filling:none;color:black".into(),
))])],
};
assert_eq!(12.0, description.left_bound());
assert_eq!(42.0, description.right_bound());
assert_eq!(-8.0, description.top_bound());
assert_eq!(16.0, description.bot_bound());
}
#[test]
fn centering_description_removes_source_drawing_offset() {
let mut description = Description {
objects: vec![Objects::Group(vec![Objects::Line(Line::new(
1_290.0,
-2_070.0,
1_330.0,
-2_050.0,
"line-style:normal;line-weight:thin;filling:none;color:black".into(),
))])],
};
description.center_on_origin();
assert_eq!(-20.0, description.left_bound());
assert_eq!(20.0, description.right_bound());
assert_eq!(-10.0, description.top_bound());
assert_eq!(10.0, description.bot_bound());
}
}

Loading…
Cancel
Save