feat(display): add model lineweight scale

This commit is contained in:
Hakan Seven committed 2026-09-02 23:03:04 +03:00
1 parent edbc4d8ff8
commit 76bdab1e5b
11 files changed
+66 -19

No files matched your search

+5
View File
@@ -492,6 +492,8 @@ pub(super) struct OpenCADStudio {
crosshair_color: Option<[u8; 3]>,
/// Editable Options buffer for the crosshair colour.
crosshair_color_input: String,
/// Model-space lineweight preview scale, in percent (25..=200).
lineweight_display_scale: i32,
/// Isometric drafting state and active axis pair.
isometric_drafting: bool,
iso_plane: settings::IsoPlane,
@@ -1910,6 +1912,8 @@ pub enum Message {
PickBoxChanged(i32),
/// Set CURSORTYPE from Options.
CursorTypeChanged(settings::CursorType),
/// Set the model-space lineweight preview scale from Options.
LineweightDisplayScaleChanged(i32),
/// Edit the optional crosshair RGB value; blank restores automatic contrast.
CrosshairColorChanged(String),
/// Set the default type/version used when first saving a new drawing.
@@ -3280,6 +3284,7 @@ impl OpenCADStudio {
cursor_type: settings::CursorType::Crosshair,
crosshair_color: None,
crosshair_color_input: String::new(),
lineweight_display_scale: 100,
isometric_drafting: false,
iso_plane: settings::IsoPlane::Left,
snap_angle_deg: 0.0,
+3
View File
@@ -164,6 +164,8 @@ pub struct UserSettings {
pub cursor_type: CursorType,
/// Explicit crosshair RGB. `None` keeps automatic background contrast.
pub crosshair_color: Option<[u8; 3]>,
/// Model-space lineweight preview scale as a percentage.
pub lineweight_display_scale: i32,
/// Isometric drafting changes the grid and crosshair to the active axis pair.
pub isometric_drafting: bool,
pub iso_plane: IsoPlane,
@@ -282,6 +284,7 @@ impl Default for UserSettings {
pick_box: 3,
cursor_type: CursorType::Crosshair,
crosshair_color: None,
lineweight_display_scale: 100,
isometric_drafting: false,
iso_plane: IsoPlane::Left,
snap_angle_deg: 0.0,
+1 -1
View File
@@ -124,7 +124,7 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
self.tab_counter += 1;
self.tabs[0] = crate::app::document::DocumentTab::new_drawing(self.tab_counter);
self.active_tab = 0;
self.apply_bg_default(0);
self.apply_display_defaults(0);
} else {
self.tabs.remove(idx);
if self.active_tab >= self.tabs.len() {
+1 -1
View File
@@ -210,7 +210,7 @@ pub(super) fn on_ribbon_tool_click(&mut self, tool_id: String, event: ModuleEven
self.tabs[0] =
crate::app::document::DocumentTab::new_drawing(self.tab_counter);
self.active_tab = 0;
self.apply_bg_default(0);
self.apply_display_defaults(0);
} else {
self.tabs.remove(idx);
if self.active_tab >= self.tabs.len() {
+13 -13
View File
@@ -364,6 +364,7 @@ impl OpenCADStudio {
pick_box: self.pick_box,
cursor_type: self.cursor_type,
crosshair_color: self.crosshair_color,
lineweight_display_scale: self.lineweight_display_scale,
isometric_drafting: self.isometric_drafting,
iso_plane: self.iso_plane,
snap_angle_deg: self.snap_angle_deg,
@@ -416,6 +417,7 @@ impl OpenCADStudio {
.crosshair_color
.map(crate::app::config::rgb_to_hex)
.unwrap_or_default();
self.lineweight_display_scale = s.lineweight_display_scale.clamp(25, 200);
self.isometric_drafting = s.isometric_drafting;
self.iso_plane = s.iso_plane;
self.snap_angle_deg = if s.snap_angle_deg.is_finite() {
@@ -469,11 +471,10 @@ impl OpenCADStudio {
.take(200)
.map(|(k, v)| (k.clone(), *v))
.collect();
// Push the restored background onto every drawing tab that exists now
// (the start tab and any initial drawing). Tabs created later pick it
// up via `apply_bg_default` at their construction site.
// Push restored display defaults onto every drawing tab that exists now.
// Tabs created later pick them up at their construction site.
for idx in 0..self.tabs.len() {
self.apply_bg_default(idx);
self.apply_display_defaults(idx);
}
self.rebuild_ribbon_modules();
}
@@ -596,19 +597,18 @@ impl OpenCADStudio {
h.object_snap_mode = osmode;
}
/// Apply the persisted default background(s) to tab `idx`. No-op for the
/// start tab or when no default is set. Refreshes the tab's cached wires
/// and meshes so background-adaptive colours pick up the change.
pub(in crate::app) fn apply_bg_default(&mut self, idx: usize) {
/// Apply persisted viewport display defaults to tab `idx`.
pub(in crate::app) fn apply_display_defaults(&mut self, idx: usize) {
let bg = self.default_bg_color;
let paper_bg = self.default_paper_bg_color;
if bg.is_none() && paper_bg.is_none() {
return;
}
let tab = &mut self.tabs[idx];
if tab.is_start {
return;
}
tab.scene.model_lineweight_scale = self.lineweight_display_scale as f32 / 100.0;
if bg.is_none() && paper_bg.is_none() {
return;
}
if let Some(c) = bg {
tab.bg_color = Some(c);
tab.scene.bg_color = c;
@@ -1286,7 +1286,7 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
self.tabs.push(new_tab);
let idx = self.tabs.len() - 1;
self.active_tab = idx;
self.apply_bg_default(idx);
self.apply_display_defaults(idx);
idx
};
@@ -2381,7 +2381,7 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
self.tab_counter += 1;
self.tabs[i] = crate::app::document::DocumentTab::new_drawing(self.tab_counter);
self.active_tab = i;
self.apply_bg_default(i);
self.apply_display_defaults(i);
self.update(Message::OpenPathPicked(Some((
conflict.path,
metadata.len(),
+11 -1
View File
@@ -1271,7 +1271,7 @@ impl OpenCADStudio {
// creating one doesn't silently reset them (its header default is
// 0 = no snaps); saving then persists them into the file.
self.stamp_header_sysvars(idx);
self.apply_bg_default(idx);
self.apply_display_defaults(idx);
self.sync_ribbon_layers();
self.sync_ribbon_styles();
// #21: reset ribbon Color / Linetype / Lineweight to the
@@ -5787,6 +5787,16 @@ impl OpenCADStudio {
Task::none()
}
Message::LineweightDisplayScaleChanged(value) => {
self.lineweight_display_scale = value.clamp(25, 200);
let scale = self.lineweight_display_scale as f32 / 100.0;
for tab in &mut self.tabs {
tab.scene.model_lineweight_scale = scale;
}
self.persist_settings_if_changed();
Task::none()
}
Message::CrosshairColorChanged(value) => {
self.crosshair_color_input = value.clone();
if value.trim().is_empty() {
+1
View File
@@ -189,6 +189,7 @@ impl OpenCADStudio {
self.cursor_type,
self.crosshair_color,
&self.crosshair_color_input,
self.lineweight_display_scale,
flow,
)
},
+3
View File
@@ -1469,6 +1469,8 @@ pub struct Scene {
/// Whether entity transparency is honoured on screen. When false the
/// wire shader forces every line opaque (a uniform toggle, no retessellate).
pub transparency_display: bool,
/// User-adjustable model-space lineweight preview multiplier.
pub model_lineweight_scale: f32,
/// Selection filter: entity-type names excluded from interactive picking.
/// Empty = every type is selectable.
pub selection_filter: HashSet<String>,
@@ -1878,6 +1880,7 @@ impl Scene {
refedit_keep: None,
hover_highlight: None,
transparency_display: true,
model_lineweight_scale: 1.0,
selection_filter: HashSet::default(),
preview_wires: vec![],
preview_hatches: Arc::new(Vec::new()),
+2 -2
View File
@@ -25,8 +25,8 @@ pub struct Uniforms {
/// zooming inside MSPACE changes one scalar instead of re-tessellating and
/// re-uploading every dashed wire.
pub linetype_scale: f32,
/// Multiplier for screen-space lineweights. Model space keeps `1.0`;
/// paper layouts derive it from the current paper zoom.
/// Multiplier for screen-space lineweights. Model space uses the user's
/// preview scale; paper layouts derive it from the current paper zoom.
pub lineweight_scale: f32,
// ── Relative-to-eye (double-single) additions ───────────────────────────
+3 -1
View File
@@ -3847,7 +3847,9 @@ impl Scene {
full_bounds,
self.document.header.lineweight_display || display_plot_lineweights,
);
if self.current_layout != "Model" {
if self.current_layout == "Model" {
uniforms.lineweight_scale = self.model_lineweight_scale;
} else {
let paper_per_pixel = 2.0 * self.camera.borrow().ortho_size() / canvas.1.max(1.0);
let paper_units_per_mm = self.paper_space_unit_factor() as f32;
uniforms.lineweight_scale = paper_units_per_mm / paper_per_pixel / MM_TO_PX;
+23
View File
@@ -39,6 +39,7 @@ pub fn view_window<'a>(
cursor_type: CursorType,
crosshair_color: Option<[u8; 3]>,
crosshair_color_input: &'a str,
lineweight_display_scale: i32,
sizing: crate::ui::modal::ModalSizing,
) -> Element<'a, Message> {
let selected_format = crate::io::SAVE_FORMAT_OPTIONS
@@ -265,6 +266,28 @@ pub fn view_window<'a>(
text(crate::t!("Leave the color blank to keep automatic viewport contrast."))
.size(11)
.width(sizing.width),
Space::new().height(24),
text(crate::t!("Lineweight")).size(15),
Space::new().height(10),
row![
text(crate::t!("Model display scale")).size(12).width(140),
slider(
25..=200,
lineweight_display_scale.clamp(25, 200),
Message::LineweightDisplayScaleChanged,
)
.step(1)
.width(Fill),
text(format!("{}%", lineweight_display_scale.clamp(25, 200)))
.size(11)
.width(44),
]
.spacing(10)
.align_y(iced::Center),
Space::new().height(6),
text(crate::t!("Changes the on-screen width in Model without affecting plotted output."))
.size(11)
.width(sizing.width),
]
.spacing(0)
.width(sizing.width);