forked from 1b-agent/opencadstudio-upstream
feat(mview): add viewport creation options
Support polygonal and object viewports through native clip-boundary handles, including clipped rendering and selection. Closes #525
This commit is contained in:
1 parent
3d90450b3f
commit
b8889ccd20
10 files changed
+1038
-135
No files matched your search
@@ -138,7 +138,7 @@ impl PlineCommand {
|
||||
|
||||
/// Compute the bulge for the arc from `a` to `b` that is tangent to `tangent` at `a`.
|
||||
/// Returns 0.0 if the points are coincident or the tangent is parallel to the chord.
|
||||
fn compute_bulge(a: DVec2, tangent: DVec2, b: DVec2) -> f64 {
|
||||
pub(crate) fn compute_bulge(a: DVec2, tangent: DVec2, b: DVec2) -> f64 {
|
||||
let d = b - a;
|
||||
let len_sq = d.length_squared();
|
||||
if len_sq < 1e-10 {
|
||||
@@ -178,7 +178,7 @@ fn compute_bulge(a: DVec2, tangent: DVec2, b: DVec2) -> f64 {
|
||||
/// the chord direction rotated by half the arc sweep (the chord bisects the
|
||||
/// entry/exit tangents of a bulge arc). Used to restore tangent continuity
|
||||
/// after Undo pops a segment.
|
||||
fn seg_exit_tangent(a: DVec3, b: DVec3, bulge: f64) -> Option<Vec2> {
|
||||
pub(crate) fn seg_exit_tangent(a: DVec3, b: DVec3, bulge: f64) -> Option<Vec2> {
|
||||
let d = DVec2::new(b.x - a.x, b.y - a.y);
|
||||
if d.length_squared() < 1e-10 {
|
||||
return None;
|
||||
@@ -189,7 +189,7 @@ fn seg_exit_tangent(a: DVec3, b: DVec3, bulge: f64) -> Option<Vec2> {
|
||||
}
|
||||
|
||||
/// Update `tangent` after an arc segment described by `bulge` from `a` to `b`.
|
||||
fn update_tangent_after_arc(tangent: &mut Option<Vec2>, bulge: f64) {
|
||||
pub(crate) fn update_tangent_after_arc(tangent: &mut Option<Vec2>, bulge: f64) {
|
||||
let Some(t) = *tangent else {
|
||||
return;
|
||||
};
|
||||
@@ -203,7 +203,7 @@ fn update_tangent_after_arc(tangent: &mut Option<Vec2>, bulge: f64) {
|
||||
|
||||
/// Sample a circular arc defined by bulge into `n` line-segment points.
|
||||
/// Returns the sampled [x, y, z] points (uses `z` from `a`).
|
||||
fn arc_sample_points(a: Vec3, bulge: f64, b: Vec3, n: usize) -> Vec<[f32; 3]> {
|
||||
pub(crate) fn arc_sample_points(a: Vec3, bulge: f64, b: Vec3, n: usize) -> Vec<[f32; 3]> {
|
||||
let ax = a.x as f64;
|
||||
let ay = a.y as f64;
|
||||
let bx = b.x as f64;
|
||||
|
||||
+521
-69
@@ -1,17 +1,17 @@
|
||||
// MVIEW — interactive paper-space viewport creation.
|
||||
//
|
||||
// Two clicks define opposite corners of a new viewport rectangle.
|
||||
// The created Viewport entity is routed to add_entity_to_layout by apply_cmd_result
|
||||
// because we're in paper space.
|
||||
|
||||
use acadrust::entities::Viewport;
|
||||
use acadrust::types::Vector3;
|
||||
use acadrust::EntityType;
|
||||
use acadrust::entities::{LwPolyline, LwVertex, Viewport};
|
||||
use acadrust::tables::View;
|
||||
use acadrust::types::{Vector2, Vector3};
|
||||
use acadrust::{EntityType, Handle};
|
||||
|
||||
use crate::command::{CadCommand, CmdResult};
|
||||
use crate::command::{CadCommand, CmdOption, CmdResult};
|
||||
use crate::modules::draw::draw::polyline::{
|
||||
arc_sample_points, compute_bulge, seg_exit_tangent, update_tangent_after_arc,
|
||||
};
|
||||
use crate::modules::{IconKind, ModuleEvent, ToolDef};
|
||||
use crate::scene::model::wire_model::WireModel;
|
||||
use glam::DVec3;
|
||||
use glam::{DVec2, DVec3, Vec2};
|
||||
|
||||
// ── Ribbon definition ─────────────────────────────────────────────────────
|
||||
|
||||
@@ -26,13 +26,237 @@ pub fn tool() -> ToolDef {
|
||||
|
||||
// ── Command ───────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
enum Step {
|
||||
RectangleFirst,
|
||||
RectangleSecond,
|
||||
Polygon,
|
||||
Object,
|
||||
ChooseView,
|
||||
DefineNewFirst,
|
||||
DefineNewSecond,
|
||||
PlaceView,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
enum PolygonMode {
|
||||
Line,
|
||||
Arc,
|
||||
}
|
||||
|
||||
pub struct MviewCommand {
|
||||
corner1: Option<DVec3>,
|
||||
step: Step,
|
||||
first: Option<DVec3>,
|
||||
polygon: Vec<DVec3>,
|
||||
polygon_bulges: Vec<f64>,
|
||||
polygon_mode: PolygonMode,
|
||||
polygon_last_tangent: Option<Vec2>,
|
||||
view: Option<View>,
|
||||
views: Vec<View>,
|
||||
paper_bounds: ((f64, f64), (f64, f64)),
|
||||
original_layout: String,
|
||||
}
|
||||
|
||||
impl MviewCommand {
|
||||
pub fn new() -> Self {
|
||||
Self { corner1: None }
|
||||
pub fn new(
|
||||
original_layout: String,
|
||||
paper_bounds: ((f64, f64), (f64, f64)),
|
||||
views: Vec<View>,
|
||||
) -> Self {
|
||||
Self {
|
||||
step: Step::RectangleFirst,
|
||||
first: None,
|
||||
polygon: Vec::new(),
|
||||
polygon_bulges: Vec::new(),
|
||||
polygon_mode: PolygonMode::Line,
|
||||
polygon_last_tangent: None,
|
||||
view: None,
|
||||
views,
|
||||
paper_bounds,
|
||||
original_layout,
|
||||
}
|
||||
}
|
||||
|
||||
fn viewport_from_corners(a: DVec3, b: DVec3) -> Option<Viewport> {
|
||||
let width = (b.x - a.x).abs();
|
||||
let height = (b.y - a.y).abs();
|
||||
if width < 1e-6 || height < 1e-6 {
|
||||
return None;
|
||||
}
|
||||
let mut viewport = Viewport::new();
|
||||
viewport.center = Vector3::new(
|
||||
(a.x + b.x) / 2.0,
|
||||
(a.y + b.y) / 2.0,
|
||||
a.z,
|
||||
);
|
||||
viewport.width = width;
|
||||
viewport.height = height;
|
||||
viewport.id = 2;
|
||||
Some(viewport)
|
||||
}
|
||||
|
||||
fn fit_viewport(&self) -> Option<Viewport> {
|
||||
let ((x0, y0), (x1, y1)) = self.paper_bounds;
|
||||
Self::viewport_from_corners(
|
||||
DVec3::new(x0, y0, 0.0),
|
||||
DVec3::new(x1, y1, 0.0),
|
||||
)
|
||||
}
|
||||
|
||||
fn placed_viewport(&self, center: DVec3) -> Option<Viewport> {
|
||||
let view = self.view.as_ref()?;
|
||||
let source_width = view.width.abs().max(1e-6);
|
||||
let source_height = view.height.abs().max(1e-6);
|
||||
let ((x0, y0), (x1, y1)) = self.paper_bounds;
|
||||
let max_width = ((x1 - x0).abs() * 0.5).max(1e-6);
|
||||
let max_height = ((y1 - y0).abs() * 0.5).max(1e-6);
|
||||
let aspect = source_width / source_height;
|
||||
let (width, height) = if max_width / max_height > aspect {
|
||||
(max_height * aspect, max_height)
|
||||
} else {
|
||||
(max_width, max_width / aspect)
|
||||
};
|
||||
|
||||
let mut viewport = Viewport::new();
|
||||
viewport.center = Vector3::new(center.x, center.y, center.z);
|
||||
viewport.width = width;
|
||||
viewport.height = height;
|
||||
viewport.id = 2;
|
||||
viewport.view_target = view.target.clone();
|
||||
viewport.view_direction = view.direction.clone();
|
||||
viewport.view_height = source_height;
|
||||
viewport.custom_scale = height / source_height;
|
||||
viewport.lens_length = view.lens_length;
|
||||
viewport.twist_angle = view.twist_angle;
|
||||
viewport.status.perspective = view.perspective;
|
||||
Some(viewport)
|
||||
}
|
||||
|
||||
fn polygon_boundary(&self) -> Option<EntityType> {
|
||||
if self.polygon.len() < 3 {
|
||||
return None;
|
||||
}
|
||||
let mut polyline = LwPolyline::new();
|
||||
polyline.is_closed = true;
|
||||
polyline.elevation = self.polygon[0].z;
|
||||
polyline.vertices = self
|
||||
.polygon
|
||||
.iter()
|
||||
.zip(self.polygon_bulges.iter())
|
||||
.map(|(point, bulge)| {
|
||||
let mut vertex = LwVertex::new(Vector2::new(point.x, point.y));
|
||||
vertex.bulge = *bulge;
|
||||
vertex
|
||||
})
|
||||
.collect();
|
||||
Some(EntityType::LwPolyline(polyline))
|
||||
}
|
||||
|
||||
fn finish_polygon(&self) -> CmdResult {
|
||||
match self.polygon_boundary() {
|
||||
Some(boundary) => CmdResult::MviewCreateClipped {
|
||||
boundary: Some(boundary),
|
||||
boundary_handle: Handle::NULL,
|
||||
},
|
||||
None => CmdResult::Cancel,
|
||||
}
|
||||
}
|
||||
|
||||
fn undo_polygon(&mut self) -> CmdResult {
|
||||
self.polygon.pop();
|
||||
self.polygon_bulges.pop();
|
||||
let count = self.polygon.len();
|
||||
self.polygon_last_tangent = if count >= 2 {
|
||||
seg_exit_tangent(
|
||||
self.polygon[count - 2],
|
||||
self.polygon[count - 1],
|
||||
self.polygon_bulges[count - 2],
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
|
||||
fn polygon_preview(&self, cursor: DVec3) -> Option<WireModel> {
|
||||
let last = self.polygon.last()?.as_vec3();
|
||||
let mut points: Vec<[f32; 3]> = Vec::new();
|
||||
|
||||
for index in 0..self.polygon.len().saturating_sub(1) {
|
||||
let start = self.polygon[index].as_vec3();
|
||||
let end = self.polygon[index + 1].as_vec3();
|
||||
let bulge = self.polygon_bulges[index];
|
||||
if bulge.abs() < 1e-10 {
|
||||
if points.is_empty() {
|
||||
points.push([start.x, start.y, start.z]);
|
||||
}
|
||||
points.push([end.x, end.y, end.z]);
|
||||
} else {
|
||||
let sampled = arc_sample_points(start, bulge, end, 16);
|
||||
if points.is_empty() {
|
||||
points.extend_from_slice(&sampled);
|
||||
} else {
|
||||
points.extend_from_slice(&sampled[1..]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let cursor = cursor.as_vec3();
|
||||
match self.polygon_mode {
|
||||
PolygonMode::Line => {
|
||||
if points.is_empty() {
|
||||
points.push([last.x, last.y, last.z]);
|
||||
}
|
||||
points.push([cursor.x, cursor.y, cursor.z]);
|
||||
}
|
||||
PolygonMode::Arc => {
|
||||
let tangent = self
|
||||
.polygon_last_tangent
|
||||
.map(|value| value.as_dvec2())
|
||||
.unwrap_or(DVec2::new(1.0, 0.0));
|
||||
let bulge = compute_bulge(
|
||||
DVec2::new(last.x as f64, last.y as f64),
|
||||
tangent,
|
||||
DVec2::new(cursor.x as f64, cursor.y as f64),
|
||||
);
|
||||
let sampled = arc_sample_points(last, bulge, cursor, 16);
|
||||
if points.is_empty() {
|
||||
points.extend_from_slice(&sampled);
|
||||
} else {
|
||||
points.extend_from_slice(&sampled[1..]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(WireModel::solid(
|
||||
"mview_preview".to_string(),
|
||||
points,
|
||||
WireModel::CYAN,
|
||||
false,
|
||||
))
|
||||
}
|
||||
|
||||
fn select_view(&mut self, name: &str) -> Option<CmdResult> {
|
||||
let view = self
|
||||
.views
|
||||
.iter()
|
||||
.find(|view| view.name.eq_ignore_ascii_case(name.trim()))?
|
||||
.clone();
|
||||
self.view = Some(view);
|
||||
self.step = Step::PlaceView;
|
||||
Some(CmdResult::NeedPoint)
|
||||
}
|
||||
|
||||
fn preview(points: Vec<DVec3>) -> Option<WireModel> {
|
||||
if points.len() < 2 {
|
||||
return None;
|
||||
}
|
||||
Some(WireModel::solid_f64(
|
||||
"mview_preview".to_string(),
|
||||
points.iter().map(|point| [point.x, point.y, point.z]).collect(),
|
||||
WireModel::CYAN,
|
||||
false,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,77 +266,305 @@ impl CadCommand for MviewCommand {
|
||||
}
|
||||
|
||||
fn prompt(&self) -> String {
|
||||
if self.corner1.is_none() {
|
||||
"MVIEW Specify first corner:".to_string()
|
||||
} else {
|
||||
"MVIEW Specify opposite corner:".to_string()
|
||||
match self.step {
|
||||
Step::RectangleFirst => {
|
||||
"MVIEW Specify corner of viewport or [Polygonal/Object/Fit/Insert view]:".into()
|
||||
}
|
||||
Step::RectangleSecond => "MVIEW Specify opposite corner:".into(),
|
||||
Step::Polygon if self.polygon.is_empty() => {
|
||||
"MVIEW Polygonal Specify start point:".into()
|
||||
}
|
||||
Step::Polygon => {
|
||||
let mode = match self.polygon_mode {
|
||||
PolygonMode::Line => "Line",
|
||||
PolygonMode::Arc => "Arc",
|
||||
};
|
||||
format!(
|
||||
"MVIEW Polygonal [{mode}] Specify next point or [Arc/Line/Close/Undo] ({} points):",
|
||||
self.polygon.len()
|
||||
)
|
||||
}
|
||||
Step::Object => {
|
||||
"MVIEW Object Select a circle, full ellipse, or closed polyline:".into()
|
||||
}
|
||||
Step::ChooseView if self.views.is_empty() => {
|
||||
"MVIEW Insert view No named views; choose [New]:".into()
|
||||
}
|
||||
Step::ChooseView => "MVIEW Insert view Choose a named view or [New]:".into(),
|
||||
Step::DefineNewFirst => "MVIEW New view Specify first model-space corner:".into(),
|
||||
Step::DefineNewSecond => "MVIEW New view Specify opposite model-space corner:".into(),
|
||||
Step::PlaceView => "MVIEW Insert view Specify placement point:".into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn options(&self) -> Vec<CmdOption> {
|
||||
match self.step {
|
||||
Step::RectangleFirst => vec![
|
||||
CmdOption::new("Polygonal", "POLYGONAL"),
|
||||
CmdOption::new("Object", "OBJECT"),
|
||||
CmdOption::new("Fit", "FIT"),
|
||||
CmdOption::new("Insert view", "INSERT"),
|
||||
],
|
||||
Step::Polygon if !self.polygon.is_empty() => vec![
|
||||
CmdOption::new("Arc", "ARC"),
|
||||
CmdOption::new("Line", "LINE"),
|
||||
CmdOption::new("Close", "CLOSE"),
|
||||
CmdOption::new("Undo", "UNDO"),
|
||||
CmdOption::enter("Done"),
|
||||
],
|
||||
Step::ChooseView => {
|
||||
let mut options = vec![CmdOption::new("New", "NEW")];
|
||||
options.extend(
|
||||
self.views
|
||||
.iter()
|
||||
.map(|view| CmdOption::new(&view.name, &view.name)),
|
||||
);
|
||||
options
|
||||
}
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn on_point(&mut self, pt: DVec3) -> CmdResult {
|
||||
if let Some(c1) = self.corner1 {
|
||||
let w = (pt.x - c1.x).abs();
|
||||
let h = (pt.y - c1.y).abs();
|
||||
if w < 1.0 || h < 1.0 {
|
||||
return CmdResult::Cancel;
|
||||
match self.step {
|
||||
Step::RectangleFirst => {
|
||||
self.first = Some(pt);
|
||||
self.step = Step::RectangleSecond;
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
let cx = (c1.x + pt.x) / 2.0;
|
||||
let cy = (c1.y + pt.y) / 2.0;
|
||||
let cz = c1.z;
|
||||
Step::RectangleSecond => match self
|
||||
.first
|
||||
.and_then(|first| Self::viewport_from_corners(first, pt))
|
||||
{
|
||||
Some(viewport) => CmdResult::MviewCreate {
|
||||
viewport,
|
||||
preserve_view: false,
|
||||
},
|
||||
None => CmdResult::NeedPoint,
|
||||
},
|
||||
Step::Polygon => {
|
||||
if let Some(last) = self.polygon.last().copied() {
|
||||
let last_index = self.polygon.len() - 1;
|
||||
let bulge = match self.polygon_mode {
|
||||
PolygonMode::Line => {
|
||||
let direction = DVec2::new(pt.x - last.x, pt.y - last.y);
|
||||
if direction.length_squared() > 1e-10 {
|
||||
self.polygon_last_tangent =
|
||||
Some(direction.normalize().as_vec2());
|
||||
}
|
||||
0.0
|
||||
}
|
||||
PolygonMode::Arc => {
|
||||
let tangent = self
|
||||
.polygon_last_tangent
|
||||
.map(|value| value.as_dvec2())
|
||||
.unwrap_or(DVec2::new(1.0, 0.0));
|
||||
let bulge = compute_bulge(
|
||||
DVec2::new(last.x, last.y),
|
||||
tangent,
|
||||
DVec2::new(pt.x, pt.y),
|
||||
);
|
||||
update_tangent_after_arc(
|
||||
&mut self.polygon_last_tangent,
|
||||
bulge,
|
||||
);
|
||||
bulge
|
||||
}
|
||||
};
|
||||
self.polygon_bulges[last_index] = bulge;
|
||||
}
|
||||
|
||||
let mut vp = Viewport::new();
|
||||
vp.center = Vector3::new(cx, cy, cz);
|
||||
vp.width = w;
|
||||
vp.height = h;
|
||||
vp.id = 2; // user viewport (id > 1)
|
||||
if let Some(first) = self.polygon.first() {
|
||||
let distance_squared =
|
||||
(pt.x - first.x).powi(2) + (pt.y - first.y).powi(2);
|
||||
if self.polygon.len() >= 3 && distance_squared < 1e-12 {
|
||||
return self.finish_polygon();
|
||||
}
|
||||
}
|
||||
|
||||
CmdResult::CommitAndExit(EntityType::Viewport(vp))
|
||||
} else {
|
||||
self.corner1 = Some(pt);
|
||||
CmdResult::NeedPoint
|
||||
self.polygon.push(pt);
|
||||
self.polygon_bulges.push(0.0);
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
Step::DefineNewFirst => {
|
||||
self.first = Some(pt);
|
||||
self.step = Step::DefineNewSecond;
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
Step::DefineNewSecond => {
|
||||
let Some(first) = self.first else {
|
||||
return CmdResult::NeedPoint;
|
||||
};
|
||||
let width = (pt.x - first.x).abs();
|
||||
let height = (pt.y - first.y).abs();
|
||||
if width < 1e-6 || height < 1e-6 {
|
||||
return CmdResult::NeedPoint;
|
||||
}
|
||||
let mut view = View::new("");
|
||||
view.width = width;
|
||||
view.height = height;
|
||||
view.target = Vector3::new(
|
||||
(first.x + pt.x) / 2.0,
|
||||
(first.y + pt.y) / 2.0,
|
||||
(first.z + pt.z) / 2.0,
|
||||
);
|
||||
self.view = Some(view);
|
||||
self.step = Step::PlaceView;
|
||||
CmdResult::MviewSwitchLayout(self.original_layout.clone())
|
||||
}
|
||||
Step::PlaceView => match self.placed_viewport(pt) {
|
||||
Some(viewport) => CmdResult::MviewCreate {
|
||||
viewport,
|
||||
preserve_view: true,
|
||||
},
|
||||
None => CmdResult::Cancel,
|
||||
},
|
||||
Step::Object | Step::ChooseView => CmdResult::NeedPoint,
|
||||
}
|
||||
}
|
||||
|
||||
fn on_enter(&mut self) -> CmdResult {
|
||||
CmdResult::Cancel
|
||||
match self.step {
|
||||
Step::Polygon if self.polygon.len() >= 3 => self.finish_polygon(),
|
||||
Step::DefineNewFirst | Step::DefineNewSecond => {
|
||||
CmdResult::MviewCancelToLayout(self.original_layout.clone())
|
||||
}
|
||||
_ => CmdResult::Cancel,
|
||||
}
|
||||
}
|
||||
|
||||
fn on_escape(&mut self) -> CmdResult {
|
||||
match self.step {
|
||||
Step::DefineNewFirst | Step::DefineNewSecond => {
|
||||
CmdResult::MviewCancelToLayout(self.original_layout.clone())
|
||||
}
|
||||
_ => CmdResult::Cancel,
|
||||
}
|
||||
}
|
||||
|
||||
fn needs_entity_pick(&self) -> bool {
|
||||
self.step == Step::Object
|
||||
}
|
||||
|
||||
fn on_entity_pick(&mut self, handle: Handle, _pt: DVec3) -> CmdResult {
|
||||
if handle.is_null() {
|
||||
return CmdResult::NeedPoint;
|
||||
}
|
||||
CmdResult::MviewCreateClipped {
|
||||
boundary: None,
|
||||
boundary_handle: handle,
|
||||
}
|
||||
}
|
||||
|
||||
fn wants_text_input(&self) -> bool {
|
||||
self.step == Step::RectangleFirst
|
||||
|| self.step == Step::ChooseView
|
||||
|| (self.step == Step::Polygon && !self.polygon.is_empty())
|
||||
}
|
||||
|
||||
fn wants_text_with_spaces(&self) -> bool {
|
||||
self.step == Step::ChooseView
|
||||
}
|
||||
|
||||
fn point_step_accepts_keywords(&self) -> bool {
|
||||
self.step == Step::RectangleFirst
|
||||
|| (self.step == Step::Polygon && !self.polygon.is_empty())
|
||||
}
|
||||
|
||||
fn window_corner_pick(&self) -> bool {
|
||||
matches!(self.step, Step::RectangleSecond | Step::DefineNewSecond)
|
||||
}
|
||||
|
||||
fn window_first_corner(&self) -> Option<DVec3> {
|
||||
self.window_corner_pick().then_some(self.first).flatten()
|
||||
}
|
||||
|
||||
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
|
||||
let keyword = text.trim();
|
||||
let upper = keyword.to_ascii_uppercase();
|
||||
match self.step {
|
||||
Step::RectangleFirst => match upper.as_str() {
|
||||
"P" | "POLYGONAL" => {
|
||||
self.step = Step::Polygon;
|
||||
Some(CmdResult::NeedPoint)
|
||||
}
|
||||
"O" | "OBJECT" => {
|
||||
self.step = Step::Object;
|
||||
Some(CmdResult::NeedPoint)
|
||||
}
|
||||
"F" | "FIT" => self.fit_viewport().map(|viewport| CmdResult::MviewCreate {
|
||||
viewport,
|
||||
preserve_view: false,
|
||||
}),
|
||||
"I" | "INSERT" | "INSERTVIEW" | "INSERT VIEW" => {
|
||||
self.step = Step::ChooseView;
|
||||
Some(CmdResult::NeedPoint)
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
Step::Polygon => match upper.as_str() {
|
||||
"A" | "ARC" if !self.polygon.is_empty() => {
|
||||
self.polygon_mode = PolygonMode::Arc;
|
||||
Some(CmdResult::NeedPoint)
|
||||
}
|
||||
"L" | "LINE" if !self.polygon.is_empty() => {
|
||||
self.polygon_mode = PolygonMode::Line;
|
||||
Some(CmdResult::NeedPoint)
|
||||
}
|
||||
"C" | "CLOSE" if self.polygon.len() >= 3 => Some(self.finish_polygon()),
|
||||
"U" | "UNDO" if !self.polygon.is_empty() => {
|
||||
Some(self.undo_polygon())
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
Step::ChooseView => {
|
||||
if matches!(upper.as_str(), "N" | "NEW") {
|
||||
self.first = None;
|
||||
self.step = Step::DefineNewFirst;
|
||||
Some(CmdResult::MviewSwitchLayout("Model".to_string()))
|
||||
} else {
|
||||
self.select_view(keyword)
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn on_undo_step(&mut self) -> Option<CmdResult> {
|
||||
if self.step == Step::Polygon && !self.polygon.is_empty() {
|
||||
Some(self.undo_polygon())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn on_mouse_move(&mut self, pt: DVec3) -> Option<WireModel> {
|
||||
let pt = pt.as_vec3();
|
||||
let c1 = self.corner1?;
|
||||
Some(WireModel {
|
||||
taper_widths: Vec::new(),
|
||||
world_width: 0.0,
|
||||
depth_override: None,
|
||||
fill_is_3d: false,
|
||||
pick_tris: Vec::new(),
|
||||
pick_tris_low: Vec::new(),
|
||||
dash_from_start: false,
|
||||
dash_align_end: None,
|
||||
text_verts: Vec::new(),
|
||||
name: "mview_preview".to_string(),
|
||||
points: vec![
|
||||
[c1.x as f32, c1.y as f32, c1.z as f32],
|
||||
[pt.x, c1.y as f32, c1.z as f32],
|
||||
[pt.x, pt.y, c1.z as f32],
|
||||
[c1.x as f32, pt.y, c1.z as f32],
|
||||
[c1.x as f32, c1.y as f32, c1.z as f32],
|
||||
],
|
||||
points_low: Vec::new(),
|
||||
color: WireModel::CYAN,
|
||||
selected: false,
|
||||
pattern_length: 0.0,
|
||||
pattern: [0.0; 8],
|
||||
line_weight_px: 1.0,
|
||||
snap_pts: vec![],
|
||||
tangent_geoms: vec![],
|
||||
aci: 0,
|
||||
key_vertices: vec![],
|
||||
aabb: WireModel::UNBOUNDED_AABB,
|
||||
plinegen: true,
|
||||
fill_tris: vec![],
|
||||
fill_tris_low: Vec::new(),
|
||||
})
|
||||
match self.step {
|
||||
Step::RectangleSecond => {
|
||||
let first = self.first?;
|
||||
Self::preview(vec![
|
||||
first,
|
||||
DVec3::new(pt.x, first.y, first.z),
|
||||
DVec3::new(pt.x, pt.y, first.z),
|
||||
DVec3::new(first.x, pt.y, first.z),
|
||||
first,
|
||||
])
|
||||
}
|
||||
Step::Polygon => self.polygon_preview(pt),
|
||||
Step::PlaceView => {
|
||||
let viewport = self.placed_viewport(pt)?;
|
||||
let half_width = viewport.width / 2.0;
|
||||
let half_height = viewport.height / 2.0;
|
||||
Self::preview(vec![
|
||||
DVec3::new(pt.x - half_width, pt.y - half_height, pt.z),
|
||||
DVec3::new(pt.x + half_width, pt.y - half_height, pt.z),
|
||||
DVec3::new(pt.x + half_width, pt.y + half_height, pt.z),
|
||||
DVec3::new(pt.x - half_width, pt.y + half_height, pt.z),
|
||||
DVec3::new(pt.x - half_width, pt.y - half_height, pt.z),
|
||||
])
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user