feat(view): add visual style previews
- preview render modes on hover without committing - distinguish 2D and 3D wireframe fill and depth behavior Refs #483
This commit is contained in:
parent
44d0c65ae9
commit
b1d55c6a7e
49 changed files with 560 additions and 51 deletions
|
|
@ -498,6 +498,11 @@ pub(super) struct OpenCADStudio {
|
||||||
/// next frame to decide whether the ViewCube still has room beside it — so
|
/// next frame to decide whether the ViewCube still has room beside it — so
|
||||||
/// the two corner widgets adapt to the bar's real width, not an estimate.
|
/// the two corner widgets adapt to the bar's real width, not an estimate.
|
||||||
render_bar_w: std::sync::Arc<std::sync::atomic::AtomicU32>,
|
render_bar_w: std::sync::Arc<std::sync::atomic::AtomicU32>,
|
||||||
|
/// Whether the visual-style flyout beside the active viewport is open.
|
||||||
|
render_mode_menu_open: bool,
|
||||||
|
/// Mode whose sample is shown while the pointer moves through the flyout.
|
||||||
|
/// This does not alter the drawing until the corresponding row is clicked.
|
||||||
|
render_mode_preview: Option<acadrust::entities::ViewportRenderMode>,
|
||||||
/// Whether the Properties panel is shown on the left (PROPERTIES).
|
/// Whether the Properties panel is shown on the left (PROPERTIES).
|
||||||
show_properties: bool,
|
show_properties: bool,
|
||||||
/// Whether the document file tabs are shown at the top (FILETAB).
|
/// Whether the document file tabs are shown at the top (FILETAB).
|
||||||
|
|
@ -1708,6 +1713,12 @@ pub enum Message {
|
||||||
/// styles). Replaces the binary `SetWireframe` over time; the older
|
/// styles). Replaces the binary `SetWireframe` over time; the older
|
||||||
/// message stays for ribbon/CLI back-compat and forwards.
|
/// message stays for ribbon/CLI back-compat and forwards.
|
||||||
SetRenderMode(acadrust::entities::ViewportRenderMode),
|
SetRenderMode(acadrust::entities::ViewportRenderMode),
|
||||||
|
/// Open or close the active viewport's visual-style flyout.
|
||||||
|
ToggleRenderModeMenu(acadrust::entities::ViewportRenderMode),
|
||||||
|
/// Close the visual-style flyout after Escape or an outside click.
|
||||||
|
DismissRenderModeMenu,
|
||||||
|
/// Change only the sample shown beside the visual-style list.
|
||||||
|
PreviewRenderMode(acadrust::entities::ViewportRenderMode),
|
||||||
/// Switch camera projection: true = Orthographic, false = Perspective.
|
/// Switch camera projection: true = Orthographic, false = Perspective.
|
||||||
SetProjection(bool),
|
SetProjection(bool),
|
||||||
/// Select a ribbon module tab by index.
|
/// Select a ribbon module tab by index.
|
||||||
|
|
@ -2933,6 +2944,8 @@ impl OpenCADStudio {
|
||||||
ucs_icon_at_origin: true,
|
ucs_icon_at_origin: true,
|
||||||
show_viewcube: true,
|
show_viewcube: true,
|
||||||
render_bar_w: std::sync::Arc::new(std::sync::atomic::AtomicU32::new(0)),
|
render_bar_w: std::sync::Arc::new(std::sync::atomic::AtomicU32::new(0)),
|
||||||
|
render_mode_menu_open: false,
|
||||||
|
render_mode_preview: None,
|
||||||
show_properties: true,
|
show_properties: true,
|
||||||
show_file_tabs: true,
|
show_file_tabs: true,
|
||||||
show_layout_tabs: true,
|
show_layout_tabs: true,
|
||||||
|
|
|
||||||
|
|
@ -1191,7 +1191,30 @@ impl OpenCADStudio {
|
||||||
Task::done(Message::SetRenderMode(mode))
|
Task::done(Message::SetRenderMode(mode))
|
||||||
}
|
}
|
||||||
|
|
||||||
Message::SetRenderMode(mode) => self.on_set_render_mode(mode),
|
Message::SetRenderMode(mode) => {
|
||||||
|
self.render_mode_menu_open = false;
|
||||||
|
self.render_mode_preview = None;
|
||||||
|
self.on_set_render_mode(mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
Message::ToggleRenderModeMenu(mode) => {
|
||||||
|
self.render_mode_menu_open = !self.render_mode_menu_open;
|
||||||
|
self.render_mode_preview = self.render_mode_menu_open.then_some(mode);
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
|
||||||
|
Message::DismissRenderModeMenu => {
|
||||||
|
self.render_mode_menu_open = false;
|
||||||
|
self.render_mode_preview = None;
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
|
||||||
|
Message::PreviewRenderMode(mode) => {
|
||||||
|
if self.render_mode_menu_open {
|
||||||
|
self.render_mode_preview = Some(mode);
|
||||||
|
}
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
|
||||||
Message::SetProjection(ortho) => {
|
Message::SetProjection(ortho) => {
|
||||||
use crate::scene::Projection;
|
use crate::scene::Projection;
|
||||||
|
|
|
||||||
|
|
@ -1757,6 +1757,7 @@ impl OpenCADStudio {
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
use super::super::document::{DynComponent, DynFieldEntry};
|
use super::super::document::{DynComponent, DynFieldEntry};
|
||||||
use super::super::Message;
|
use super::super::Message;
|
||||||
use iced::widget::{button, container, mouse_area, row, tooltip};
|
use iced::widget::{button, canvas, column, container, mouse_area, row, text, tooltip};
|
||||||
use iced::{Background, Border, Element, Theme};
|
use iced::{Background, Border, Color, Element, Length, Point, Rectangle, Theme};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
fn viewport_tooltip<'a>(
|
fn viewport_tooltip<'a>(
|
||||||
|
|
@ -22,15 +22,200 @@ fn viewport_tooltip<'a>(
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct RenderModePreview {
|
||||||
|
mode: acadrust::entities::ViewportRenderMode,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl canvas::Program<Message> for RenderModePreview {
|
||||||
|
type State = ();
|
||||||
|
|
||||||
|
fn draw(
|
||||||
|
&self,
|
||||||
|
_state: &(),
|
||||||
|
renderer: &iced::Renderer,
|
||||||
|
theme: &Theme,
|
||||||
|
bounds: Rectangle,
|
||||||
|
_cursor: iced::mouse::Cursor,
|
||||||
|
) -> Vec<canvas::Geometry> {
|
||||||
|
use acadrust::entities::ViewportRenderMode as M;
|
||||||
|
|
||||||
|
let mut frame = canvas::Frame::new(renderer, bounds.size());
|
||||||
|
let palette = theme.palette();
|
||||||
|
let ink = palette.background.base.text.scale_alpha(0.86);
|
||||||
|
let faint = ink.scale_alpha(0.34);
|
||||||
|
let accent = palette.primary.base.color;
|
||||||
|
let face = palette.primary.weak.color.scale_alpha(0.72);
|
||||||
|
let face_alt = palette.primary.strong.color.scale_alpha(0.64);
|
||||||
|
let edge = if self.mode == M::Wireframe3D { accent } else { ink };
|
||||||
|
let shaded = matches!(
|
||||||
|
self.mode,
|
||||||
|
M::FlatShaded
|
||||||
|
| M::GouraudShaded
|
||||||
|
| M::FlatShadedWithEdges
|
||||||
|
| M::GouraudShadedWithEdges
|
||||||
|
);
|
||||||
|
let flat = matches!(self.mode, M::FlatShaded | M::FlatShadedWithEdges);
|
||||||
|
let smooth = matches!(self.mode, M::GouraudShaded | M::GouraudShadedWithEdges);
|
||||||
|
let with_edges = matches!(
|
||||||
|
self.mode,
|
||||||
|
M::Wireframe2D
|
||||||
|
| M::Wireframe3D
|
||||||
|
| M::HiddenLine
|
||||||
|
| M::FlatShadedWithEdges
|
||||||
|
| M::GouraudShadedWithEdges
|
||||||
|
);
|
||||||
|
|
||||||
|
// A small cube makes hidden/back edges and the shaded-edge variants
|
||||||
|
// immediately distinguishable without depending on the open drawing.
|
||||||
|
let a = Point::new(25.0, 35.0);
|
||||||
|
let b = Point::new(87.0, 35.0);
|
||||||
|
let c = Point::new(87.0, 94.0);
|
||||||
|
let d = Point::new(25.0, 94.0);
|
||||||
|
let off = iced::Vector::new(22.0, -17.0);
|
||||||
|
let e = a + off;
|
||||||
|
let f = b + off;
|
||||||
|
let g = c + off;
|
||||||
|
let h = d + off;
|
||||||
|
let quad = |points: [Point; 4]| {
|
||||||
|
canvas::Path::new(|path| {
|
||||||
|
path.move_to(points[0]);
|
||||||
|
path.line_to(points[1]);
|
||||||
|
path.line_to(points[2]);
|
||||||
|
path.line_to(points[3]);
|
||||||
|
path.close();
|
||||||
|
})
|
||||||
|
};
|
||||||
|
if shaded || self.mode == M::HiddenLine {
|
||||||
|
frame.fill(&quad([a, b, f, e]), face_alt);
|
||||||
|
frame.fill(&quad([b, c, g, f]), face.scale_alpha(0.82));
|
||||||
|
frame.fill(&quad([a, d, c, b]), face);
|
||||||
|
}
|
||||||
|
let stroke_line = |frame: &mut canvas::Frame, from: Point, to: Point, color: Color| {
|
||||||
|
frame.stroke(
|
||||||
|
&canvas::Path::line(from, to),
|
||||||
|
canvas::Stroke::default().with_color(color).with_width(1.2),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
if matches!(self.mode, M::Wireframe2D | M::Wireframe3D) {
|
||||||
|
for (from, to) in [(e, f), (f, g), (g, h), (h, e)] {
|
||||||
|
stroke_line(&mut frame, from, to, faint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if with_edges {
|
||||||
|
for (from, to) in [
|
||||||
|
(a, b), (b, c), (c, d), (d, a),
|
||||||
|
(a, e), (b, f), (c, g), (d, h),
|
||||||
|
(e, f), (f, g),
|
||||||
|
] {
|
||||||
|
stroke_line(&mut frame, from, to, edge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Curved sample: faceted tones for Flat, concentric highlight for
|
||||||
|
// Gouraud, and latitude/longitude lines for the wireframe modes.
|
||||||
|
let center = Point::new((bounds.width - 55.0).max(145.0), 59.0);
|
||||||
|
let radius = 32.0;
|
||||||
|
if flat {
|
||||||
|
for i in 0..12 {
|
||||||
|
let a0 = i as f32 * std::f32::consts::TAU / 12.0;
|
||||||
|
let a1 = (i + 1) as f32 * std::f32::consts::TAU / 12.0;
|
||||||
|
let wedge = canvas::Path::new(|path| {
|
||||||
|
path.move_to(center);
|
||||||
|
path.line_to(Point::new(center.x + radius * a0.cos(), center.y + radius * a0.sin()));
|
||||||
|
path.line_to(Point::new(center.x + radius * a1.cos(), center.y + radius * a1.sin()));
|
||||||
|
path.close();
|
||||||
|
});
|
||||||
|
frame.fill(&wedge, if i % 3 == 0 { face_alt } else { face });
|
||||||
|
}
|
||||||
|
} else if smooth {
|
||||||
|
frame.fill(&canvas::Path::circle(center, radius), face_alt);
|
||||||
|
frame.fill(
|
||||||
|
&canvas::Path::circle(Point::new(center.x - 7.0, center.y - 8.0), radius * 0.72),
|
||||||
|
face,
|
||||||
|
);
|
||||||
|
frame.fill(
|
||||||
|
&canvas::Path::circle(Point::new(center.x - 12.0, center.y - 13.0), radius * 0.34),
|
||||||
|
palette.primary.weak.text.scale_alpha(0.38),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if with_edges || self.mode == M::HiddenLine {
|
||||||
|
frame.stroke(
|
||||||
|
&canvas::Path::circle(center, radius),
|
||||||
|
canvas::Stroke::default().with_color(edge).with_width(1.2),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if matches!(self.mode, M::Wireframe2D | M::Wireframe3D) {
|
||||||
|
for scale in [0.38_f32, 0.72] {
|
||||||
|
let y = radius * scale;
|
||||||
|
let half = (radius * radius - y * y).sqrt();
|
||||||
|
stroke_line(
|
||||||
|
&mut frame,
|
||||||
|
Point::new(center.x - half, center.y - y),
|
||||||
|
Point::new(center.x + half, center.y - y),
|
||||||
|
faint,
|
||||||
|
);
|
||||||
|
stroke_line(
|
||||||
|
&mut frame,
|
||||||
|
Point::new(center.x - half, center.y + y),
|
||||||
|
Point::new(center.x + half, center.y + y),
|
||||||
|
faint,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
stroke_line(
|
||||||
|
&mut frame,
|
||||||
|
Point::new(center.x, center.y - radius),
|
||||||
|
Point::new(center.x, center.y + radius),
|
||||||
|
faint,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bottom samples show the intentional Wireframe 2D/3D distinction:
|
||||||
|
// the legacy planar solid loses only its interior in the 3D style,
|
||||||
|
// while a hatch-like pattern remains visible in both.
|
||||||
|
let solid = quad([
|
||||||
|
Point::new(18.0, 118.0),
|
||||||
|
Point::new(74.0, 112.0),
|
||||||
|
Point::new(69.0, 140.0),
|
||||||
|
Point::new(23.0, 143.0),
|
||||||
|
]);
|
||||||
|
if self.mode != M::Wireframe3D {
|
||||||
|
frame.fill(&solid, accent.scale_alpha(0.48));
|
||||||
|
}
|
||||||
|
frame.stroke(
|
||||||
|
&solid,
|
||||||
|
canvas::Stroke::default().with_color(edge).with_width(1.0),
|
||||||
|
);
|
||||||
|
let hatch_box = canvas::Path::rectangle(Point::new(105.0, 112.0), iced::Size::new(82.0, 31.0));
|
||||||
|
frame.stroke(
|
||||||
|
&hatch_box,
|
||||||
|
canvas::Stroke::default().with_color(ink).with_width(1.0),
|
||||||
|
);
|
||||||
|
for x in (94..188).step_by(9) {
|
||||||
|
let x = x as f32;
|
||||||
|
stroke_line(
|
||||||
|
&mut frame,
|
||||||
|
Point::new(x.max(105.0), 143.0),
|
||||||
|
Point::new((x + 24.0).min(187.0), 112.0),
|
||||||
|
faint,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec![frame.into_geometry()]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn viewport_controls<'a>(
|
pub(super) fn viewport_controls<'a>(
|
||||||
render_mode: acadrust::entities::ViewportRenderMode,
|
render_mode: acadrust::entities::ViewportRenderMode,
|
||||||
show_grid: bool,
|
show_grid: bool,
|
||||||
snap_on: bool,
|
snap_on: bool,
|
||||||
include_split: bool,
|
include_split: bool,
|
||||||
tile_count: usize,
|
tile_count: usize,
|
||||||
|
render_mode_menu_open: bool,
|
||||||
|
render_mode_preview: Option<acadrust::entities::ViewportRenderMode>,
|
||||||
) -> Element<'a, Message> {
|
) -> Element<'a, Message> {
|
||||||
use acadrust::entities::ViewportRenderMode as M;
|
use acadrust::entities::ViewportRenderMode as M;
|
||||||
let render_modes: Vec<RenderModeChoice> = vec![
|
let render_modes = [
|
||||||
RenderModeChoice(M::Wireframe2D),
|
RenderModeChoice(M::Wireframe2D),
|
||||||
RenderModeChoice(M::Wireframe3D),
|
RenderModeChoice(M::Wireframe3D),
|
||||||
RenderModeChoice(M::HiddenLine),
|
RenderModeChoice(M::HiddenLine),
|
||||||
|
|
@ -101,34 +286,135 @@ pub(super) fn viewport_controls<'a>(
|
||||||
viewport_tooltip(button, title, command)
|
viewport_tooltip(button, title, command)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Render-mode picker, restyled borderless so the outer chip frames it.
|
// The standard picker cannot show a live sample beside its rows. This
|
||||||
let picker = iced::widget::pick_list(
|
// flyout keeps the current style unchanged while hovering; a click commits.
|
||||||
Some(RenderModeChoice(render_mode)),
|
let picker_head = button(
|
||||||
render_modes,
|
row![
|
||||||
|value| value.to_string(),
|
text(RenderModeChoice(render_mode).to_string()).size(11),
|
||||||
|
crate::ui::icons::themed_arrow_toggle(render_mode_menu_open, 9.0),
|
||||||
|
]
|
||||||
|
.spacing(6)
|
||||||
|
.align_y(iced::Center),
|
||||||
)
|
)
|
||||||
.on_select(|c| Message::SetRenderMode(c.0))
|
.on_press(Message::ToggleRenderModeMenu(render_mode))
|
||||||
.text_size(11)
|
|
||||||
.padding([4, 6])
|
.padding([4, 6])
|
||||||
.style(move |theme: &Theme, _| {
|
.style(move |theme: &Theme, status| {
|
||||||
let text = theme.palette().background.base.text;
|
let palette = theme.palette();
|
||||||
iced::widget::pick_list::Style {
|
let active = render_mode_menu_open
|
||||||
background: Background::Color(iced::Color::TRANSPARENT),
|
|| matches!(status, button::Status::Hovered | button::Status::Pressed);
|
||||||
border: Border {
|
button::Style {
|
||||||
radius: 3.0.into(),
|
background: active.then_some(Background::Color(palette.background.strong.color)),
|
||||||
|
text_color: if active {
|
||||||
|
palette.background.strong.text
|
||||||
|
} else {
|
||||||
|
palette.background.base.text
|
||||||
|
},
|
||||||
|
border: Border {
|
||||||
|
radius: 3.0.into(),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
|
||||||
text_color: text,
|
|
||||||
placeholder_color: text.scale_alpha(0.68),
|
|
||||||
handle_color: text,
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let picker = viewport_tooltip(
|
let picker_head = viewport_tooltip(
|
||||||
picker,
|
picker_head,
|
||||||
crate::t!("Visual Style").into_owned(),
|
crate::t!("Visual Style").into_owned(),
|
||||||
"VISUALSTYLES",
|
"VISUALSTYLES",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let preview_mode = render_mode_preview.unwrap_or(render_mode);
|
||||||
|
let mut choices = column![].spacing(2).width(Length::Fixed(174.0));
|
||||||
|
for choice in render_modes {
|
||||||
|
let highlighted = choice.0 == preview_mode;
|
||||||
|
let selected = choice.0 == render_mode;
|
||||||
|
let option = container(text(choice.to_string()).size(11).width(Length::Fill))
|
||||||
|
.padding([6, 8])
|
||||||
|
.width(Length::Fill)
|
||||||
|
.style(move |theme: &Theme| {
|
||||||
|
let palette = theme.palette();
|
||||||
|
let active = highlighted;
|
||||||
|
container::Style {
|
||||||
|
background: active.then_some(Background::Color(
|
||||||
|
if selected { palette.primary.weak.color } else { palette.background.strong.color },
|
||||||
|
)),
|
||||||
|
text_color: if selected {
|
||||||
|
Some(palette.primary.weak.text)
|
||||||
|
} else if active {
|
||||||
|
Some(palette.background.strong.text)
|
||||||
|
} else {
|
||||||
|
Some(palette.background.base.text)
|
||||||
|
},
|
||||||
|
border: Border {
|
||||||
|
radius: 3.0.into(),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
choices = choices.push(mouse_area(option)
|
||||||
|
.interaction(iced::mouse::Interaction::Pointer)
|
||||||
|
.on_enter(Message::PreviewRenderMode(choice.0))
|
||||||
|
.on_press(Message::SetRenderMode(choice.0)));
|
||||||
|
}
|
||||||
|
let preview = container(
|
||||||
|
column![
|
||||||
|
container(
|
||||||
|
text(RenderModeChoice(preview_mode).to_string())
|
||||||
|
.size(12)
|
||||||
|
.wrapping(iced::advanced::text::Wrapping::None),
|
||||||
|
)
|
||||||
|
.width(Length::Fixed(215.0))
|
||||||
|
.height(Length::Fixed(16.0))
|
||||||
|
.align_y(iced::alignment::Vertical::Center),
|
||||||
|
canvas(RenderModePreview { mode: preview_mode })
|
||||||
|
.width(Length::Fixed(215.0))
|
||||||
|
.height(Length::Fixed(154.0)),
|
||||||
|
]
|
||||||
|
.spacing(6),
|
||||||
|
)
|
||||||
|
.padding(8)
|
||||||
|
.style(|theme: &Theme| {
|
||||||
|
let palette = theme.palette();
|
||||||
|
container::Style {
|
||||||
|
background: Some(Background::Color(palette.background.base.color)),
|
||||||
|
border: Border {
|
||||||
|
color: palette.background.neutral.color,
|
||||||
|
width: 1.0,
|
||||||
|
radius: 3.0.into(),
|
||||||
|
},
|
||||||
|
text_color: Some(palette.background.base.text),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let popup = container(
|
||||||
|
row![choices, preview]
|
||||||
|
.spacing(6)
|
||||||
|
.align_y(iced::alignment::Vertical::Top),
|
||||||
|
)
|
||||||
|
.padding(6)
|
||||||
|
.style(|theme: &Theme| {
|
||||||
|
let palette = theme.palette();
|
||||||
|
container::Style {
|
||||||
|
background: Some(Background::Color(palette.background.weak.color)),
|
||||||
|
border: Border {
|
||||||
|
color: palette.background.neutral.color,
|
||||||
|
width: 1.0,
|
||||||
|
radius: 4.0.into(),
|
||||||
|
},
|
||||||
|
text_color: Some(palette.background.weak.text),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let picker: Element<'a, Message> = iced_aw::DropDown::new(
|
||||||
|
picker_head,
|
||||||
|
popup,
|
||||||
|
render_mode_menu_open,
|
||||||
|
)
|
||||||
|
.alignment(iced_aw::drop_down::Alignment::Bottom)
|
||||||
|
.offset(3.0)
|
||||||
|
.on_dismiss(Message::DismissRenderModeMenu)
|
||||||
|
.into();
|
||||||
|
|
||||||
// Thin vertical divider between control groups.
|
// Thin vertical divider between control groups.
|
||||||
let sep = || {
|
let sep = || {
|
||||||
container(iced::widget::Space::new().width(1.0).height(16.0)).style(|theme: &Theme| {
|
container(iced::widget::Space::new().width(1.0).height(16.0)).style(|theme: &Theme| {
|
||||||
|
|
|
||||||
|
|
@ -199,6 +199,21 @@ impl OpenCADStudio {
|
||||||
theme_text.a,
|
theme_text.a,
|
||||||
];
|
];
|
||||||
let is_paper = tab.scene.current_layout != "Model";
|
let is_paper = tab.scene.current_layout != "Model";
|
||||||
|
let committed_render_mode = if is_paper {
|
||||||
|
tab.scene
|
||||||
|
.active_viewport_render_mode()
|
||||||
|
.unwrap_or(tab.render_mode)
|
||||||
|
} else {
|
||||||
|
tab.render_mode
|
||||||
|
};
|
||||||
|
// Gallery hover is a non-destructive live preview: only the shader's
|
||||||
|
// input changes. Dismissal restores the committed mode, while clicking
|
||||||
|
// a row follows the normal SetRenderMode path and persists it.
|
||||||
|
let viewport_render_mode = if self.render_mode_menu_open {
|
||||||
|
self.render_mode_preview.unwrap_or(committed_render_mode)
|
||||||
|
} else {
|
||||||
|
committed_render_mode
|
||||||
|
};
|
||||||
// Adaptive corner widgets: the ViewCube shows only while the active
|
// Adaptive corner widgets: the ViewCube shows only while the active
|
||||||
// viewport is wide enough to hold it *beside* the render-mode bar, whose
|
// viewport is wide enough to hold it *beside* the render-mode bar, whose
|
||||||
// real width is measured each frame by its `DensitySwap` and read back
|
// real width is measured each frame by its `DensitySwap` and read back
|
||||||
|
|
@ -250,7 +265,7 @@ impl OpenCADStudio {
|
||||||
shader(ViewportPane::model(
|
shader(ViewportPane::model(
|
||||||
&tab.scene,
|
&tab.scene,
|
||||||
viewcube_visible,
|
viewcube_visible,
|
||||||
tab.render_mode,
|
viewport_render_mode,
|
||||||
viewcube_text_color,
|
viewcube_text_color,
|
||||||
))
|
))
|
||||||
.width(Fill)
|
.width(Fill)
|
||||||
|
|
@ -268,7 +283,7 @@ impl OpenCADStudio {
|
||||||
// mouse_areas' hover state and drops their move events).
|
// mouse_areas' hover state and drops their move events).
|
||||||
let scene = &tab.scene;
|
let scene = &tab.scene;
|
||||||
let show_viewcube = viewcube_visible;
|
let show_viewcube = viewcube_visible;
|
||||||
let render_mode = tab.render_mode;
|
let render_mode = viewport_render_mode;
|
||||||
let size_probe: Element<'_, Message> = responsive(move |size| {
|
let size_probe: Element<'_, Message> = responsive(move |size| {
|
||||||
{
|
{
|
||||||
let mut sel = scene.selection.borrow_mut();
|
let mut sel = scene.selection.borrow_mut();
|
||||||
|
|
@ -856,6 +871,8 @@ impl OpenCADStudio {
|
||||||
self.snapper.grid_snap(),
|
self.snapper.grid_snap(),
|
||||||
true,
|
true,
|
||||||
tab.scene.model_tiles.borrow().len(),
|
tab.scene.model_tiles.borrow().len(),
|
||||||
|
self.render_mode_menu_open,
|
||||||
|
self.render_mode_preview,
|
||||||
);
|
);
|
||||||
// Adaptive: DensitySwap measures the bar's real width every frame
|
// Adaptive: DensitySwap measures the bar's real width every frame
|
||||||
// (reported into `render_bar_w`, which the ViewCube reads to decide
|
// (reported into `render_bar_w`, which the ViewCube reads to decide
|
||||||
|
|
@ -946,6 +963,8 @@ impl OpenCADStudio {
|
||||||
self.snapper.grid_snap(),
|
self.snapper.grid_snap(),
|
||||||
false,
|
false,
|
||||||
0,
|
0,
|
||||||
|
self.render_mode_menu_open,
|
||||||
|
self.render_mode_preview,
|
||||||
);
|
);
|
||||||
let adaptive: Element<'_, Message> = DensitySwap::new(vec![
|
let adaptive: Element<'_, Message> = DensitySwap::new(vec![
|
||||||
iced::widget::opaque(bar),
|
iced::widget::opaque(bar),
|
||||||
|
|
|
||||||
|
|
@ -1825,6 +1825,7 @@ fn tessellate_dimension_inner(
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
@ -1854,6 +1855,7 @@ fn tessellate_dimension_inner(
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
@ -1883,6 +1885,7 @@ fn tessellate_dimension_inner(
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
@ -1913,6 +1916,7 @@ fn tessellate_dimension_inner(
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
@ -1959,6 +1963,7 @@ fn tessellate_dimension_inner(
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
|
||||||
|
|
@ -624,6 +624,7 @@ impl LeaderTess for Leader {
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
@ -798,6 +799,7 @@ impl LeaderTess for Leader {
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
|
||||||
|
|
@ -1507,6 +1507,7 @@ impl MultiLeaderTess for MultiLeader {
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
@ -1829,6 +1830,7 @@ impl MultiLeaderTess for MultiLeader {
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
@ -1897,6 +1899,7 @@ impl MultiLeaderTess for MultiLeader {
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
@ -1980,6 +1983,7 @@ impl MultiLeaderTess for MultiLeader {
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
@ -2018,6 +2022,7 @@ impl MultiLeaderTess for MultiLeader {
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
|
||||||
|
|
@ -1671,6 +1671,7 @@ pub fn tessellate_table(
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
|
||||||
|
|
@ -191,6 +191,7 @@ impl CadCommand for AlignedDimensionCommand {
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
@ -245,6 +246,7 @@ fn preview_aligned(p1: DVec3, p2: DVec3, dim_pt: DVec3) -> WireModel {
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
|
||||||
|
|
@ -139,6 +139,7 @@ fn preview_wire(points: Vec<Vec3>) -> WireModel {
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
|
||||||
|
|
@ -185,6 +185,7 @@ fn preview_line(a: Vec3, b: Vec3) -> WireModel {
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
|
||||||
|
|
@ -184,6 +184,7 @@ impl CadCommand for DimBaselineCommand {
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
|
||||||
|
|
@ -164,6 +164,7 @@ impl CadCommand for DimContinueCommand {
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,7 @@ impl CadCommand for DimJogLineCommand {
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,7 @@ impl CadCommand for DimTeditCommand {
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
|
||||||
|
|
@ -233,6 +233,7 @@ fn preview_wire(pts: &[Vec3], arrow_size: f32) -> WireModel {
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
|
||||||
|
|
@ -223,6 +223,7 @@ fn preview_wire(points: Vec<DVec3>) -> WireModel {
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
|
||||||
|
|
@ -205,6 +205,7 @@ fn preview_wire(pts: &[Vec3], arrow_size: f32) -> WireModel {
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
|
||||||
|
|
@ -466,6 +466,7 @@ fn preview_wire(pts: &[Vec3]) -> WireModel {
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,7 @@ fn preview_wire(points: Vec<Vec3>) -> WireModel {
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,7 @@ impl CadCommand for QdimCommand {
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
|
||||||
|
|
@ -190,6 +190,7 @@ fn preview_wire(points: Vec<Vec3>) -> WireModel {
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
|
||||||
|
|
@ -189,6 +189,7 @@ impl CadCommand for TableCommand {
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@ impl CadCommand for ToleranceCommand {
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
|
||||||
|
|
@ -188,6 +188,7 @@ impl CadCommand for AttdefCommand {
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
|
||||||
|
|
@ -170,6 +170,7 @@ impl CadCommand for MlineCommand {
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,7 @@ impl CadCommand for ImageCommand {
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,7 @@ impl CadCommand for RayCommand {
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
@ -187,6 +188,7 @@ impl CadCommand for XLineCommand {
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,7 @@ impl CadCommand for RevCloudCommand {
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
|
||||||
|
|
@ -414,6 +414,7 @@ impl CadCommand for AreaCommand {
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,7 @@ impl CadCommand for DistCommand {
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,7 @@ impl MeasureGeomCommand {
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
|
||||||
|
|
@ -4570,6 +4570,7 @@ fn preview_wire(points: Vec<[f32; 3]>, color: [f32; 4], name: &str) -> WireModel
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
|
||||||
|
|
@ -310,6 +310,7 @@ fn wire(name: &str, points: Vec<[f32; 3]>) -> WireModel {
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ impl CadCommand for PlotWindowCommand {
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,7 @@ impl CadCommand for ZoomWindowCommand {
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
|
||||||
15
src/scene/cache/block_cache.rs
vendored
15
src/scene/cache/block_cache.rs
vendored
|
|
@ -48,6 +48,9 @@ pub struct LocalWire {
|
||||||
pub tangent_geoms: Vec<TangentGeom>,
|
pub tangent_geoms: Vec<TangentGeom>,
|
||||||
pub fill_tris: Vec<[f32; 3]>,
|
pub fill_tris: Vec<[f32; 3]>,
|
||||||
pub fill_tris_low: Vec<[f32; 3]>,
|
pub fill_tris_low: Vec<[f32; 3]>,
|
||||||
|
/// Preserves the planar SOLID classification through block expansion so
|
||||||
|
/// it never gets merged with unrelated annotation fills of the same style.
|
||||||
|
pub fill_is_2d_solid: bool,
|
||||||
/// Thickness-wall pick geometry, transformed by the insert like `points`
|
/// Thickness-wall pick geometry, transformed by the insert like `points`
|
||||||
/// so a block child's extruded wall stays selectable at the instance's
|
/// so a block child's extruded wall stays selectable at the instance's
|
||||||
/// place and scale. Never reaches the GPU.
|
/// place and scale. Never reaches the GPU.
|
||||||
|
|
@ -736,6 +739,7 @@ fn tessellate_sub_local(
|
||||||
tangent_geoms: wire.tangent_geoms,
|
tangent_geoms: wire.tangent_geoms,
|
||||||
fill_tris: wire.fill_tris,
|
fill_tris: wire.fill_tris,
|
||||||
fill_tris_low: wire.fill_tris_low,
|
fill_tris_low: wire.fill_tris_low,
|
||||||
|
fill_is_2d_solid: wire.fill_is_2d_solid,
|
||||||
pick_tris: wire.pick_tris,
|
pick_tris: wire.pick_tris,
|
||||||
pick_tris_low: wire.pick_tris_low,
|
pick_tris_low: wire.pick_tris_low,
|
||||||
color: wire.color,
|
color: wire.color,
|
||||||
|
|
@ -1191,6 +1195,9 @@ struct StyleKey {
|
||||||
/// discriminator, so greek fills must stay in their own batches even
|
/// discriminator, so greek fills must stay in their own batches even
|
||||||
/// when their color/style would otherwise collide with regular wires.
|
/// when their color/style would otherwise collide with regular wires.
|
||||||
is_fill_only: bool,
|
is_fill_only: bool,
|
||||||
|
/// Part of the batch key so planar SOLID fills remain independently
|
||||||
|
/// switchable after block geometry is merged by style.
|
||||||
|
fill_is_2d_solid: bool,
|
||||||
/// Bit-cast composed block-local depth for band wires (`0` = no override).
|
/// Bit-cast composed block-local depth for band wires (`0` = no override).
|
||||||
/// Keeps bands of different in-block draw ranks in separate batches so
|
/// Keeps bands of different in-block draw ranks in separate batches so
|
||||||
/// each finalized WireModel carries one correct `depth_override`.
|
/// each finalized WireModel carries one correct `depth_override`.
|
||||||
|
|
@ -1220,6 +1227,7 @@ struct BatchEntry {
|
||||||
/// reconstructs `high + low`). Without it absolute f32 fills quantize to
|
/// reconstructs `high + low`). Without it absolute f32 fills quantize to
|
||||||
/// ~0.5 m and the greek-text rectangles shear.
|
/// ~0.5 m and the greek-text rectangles shear.
|
||||||
fill_tris_low: Vec<[f32; 3]>,
|
fill_tris_low: Vec<[f32; 3]>,
|
||||||
|
fill_is_2d_solid: bool,
|
||||||
/// Accumulated thickness-wall pick geometry, paired high/low like
|
/// Accumulated thickness-wall pick geometry, paired high/low like
|
||||||
/// `fill_tris`. Pick-only — no GPU batch reads this.
|
/// `fill_tris`. Pick-only — no GPU batch reads this.
|
||||||
pick_tris: Vec<[f32; 3]>,
|
pick_tris: Vec<[f32; 3]>,
|
||||||
|
|
@ -1261,6 +1269,7 @@ impl BatchEntry {
|
||||||
aci: u8,
|
aci: u8,
|
||||||
plinegen: bool,
|
plinegen: bool,
|
||||||
_is_fill_only: bool,
|
_is_fill_only: bool,
|
||||||
|
fill_is_2d_solid: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
// `is_fill_only` is part of the StyleKey hash so greek fills never
|
// `is_fill_only` is part of the StyleKey hash so greek fills never
|
||||||
// share a batch with regular wires (otherwise the finalized
|
// share a batch with regular wires (otherwise the finalized
|
||||||
|
|
@ -1275,6 +1284,7 @@ impl BatchEntry {
|
||||||
world_width,
|
world_width,
|
||||||
aci,
|
aci,
|
||||||
plinegen,
|
plinegen,
|
||||||
|
fill_is_2d_solid,
|
||||||
min_x: f32::INFINITY,
|
min_x: f32::INFINITY,
|
||||||
min_y: f32::INFINITY,
|
min_y: f32::INFINITY,
|
||||||
max_x: f32::NEG_INFINITY,
|
max_x: f32::NEG_INFINITY,
|
||||||
|
|
@ -1308,6 +1318,7 @@ impl Batches {
|
||||||
world_width: b.world_width,
|
world_width: b.world_width,
|
||||||
depth_override: b.local_depth,
|
depth_override: b.local_depth,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: b.fill_is_2d_solid,
|
||||||
pick_tris: b.pick_tris,
|
pick_tris: b.pick_tris,
|
||||||
pick_tris_low: b.pick_tris_low,
|
pick_tris_low: b.pick_tris_low,
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
@ -1352,6 +1363,7 @@ fn style_key(
|
||||||
aci: u8,
|
aci: u8,
|
||||||
plinegen: bool,
|
plinegen: bool,
|
||||||
is_fill_only: bool,
|
is_fill_only: bool,
|
||||||
|
fill_is_2d_solid: bool,
|
||||||
local_depth: Option<f32>,
|
local_depth: Option<f32>,
|
||||||
) -> StyleKey {
|
) -> StyleKey {
|
||||||
StyleKey {
|
StyleKey {
|
||||||
|
|
@ -1377,6 +1389,7 @@ fn style_key(
|
||||||
aci,
|
aci,
|
||||||
plinegen,
|
plinegen,
|
||||||
is_fill_only,
|
is_fill_only,
|
||||||
|
fill_is_2d_solid,
|
||||||
depth_bits: local_depth.map_or(0, f32::to_bits),
|
depth_bits: local_depth.map_or(0, f32::to_bits),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1708,6 +1721,7 @@ fn emit_wire(
|
||||||
lw.aci,
|
lw.aci,
|
||||||
lw.plinegen,
|
lw.plinegen,
|
||||||
lw.is_fill_only,
|
lw.is_fill_only,
|
||||||
|
lw.fill_is_2d_solid,
|
||||||
local_depth,
|
local_depth,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -1730,6 +1744,7 @@ fn emit_wire(
|
||||||
lw.aci,
|
lw.aci,
|
||||||
lw.plinegen,
|
lw.plinegen,
|
||||||
lw.is_fill_only,
|
lw.is_fill_only,
|
||||||
|
lw.fill_is_2d_solid,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
entry.local_depth = local_depth;
|
entry.local_depth = local_depth;
|
||||||
|
|
|
||||||
|
|
@ -712,6 +712,7 @@ pub(crate) fn tessellate_entity(
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris,
|
pick_tris,
|
||||||
pick_tris_low,
|
pick_tris_low,
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
@ -961,6 +962,7 @@ pub(crate) fn tessellate_entity(
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
@ -1402,6 +1404,7 @@ fn lod_stub_wire(
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
@ -1491,6 +1494,7 @@ fn lod_stub_wire_3d(
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
|
||||||
|
|
@ -376,6 +376,7 @@ pub fn tessellate(
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
// MLINE dashes: A-type aligned, but the end-dash length is
|
// MLINE dashes: A-type aligned, but the end-dash length is
|
||||||
|
|
@ -674,6 +675,7 @@ pub fn tessellate(
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
@ -714,6 +716,7 @@ pub fn tessellate(
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
@ -761,6 +764,7 @@ pub fn tessellate(
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
@ -789,6 +793,7 @@ pub fn tessellate(
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
@ -847,6 +852,7 @@ pub fn tessellate(
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
@ -887,6 +893,7 @@ pub fn tessellate(
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
@ -924,6 +931,7 @@ pub fn tessellate(
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
@ -954,6 +962,7 @@ pub fn tessellate(
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
@ -1006,6 +1015,7 @@ pub fn tessellate(
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
@ -1057,6 +1067,7 @@ pub fn tessellate(
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
@ -1101,6 +1112,7 @@ pub fn tessellate(
|
||||||
world_width: polyline_band_width(entity),
|
world_width: polyline_band_width(entity),
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris,
|
pick_tris,
|
||||||
pick_tris_low,
|
pick_tris_low,
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
@ -1194,6 +1206,7 @@ pub fn tessellate(
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris,
|
pick_tris,
|
||||||
pick_tris_low,
|
pick_tris_low,
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
@ -1253,6 +1266,7 @@ pub fn tessellate(
|
||||||
fill_tris,
|
fill_tris,
|
||||||
fill_tris_low,
|
fill_tris_low,
|
||||||
fill_is_3d,
|
fill_is_3d,
|
||||||
|
fill_is_2d_solid: matches!(entity, EntityType::Solid(_)),
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -1263,6 +1277,7 @@ pub fn tessellate(
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
@ -1306,6 +1321,7 @@ pub fn tessellate(
|
||||||
world_width: polyline_band_width(entity),
|
world_width: polyline_band_width(entity),
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris,
|
pick_tris,
|
||||||
pick_tris_low,
|
pick_tris_low,
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
@ -1350,6 +1366,7 @@ pub fn tessellate(
|
||||||
world_width,
|
world_width,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris,
|
pick_tris,
|
||||||
pick_tris_low,
|
pick_tris_low,
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
@ -1482,6 +1499,7 @@ pub fn tessellate(
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris,
|
pick_tris,
|
||||||
pick_tris_low,
|
pick_tris_low,
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
|
||||||
|
|
@ -1649,6 +1649,9 @@ pub struct Scene {
|
||||||
/// constants. `[depth, half]` retains a fixed child sub-range for block
|
/// constants. `[depth, half]` retains a fixed child sub-range for block
|
||||||
/// composition. Full sort/layout/block changes rebuild the labels.
|
/// composition. Full sort/layout/block changes rebuild the labels.
|
||||||
draw_depth_cache: RefCell<Option<DrawDepthCache>>,
|
draw_depth_cache: RefCell<Option<DrawDepthCache>>,
|
||||||
|
/// Shared empty map for 3-D wireframe, where true depth wins instead of
|
||||||
|
/// the entity submission order used by the optimized 2-D style.
|
||||||
|
no_draw_depths: Arc<HashMap<u64, [f32; 2]>>,
|
||||||
/// Cached hatch fill models, keyed by target block and geometry_epoch. View culling
|
/// Cached hatch fill models, keyed by target block and geometry_epoch. View culling
|
||||||
/// is handled at draw time via `hatch_skip_flags` in the pipeline,
|
/// is handled at draw time via `hatch_skip_flags` in the pipeline,
|
||||||
/// not at build time — that lets the GPU buffer stay stable across
|
/// not at build time — that lets the GPU buffer stay stable across
|
||||||
|
|
@ -1960,6 +1963,7 @@ impl Scene {
|
||||||
interaction_handle_index_cache: RefCell::new(None),
|
interaction_handle_index_cache: RefCell::new(None),
|
||||||
sort_cache: RefCell::new(None),
|
sort_cache: RefCell::new(None),
|
||||||
draw_depth_cache: RefCell::new(None),
|
draw_depth_cache: RefCell::new(None),
|
||||||
|
no_draw_depths: Arc::new(HashMap::default()),
|
||||||
hatch_cache: RefCell::new(HashMap::default()),
|
hatch_cache: RefCell::new(HashMap::default()),
|
||||||
wipeout_cache: RefCell::new(HashMap::default()),
|
wipeout_cache: RefCell::new(HashMap::default()),
|
||||||
image_cache: RefCell::new(HashMap::default()),
|
image_cache: RefCell::new(HashMap::default()),
|
||||||
|
|
|
||||||
|
|
@ -145,6 +145,9 @@ pub struct WireModel {
|
||||||
/// every view mode. The render pass can't infer this from `fill_tris_low`
|
/// every view mode. The render pass can't infer this from `fill_tris_low`
|
||||||
/// alone — a 2-D fill at UTM scale carries a low residual too.
|
/// alone — a 2-D fill at UTM scale carries a low residual too.
|
||||||
pub fill_is_3d: bool,
|
pub fill_is_3d: bool,
|
||||||
|
/// `true` only for a planar SOLID entity's interior. Wireframe 3D omits
|
||||||
|
/// this fill while preserving its perimeter and every other 2-D overlay.
|
||||||
|
pub fill_is_2d_solid: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WireModel {
|
impl WireModel {
|
||||||
|
|
@ -196,6 +199,7 @@ impl WireModel {
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
text_verts: Vec::new(),
|
text_verts: Vec::new(),
|
||||||
|
|
@ -430,6 +434,7 @@ impl Default for WireModel {
|
||||||
fill_tris_low: Vec::new(),
|
fill_tris_low: Vec::new(),
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -82,8 +82,10 @@ impl Scene {
|
||||||
/// scene camera (tiled splits will append more later). `model_mode`
|
/// scene camera (tiled splits will append more later). `model_mode`
|
||||||
/// supplies its render mode (held on the tab, not the scene).
|
/// supplies its render mode (held on the tab, not the scene).
|
||||||
/// - **Paper layout**: one instance per content viewport entity
|
/// - **Paper layout**: one instance per content viewport entity
|
||||||
/// (`id > 1`, owned by the current layout block, switched on),
|
/// (`id > 1`, owned by the current layout block, switched on), using each
|
||||||
/// using each viewport's own camera and render mode.
|
/// viewport's own camera and render mode. `model_mode` temporarily
|
||||||
|
/// overrides the active viewport so the visual-style gallery can preview
|
||||||
|
/// on hover without modifying the document.
|
||||||
pub fn active_viewports(
|
pub fn active_viewports(
|
||||||
&self,
|
&self,
|
||||||
canvas_w: f32,
|
canvas_w: f32,
|
||||||
|
|
@ -182,7 +184,11 @@ impl Scene {
|
||||||
tile_idx: None,
|
tile_idx: None,
|
||||||
screen_rect,
|
screen_rect,
|
||||||
camera,
|
camera,
|
||||||
render_mode: vp.render_mode,
|
render_mode: if self.active_viewport == Some(h) {
|
||||||
|
model_mode
|
||||||
|
} else {
|
||||||
|
vp.render_mode
|
||||||
|
},
|
||||||
active: self.active_viewport == Some(h),
|
active: self.active_viewport == Some(h),
|
||||||
grid_on: vp.status.grid_on,
|
grid_on: vp.status.grid_on,
|
||||||
paper_sheet: false,
|
paper_sheet: false,
|
||||||
|
|
|
||||||
|
|
@ -263,6 +263,7 @@ pub fn frame_wire(
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
@ -783,4 +784,3 @@ mod tests {
|
||||||
assert!(out.iter().all(|p| p[0] <= 10.0 + 1e-3));
|
assert!(out.iter().all(|p| p[0] <= 10.0 + 1e-3));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -108,12 +108,15 @@ impl Face3DGpu {
|
||||||
/// true depth; fills with empty `fill_tris_low` (2D fills — text greek,
|
/// true depth; fills with empty `fill_tris_low` (2D fills — text greek,
|
||||||
/// MultiLeader / dimension backgrounds) feed the 2D draw-order buffer.
|
/// MultiLeader / dimension backgrounds) feed the 2D draw-order buffer.
|
||||||
/// - `keep_3d_mesh_fills`: when false (wireframe modes), the 3D side
|
/// - `keep_3d_mesh_fills`: when false (wireframe modes), the 3D side
|
||||||
/// is left empty; the 2D side is always populated.
|
/// is left empty.
|
||||||
|
/// - `show_2d_solid_fills`: false only for Wireframe 3D; removes the
|
||||||
|
/// legacy planar SOLID interior while keeping other 2-D overlays.
|
||||||
pub fn from_wires(
|
pub fn from_wires(
|
||||||
device: &wgpu::Device,
|
device: &wgpu::Device,
|
||||||
face3d_wires: &[WireModel],
|
face3d_wires: &[WireModel],
|
||||||
all_wires: &[WireModel],
|
all_wires: &[WireModel],
|
||||||
keep_3d_mesh_fills: bool,
|
keep_3d_mesh_fills: bool,
|
||||||
|
show_2d_solid_fills: bool,
|
||||||
depth_map: &rustc_hash::FxHashMap<u64, [f32; 2]>,
|
depth_map: &rustc_hash::FxHashMap<u64, [f32; 2]>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let depth_of =
|
let depth_of =
|
||||||
|
|
@ -168,13 +171,17 @@ impl Face3DGpu {
|
||||||
// with both fill_tris and points (mesh edges + faces) keep the dim
|
// with both fill_tris and points (mesh edges + faces) keep the dim
|
||||||
// so PolyfaceMesh / PolygonMesh still look 3-D-shaded.
|
// so PolyfaceMesh / PolygonMesh still look 3-D-shaded.
|
||||||
//
|
//
|
||||||
// 2-D fills always go to `verts_2d` (visible in every mode).
|
// 2-D fills go to `verts_2d`; only legacy planar SOLID interiors are
|
||||||
|
// omitted by Wireframe 3D.
|
||||||
// 3-D mesh face data goes to `verts_3d` only when
|
// 3-D mesh face data goes to `verts_3d` only when
|
||||||
// `keep_3d_mesh_fills` is true.
|
// `keep_3d_mesh_fills` is true.
|
||||||
for wire in all_wires {
|
for wire in all_wires {
|
||||||
if wire.fill_tris.is_empty() {
|
if wire.fill_tris.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if !show_2d_solid_fills && wire.fill_is_2d_solid {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// A real 3-D surface fill (PolyfaceMesh / PolygonMesh) carries a
|
// A real 3-D surface fill (PolyfaceMesh / PolygonMesh) carries a
|
||||||
// double-single low residual paired with `fill_tris` — it lives at
|
// double-single low residual paired with `fill_tris` — it lives at
|
||||||
// true world coordinates and must keep its real depth. 2-D fills
|
// true world coordinates and must keep its real depth. 2-D fills
|
||||||
|
|
|
||||||
|
|
@ -296,11 +296,12 @@ pub struct Pipeline {
|
||||||
/// a pick bumps only `selection_generation`, refreshing the overlay without
|
/// a pick bumps only `selection_generation`, refreshing the overlay without
|
||||||
/// touching the main wire buffers.
|
/// touching the main wire buffers.
|
||||||
pub cached_selection: (u64, u64),
|
pub cached_selection: (u64, u64),
|
||||||
/// `(wire_content_id, face3d_fill_active)` the Face3D edge/fill buffers were
|
/// `(wire_content_id, face3d_fill_active, show_2d_solid_fills)` the Face3D
|
||||||
/// uploaded for. A stable content id avoids retaining the resident wire Arc:
|
/// edge/fill buffers were uploaded for. A stable content id avoids retaining
|
||||||
|
/// the resident wire Arc:
|
||||||
/// that Arc must stay uniquely owned by Scene so a small edit can splice it
|
/// that Arc must stay uniquely owned by Scene so a small edit can splice it
|
||||||
/// in place instead of rebuilding the whole drawing.
|
/// in place instead of rebuilding the whole drawing.
|
||||||
pub cached_face3d_key: (u64, bool),
|
pub cached_face3d_key: (u64, bool, bool),
|
||||||
/// Handle → indices into the resident wire set, built once per wire upload
|
/// Handle → indices into the resident wire set, built once per wire upload
|
||||||
/// (when `cached_wire_id` changes). Lets the selection/hover xray overlay
|
/// (when `cached_wire_id` changes). Lets the selection/hover xray overlay
|
||||||
/// gather just the highlighted entity's wires (`O(highlighted)`) instead of
|
/// gather just the highlighted entity's wires (`O(highlighted)`) instead of
|
||||||
|
|
@ -1710,7 +1711,7 @@ impl Pipeline {
|
||||||
cached_epoch: (u64::MAX, u64::MAX, u64::MAX),
|
cached_epoch: (u64::MAX, u64::MAX, u64::MAX),
|
||||||
cached_wire_id: u64::MAX,
|
cached_wire_id: u64::MAX,
|
||||||
cached_selection: (u64::MAX, u64::MAX),
|
cached_selection: (u64::MAX, u64::MAX),
|
||||||
cached_face3d_key: (u64::MAX, false),
|
cached_face3d_key: (u64::MAX, false, false),
|
||||||
wire_handle_index: std::sync::Arc::new(rustc_hash::FxHashMap::default()),
|
wire_handle_index: std::sync::Arc::new(rustc_hash::FxHashMap::default()),
|
||||||
render_sig: u64::MAX,
|
render_sig: u64::MAX,
|
||||||
skip_geometry: false,
|
skip_geometry: false,
|
||||||
|
|
@ -2269,6 +2270,7 @@ impl Pipeline {
|
||||||
face3d_wires: &[WireModel],
|
face3d_wires: &[WireModel],
|
||||||
all_wires: &[WireModel],
|
all_wires: &[WireModel],
|
||||||
wireframe_only: bool,
|
wireframe_only: bool,
|
||||||
|
show_2d_solid_fills: bool,
|
||||||
depth_map: &rustc_hash::FxHashMap<u64, [f32; 2]>,
|
depth_map: &rustc_hash::FxHashMap<u64, [f32; 2]>,
|
||||||
) {
|
) {
|
||||||
let perf_started = crate::perf::enabled().then(iced::time::Instant::now);
|
let perf_started = crate::perf::enabled().then(iced::time::Instant::now);
|
||||||
|
|
@ -2279,11 +2281,15 @@ impl Pipeline {
|
||||||
// Fill buffer split: 3D quads + PolyfaceMesh / PolygonMesh face
|
// Fill buffer split: 3D quads + PolyfaceMesh / PolygonMesh face
|
||||||
// tris go to `chunks_3d` (gated by `keep_3d_mesh_fills`);
|
// tris go to `chunks_3d` (gated by `keep_3d_mesh_fills`);
|
||||||
// 2D fills (text-LOD greek, MultiLeader background) go to
|
// 2D fills (text-LOD greek, MultiLeader background) go to
|
||||||
// `chunks_2d` and are visible in every mode.
|
// `chunks_2d`. The 3-D wireframe additionally removes only legacy
|
||||||
|
// planar SOLID interiors; HATCH is handled by a separate pass.
|
||||||
let keep_3d_mesh_fills = !wireframe_only;
|
let keep_3d_mesh_fills = !wireframe_only;
|
||||||
|
let solid_fill_hidden = |wire: &WireModel| {
|
||||||
|
!show_2d_solid_fills && wire.fill_is_2d_solid
|
||||||
|
};
|
||||||
let has_any_2d_fill = all_wires
|
let has_any_2d_fill = all_wires
|
||||||
.iter()
|
.iter()
|
||||||
.any(|w| !w.fill_tris.is_empty() && w.points.is_empty());
|
.any(|w| !w.fill_tris.is_empty() && w.points.is_empty() && !solid_fill_hidden(w));
|
||||||
let has_any_3d_fill = !face3d_wires.is_empty()
|
let has_any_3d_fill = !face3d_wires.is_empty()
|
||||||
|| all_wires
|
|| all_wires
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -2297,6 +2303,7 @@ impl Pipeline {
|
||||||
face3d_wires,
|
face3d_wires,
|
||||||
all_wires,
|
all_wires,
|
||||||
keep_3d_mesh_fills,
|
keep_3d_mesh_fills,
|
||||||
|
show_2d_solid_fills,
|
||||||
depth_map,
|
depth_map,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -313,6 +313,7 @@ pub fn apply_along(
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
@ -355,6 +356,7 @@ pub fn apply_along(
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
|
fill_is_2d_solid: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,10 @@ pub struct ViewportData {
|
||||||
/// Hatch / wipeout uploads are deliberately *not* gated by this flag —
|
/// Hatch / wipeout uploads are deliberately *not* gated by this flag —
|
||||||
/// the user toggle should only affect 3D solids, not 2D fills.
|
/// the user toggle should only affect 3D solids, not 2D fills.
|
||||||
pub(in crate::scene) view_wireframe: bool,
|
pub(in crate::scene) view_wireframe: bool,
|
||||||
|
/// Legacy planar SOLID interiors remain filled in the optimized 2-D
|
||||||
|
/// wireframe, but become outlines in the 3-D wireframe. HATCH uses its
|
||||||
|
/// own pipeline and is intentionally unaffected.
|
||||||
|
pub(in crate::scene) show_2d_solid_fills: bool,
|
||||||
/// Whether the active render mode wants 3D mesh fills uploaded. Off
|
/// Whether the active render mode wants 3D mesh fills uploaded. Off
|
||||||
/// in `Wireframe2D` / `Wireframe3D`; on for every shaded variant. Set
|
/// in `Wireframe2D` / `Wireframe3D`; on for every shaded variant. Set
|
||||||
/// at the same point `view_wireframe` is computed so the two stay in
|
/// at the same point `view_wireframe` is computed so the two stay in
|
||||||
|
|
@ -169,7 +173,9 @@ pub struct Primitive {
|
||||||
/// Flags the render pipeline consumes, derived from
|
/// Flags the render pipeline consumes, derived from
|
||||||
/// [`acadrust::entities::ViewportRenderMode`]. Each shaded variant fills
|
/// [`acadrust::entities::ViewportRenderMode`]. Each shaded variant fills
|
||||||
/// 3D faces and meshes; the pure wireframes drop the fill and keep only
|
/// 3D faces and meshes; the pure wireframes drop the fill and keep only
|
||||||
/// edges. `*WithEdges` variants render both. HiddenLine uses a depth
|
/// edges. The optimized 2-D wireframe retains planar SOLID interiors and
|
||||||
|
/// entity draw order; the 3-D wireframe uses true depth and outlines them.
|
||||||
|
/// `*WithEdges` variants render both. HiddenLine uses a depth
|
||||||
/// prepass: face/mesh fills are uploaded but routed through depth-only
|
/// prepass: face/mesh fills are uploaded but routed through depth-only
|
||||||
/// pipelines so hidden edges drop out. `FlatShaded` vs `GouraudShaded`
|
/// pipelines so hidden edges drop out. `FlatShaded` vs `GouraudShaded`
|
||||||
/// differ in shader uniform only and produce identical fill flags here.
|
/// differ in shader uniform only and produce identical fill flags here.
|
||||||
|
|
@ -179,6 +185,7 @@ pub struct RenderModeFlags {
|
||||||
pub mesh_fill: bool,
|
pub mesh_fill: bool,
|
||||||
pub show_3d_edges: bool,
|
pub show_3d_edges: bool,
|
||||||
pub hidden_line: bool,
|
pub hidden_line: bool,
|
||||||
|
pub show_2d_solid_fills: bool,
|
||||||
/// `true` for FlatShaded / FlatShadedWithEdges. The mesh shader
|
/// `true` for FlatShaded / FlatShadedWithEdges. The mesh shader
|
||||||
/// reads `Uniforms.flat_shade` and replaces the smooth per-vertex
|
/// reads `Uniforms.flat_shade` and replaces the smooth per-vertex
|
||||||
/// normal with a per-triangle face normal so each triangle reads
|
/// normal with a per-triangle face normal so each triangle reads
|
||||||
|
|
@ -191,11 +198,20 @@ pub fn render_mode_flags(
|
||||||
) -> RenderModeFlags {
|
) -> RenderModeFlags {
|
||||||
use acadrust::entities::ViewportRenderMode as M;
|
use acadrust::entities::ViewportRenderMode as M;
|
||||||
match mode {
|
match mode {
|
||||||
M::Wireframe2D | M::Wireframe3D => RenderModeFlags {
|
M::Wireframe2D => RenderModeFlags {
|
||||||
face3d_fill: false,
|
face3d_fill: false,
|
||||||
mesh_fill: false,
|
mesh_fill: false,
|
||||||
show_3d_edges: true,
|
show_3d_edges: true,
|
||||||
hidden_line: false,
|
hidden_line: false,
|
||||||
|
show_2d_solid_fills: true,
|
||||||
|
flat_shade: false,
|
||||||
|
},
|
||||||
|
M::Wireframe3D => RenderModeFlags {
|
||||||
|
face3d_fill: false,
|
||||||
|
mesh_fill: false,
|
||||||
|
show_3d_edges: true,
|
||||||
|
hidden_line: false,
|
||||||
|
show_2d_solid_fills: false,
|
||||||
flat_shade: false,
|
flat_shade: false,
|
||||||
},
|
},
|
||||||
M::HiddenLine => RenderModeFlags {
|
M::HiddenLine => RenderModeFlags {
|
||||||
|
|
@ -203,6 +219,7 @@ pub fn render_mode_flags(
|
||||||
mesh_fill: true,
|
mesh_fill: true,
|
||||||
show_3d_edges: true,
|
show_3d_edges: true,
|
||||||
hidden_line: true,
|
hidden_line: true,
|
||||||
|
show_2d_solid_fills: true,
|
||||||
flat_shade: false,
|
flat_shade: false,
|
||||||
},
|
},
|
||||||
M::FlatShaded => RenderModeFlags {
|
M::FlatShaded => RenderModeFlags {
|
||||||
|
|
@ -210,6 +227,7 @@ pub fn render_mode_flags(
|
||||||
mesh_fill: true,
|
mesh_fill: true,
|
||||||
show_3d_edges: false,
|
show_3d_edges: false,
|
||||||
hidden_line: false,
|
hidden_line: false,
|
||||||
|
show_2d_solid_fills: true,
|
||||||
flat_shade: true,
|
flat_shade: true,
|
||||||
},
|
},
|
||||||
M::GouraudShaded => RenderModeFlags {
|
M::GouraudShaded => RenderModeFlags {
|
||||||
|
|
@ -217,6 +235,7 @@ pub fn render_mode_flags(
|
||||||
mesh_fill: true,
|
mesh_fill: true,
|
||||||
show_3d_edges: false,
|
show_3d_edges: false,
|
||||||
hidden_line: false,
|
hidden_line: false,
|
||||||
|
show_2d_solid_fills: true,
|
||||||
flat_shade: false,
|
flat_shade: false,
|
||||||
},
|
},
|
||||||
M::FlatShadedWithEdges => RenderModeFlags {
|
M::FlatShadedWithEdges => RenderModeFlags {
|
||||||
|
|
@ -224,6 +243,7 @@ pub fn render_mode_flags(
|
||||||
mesh_fill: true,
|
mesh_fill: true,
|
||||||
show_3d_edges: true,
|
show_3d_edges: true,
|
||||||
hidden_line: false,
|
hidden_line: false,
|
||||||
|
show_2d_solid_fills: true,
|
||||||
flat_shade: true,
|
flat_shade: true,
|
||||||
},
|
},
|
||||||
M::GouraudShadedWithEdges => RenderModeFlags {
|
M::GouraudShadedWithEdges => RenderModeFlags {
|
||||||
|
|
@ -231,6 +251,7 @@ pub fn render_mode_flags(
|
||||||
mesh_fill: true,
|
mesh_fill: true,
|
||||||
show_3d_edges: true,
|
show_3d_edges: true,
|
||||||
hidden_line: false,
|
hidden_line: false,
|
||||||
|
show_2d_solid_fills: true,
|
||||||
flat_shade: false,
|
flat_shade: false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -271,7 +292,7 @@ impl shader::Primitive for Primitive {
|
||||||
inner.cached_wire_id = u64::MAX;
|
inner.cached_wire_id = u64::MAX;
|
||||||
inner.cached_selection = (u64::MAX, u64::MAX);
|
inner.cached_selection = (u64::MAX, u64::MAX);
|
||||||
inner.cached_mesh_content_id = u64::MAX;
|
inner.cached_mesh_content_id = u64::MAX;
|
||||||
inner.cached_face3d_key = (u64::MAX, false);
|
inner.cached_face3d_key = (u64::MAX, false, false);
|
||||||
inner.cached_hatch_source = None;
|
inner.cached_hatch_source = None;
|
||||||
inner.cached_preview_hatch_source = None;
|
inner.cached_preview_hatch_source = None;
|
||||||
inner.cached_wipeout_source = None;
|
inner.cached_wipeout_source = None;
|
||||||
|
|
@ -440,18 +461,26 @@ impl shader::Primitive for Primitive {
|
||||||
.map_or(true, |source| !Arc::ptr_eq(&source, &draw_depths))
|
.map_or(true, |source| !Arc::ptr_eq(&source, &draw_depths))
|
||||||
|| (inner.cached_face3d_key.0 != vp.wire_content_id
|
|| (inner.cached_face3d_key.0 != vp.wire_content_id
|
||||||
&& !face_pass_unchanged);
|
&& !face_pass_unchanged);
|
||||||
if face3d_changed || face3d_fill_active != inner.cached_face3d_key.1 {
|
if face3d_changed
|
||||||
|
|| face3d_fill_active != inner.cached_face3d_key.1
|
||||||
|
|| vp.show_2d_solid_fills != inner.cached_face3d_key.2
|
||||||
|
{
|
||||||
inner.upload_face3d(
|
inner.upload_face3d(
|
||||||
device,
|
device,
|
||||||
&vp.face3d_wires[..],
|
&vp.face3d_wires[..],
|
||||||
&vp_wires[..],
|
&vp_wires[..],
|
||||||
!face3d_fill_active,
|
!face3d_fill_active,
|
||||||
|
vp.show_2d_solid_fills,
|
||||||
&draw_depths,
|
&draw_depths,
|
||||||
);
|
);
|
||||||
inner.cached_face3d_source = Some(Arc::clone(&vp.face3d_wires));
|
inner.cached_face3d_source = Some(Arc::clone(&vp.face3d_wires));
|
||||||
inner.cached_face3d_depth_source = Some(Arc::downgrade(&draw_depths));
|
inner.cached_face3d_depth_source = Some(Arc::downgrade(&draw_depths));
|
||||||
}
|
}
|
||||||
inner.cached_face3d_key = (vp.wire_content_id, face3d_fill_active);
|
inner.cached_face3d_key = (
|
||||||
|
vp.wire_content_id,
|
||||||
|
face3d_fill_active,
|
||||||
|
vp.show_2d_solid_fills,
|
||||||
|
);
|
||||||
// Wire buffers are world-space, so a camera move alone doesn't
|
// Wire buffers are world-space, so a camera move alone doesn't
|
||||||
// change them — only the view_proj uniform (uploaded every frame).
|
// change them — only the view_proj uniform (uploaded every frame).
|
||||||
// Gate the upload on the wire content id instead of the camera tick:
|
// Gate the upload on the wire content id instead of the camera tick:
|
||||||
|
|
@ -1117,6 +1146,7 @@ fn render_signature(vp: &ViewportData, clip_w: u32, clip_h: u32) -> u64 {
|
||||||
vp.wire_content_id.hash(&mut h);
|
vp.wire_content_id.hash(&mut h);
|
||||||
vp.fill_mode.hash(&mut h);
|
vp.fill_mode.hash(&mut h);
|
||||||
vp.view_wireframe.hash(&mut h);
|
vp.view_wireframe.hash(&mut h);
|
||||||
|
vp.show_2d_solid_fills.hash(&mut h);
|
||||||
vp.mesh_fill.hash(&mut h);
|
vp.mesh_fill.hash(&mut h);
|
||||||
vp.show_3d_edges.hash(&mut h);
|
vp.show_3d_edges.hash(&mut h);
|
||||||
vp.hidden_line.hash(&mut h);
|
vp.hidden_line.hash(&mut h);
|
||||||
|
|
@ -2272,15 +2302,15 @@ impl Scene {
|
||||||
// are NOT part of this buffer anymore (they go in a separate per-frame
|
// are NOT part of this buffer anymore (they go in a separate per-frame
|
||||||
// overlay buffer below), so the base id is the source's stable content
|
// overlay buffer below), so the base id is the source's stable content
|
||||||
// gen — a drag or camera move never re-uploads the base wire set.
|
// gen — a drag or camera move never re-uploads the base wire set.
|
||||||
let wire_content_id = self.last_model_wire_gen.get();
|
let base_wire_content_id = self.last_model_wire_gen.get();
|
||||||
let wire_patch = self.model_wire_patch_for(wire_content_id);
|
let base_wire_patch = self.model_wire_patch_for(base_wire_content_id);
|
||||||
// Split Face3D wires from the rest. The split is content-only (keyed
|
// Split Face3D wires from the rest. The split is content-only (keyed
|
||||||
// by the wire-set content id), so while the geometry is unchanged it's
|
// by the wire-set content id), so while the geometry is unchanged it's
|
||||||
// memoized rather than re-walking every wire (handle lookup + clone)
|
// memoized rather than re-walking every wire (handle lookup + clone)
|
||||||
// each frame — for every source, since all ids are stable now.
|
// each frame — for every source, since all ids are stable now.
|
||||||
let (face3d_wires, other_arc) = {
|
let (face3d_wires, other_arc) = {
|
||||||
let cached = { self.split_cache.borrow().get(&wire_content_id).cloned() };
|
let cached = { self.split_cache.borrow().get(&base_wire_content_id).cloned() };
|
||||||
let inherited_empty = if let Some((base, patch)) = wire_patch.as_ref() {
|
let inherited_empty = if let Some((base, patch)) = base_wire_patch.as_ref() {
|
||||||
if patch.face_pass_changed {
|
if patch.face_pass_changed {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -2311,12 +2341,12 @@ impl Scene {
|
||||||
if c.len() > 8 {
|
if c.len() > 8 {
|
||||||
c.clear();
|
c.clear();
|
||||||
}
|
}
|
||||||
c.insert(wire_content_id, (fa.clone(), oa.clone()));
|
c.insert(base_wire_content_id, (fa.clone(), oa.clone()));
|
||||||
(fa, oa)
|
(fa, oa)
|
||||||
});
|
});
|
||||||
self.split_cache
|
self.split_cache
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.entry(wire_content_id)
|
.entry(base_wire_content_id)
|
||||||
.or_insert_with(|| (fa.clone(), oa.clone()));
|
.or_insert_with(|| (fa.clone(), oa.clone()));
|
||||||
(fa, oa.unwrap_or_else(|| Arc::clone(&base_arc)))
|
(fa, oa.unwrap_or_else(|| Arc::clone(&base_arc)))
|
||||||
};
|
};
|
||||||
|
|
@ -2325,6 +2355,24 @@ impl Scene {
|
||||||
// per-frame buffer so the (potentially huge) base buffer stays resident
|
// per-frame buffer so the (potentially huge) base buffer stays resident
|
||||||
// and unchanged while a command preview or grip drag is live.
|
// and unchanged while a command preview or grip drag is live.
|
||||||
let all_wires = other_arc;
|
let all_wires = other_arc;
|
||||||
|
// The 3-D wireframe deliberately ignores entity draw order and lets
|
||||||
|
// true depth decide overlaps. Tag its resident wire id separately so
|
||||||
|
// switching between the 2-D and 3-D styles rebuilds the GPU constants
|
||||||
|
// even though the world-space geometry itself did not change. The
|
||||||
|
// incremental patch's base id receives the same tag, preserving the
|
||||||
|
// arena fast path after the first mode switch.
|
||||||
|
let wire_mode_tag = u64::from(
|
||||||
|
inst.render_mode == acadrust::entities::ViewportRenderMode::Wireframe3D,
|
||||||
|
);
|
||||||
|
let wire_content_id = base_wire_content_id
|
||||||
|
.wrapping_mul(2)
|
||||||
|
.wrapping_add(wire_mode_tag);
|
||||||
|
let wire_patch = base_wire_patch.map(|(base, patch)| {
|
||||||
|
(
|
||||||
|
base.wrapping_mul(2).wrapping_add(wire_mode_tag),
|
||||||
|
patch,
|
||||||
|
)
|
||||||
|
});
|
||||||
// A live overlay belongs to one drawing space, but every viewport that
|
// A live overlay belongs to one drawing space, but every viewport that
|
||||||
// displays that space must project the same world-space preview. In a
|
// displays that space must project the same world-space preview. In a
|
||||||
// paper layout, model-space overlays go to all content viewports while
|
// paper layout, model-space overlays go to all content viewports while
|
||||||
|
|
@ -2490,7 +2538,13 @@ impl Scene {
|
||||||
} else {
|
} else {
|
||||||
0x3000_0000_0000_0000 | inst.handle.value()
|
0x3000_0000_0000_0000 | inst.handle.value()
|
||||||
};
|
};
|
||||||
let draw_depths = self.draw_depth_map();
|
let draw_depths = if inst.render_mode
|
||||||
|
== acadrust::entities::ViewportRenderMode::Wireframe3D
|
||||||
|
{
|
||||||
|
Arc::clone(&self.no_draw_depths)
|
||||||
|
} else {
|
||||||
|
self.draw_depth_map()
|
||||||
|
};
|
||||||
let text_verts = self.gather_text_verts(
|
let text_verts = self.gather_text_verts(
|
||||||
&all_wires,
|
&all_wires,
|
||||||
wire_content_id,
|
wire_content_id,
|
||||||
|
|
@ -2567,6 +2621,7 @@ impl Scene {
|
||||||
show_viewcube: inst.active && show_viewcube,
|
show_viewcube: inst.active && show_viewcube,
|
||||||
fill_mode: self.document.header.fill_mode,
|
fill_mode: self.document.header.fill_mode,
|
||||||
view_wireframe,
|
view_wireframe,
|
||||||
|
show_2d_solid_fills: flags.show_2d_solid_fills,
|
||||||
mesh_fill: flags.mesh_fill,
|
mesh_fill: flags.mesh_fill,
|
||||||
show_3d_edges: flags.show_3d_edges,
|
show_3d_edges: flags.show_3d_edges,
|
||||||
display_silhouette: flags.hidden_line || self.document.header.display_silhouette,
|
display_silhouette: flags.hidden_line || self.document.header.display_silhouette,
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,9 @@ use iced::{mouse, Event, Rectangle};
|
||||||
pub struct ViewportPane<'a> {
|
pub struct ViewportPane<'a> {
|
||||||
pub scene: &'a Scene,
|
pub scene: &'a Scene,
|
||||||
pub show_viewcube: bool,
|
pub show_viewcube: bool,
|
||||||
/// Render mode applied to the Model layout's tiles. Paper-space content
|
/// Render mode applied to the active Model tile or active paper viewport.
|
||||||
/// viewports use the render mode stored on their own viewport entity.
|
/// The app may supply a temporary gallery-hover mode; inactive viewports
|
||||||
|
/// keep the values stored on their own tile/entity.
|
||||||
pub render_mode: acadrust::entities::ViewportRenderMode,
|
pub render_mode: acadrust::entities::ViewportRenderMode,
|
||||||
/// `Some(tile_idx)` → this widget renders a single Model pane (one shader
|
/// `Some(tile_idx)` → this widget renders a single Model pane (one shader
|
||||||
/// per `pane_grid` pane, filling its own bounds). `None` → the unified
|
/// per `pane_grid` pane, filling its own bounds). `None` → the unified
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue