forked from 1b-agent/opencadstudio-upstream
fix(section): add state grip menu
This commit is contained in:
1 parent
78bbd1065a
commit
0426df79ca
5 files changed
+104
-17
No files matched your search
@@ -3049,7 +3049,11 @@ impl OpenCADStudio {
|
|||||||
return Task::none();
|
return Task::none();
|
||||||
};
|
};
|
||||||
let grip_shape = self.tabs[i].selected_grips[grip_index].shape;
|
let grip_shape = self.tabs[i].selected_grips[grip_index].shape;
|
||||||
if grip_shape == crate::scene::model::object::GripShape::Dropdown {
|
if matches!(
|
||||||
|
grip_shape,
|
||||||
|
crate::scene::model::object::GripShape::Dropdown
|
||||||
|
| crate::scene::model::object::GripShape::DropdownAdjacent
|
||||||
|
) {
|
||||||
use crate::entities::traits::EntityTypeOps;
|
use crate::entities::traits::EntityTypeOps;
|
||||||
let items = self.tabs[i]
|
let items = self.tabs[i]
|
||||||
.scene
|
.scene
|
||||||
|
|||||||
+78
-10
@@ -13,7 +13,7 @@ use crate::entities::common::{
|
|||||||
use crate::entities::traits::{Grippable, PropertyEditable, Transformable, RenderConvertible};
|
use crate::entities::traits::{Grippable, PropertyEditable, Transformable, RenderConvertible};
|
||||||
use crate::scene::convert::acad_to_render::{RenderEntity, RenderObject};
|
use crate::scene::convert::acad_to_render::{RenderEntity, RenderObject};
|
||||||
use crate::scene::model::object::{
|
use crate::scene::model::object::{
|
||||||
GripApply, GripDef, PropSection, PropValue, Property,
|
GripApply, GripDef, GripMenuAction, GripMenuItem, PropSection, PropValue, Property,
|
||||||
};
|
};
|
||||||
use crate::scene::model::wire_model::SnapHint;
|
use crate::scene::model::wire_model::SnapHint;
|
||||||
|
|
||||||
@@ -25,6 +25,7 @@ const SECTION_GRIP_RIGHT: usize = 100_001;
|
|||||||
const SECTION_GRIP_TOP: usize = 100_002;
|
const SECTION_GRIP_TOP: usize = 100_002;
|
||||||
const SECTION_GRIP_BOTTOM: usize = 100_003;
|
const SECTION_GRIP_BOTTOM: usize = 100_003;
|
||||||
const SECTION_GRIP_NORMAL: usize = 100_004;
|
const SECTION_GRIP_NORMAL: usize = 100_004;
|
||||||
|
const SECTION_GRIP_STATE: usize = 100_005;
|
||||||
|
|
||||||
pub(crate) fn section_is_slice(entity: &ExtendedEntity) -> bool {
|
pub(crate) fn section_is_slice(entity: &ExtendedEntity) -> bool {
|
||||||
entity
|
entity
|
||||||
@@ -1584,6 +1585,19 @@ fn grips(entity: &ExtendedEntity) -> Vec<GripDef> {
|
|||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
};
|
};
|
||||||
|
if let Some(right) = data.vertices.last() {
|
||||||
|
let vertical = normalized(data.vertical_direction, Vector3::UNIT_Z);
|
||||||
|
let edge_center_offset = (data.top_height - data.bottom_height) * 0.5;
|
||||||
|
let anchor = *right + vertical * edge_center_offset;
|
||||||
|
grips.push(GripDef {
|
||||||
|
id: SECTION_GRIP_STATE,
|
||||||
|
world: glam::DVec3::new(anchor.x, anchor.y, anchor.z),
|
||||||
|
is_midpoint: false,
|
||||||
|
shape: crate::scene::model::object::GripShape::DropdownAdjacent,
|
||||||
|
dir: None,
|
||||||
|
axis: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
if let Some(center) = section_center(data) {
|
if let Some(center) = section_center(data) {
|
||||||
let normal = section_viewing_direction(data);
|
let normal = section_viewing_direction(data);
|
||||||
let span = data
|
let span = data
|
||||||
@@ -1789,25 +1803,25 @@ fn section_plane_edge_grips(data: &SectionObjectData) -> Vec<GripDef> {
|
|||||||
SECTION_GRIP_LEFT,
|
SECTION_GRIP_LEFT,
|
||||||
*first + vertical * edge_center_offset,
|
*first + vertical * edge_center_offset,
|
||||||
vertical,
|
vertical,
|
||||||
tangent,
|
None,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
SECTION_GRIP_RIGHT,
|
SECTION_GRIP_RIGHT,
|
||||||
*last + vertical * edge_center_offset,
|
*last + vertical * edge_center_offset,
|
||||||
vertical,
|
vertical,
|
||||||
tangent,
|
None,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
SECTION_GRIP_TOP,
|
SECTION_GRIP_TOP,
|
||||||
center + vertical * data.top_height,
|
center + vertical * data.top_height,
|
||||||
tangent,
|
tangent,
|
||||||
vertical,
|
Some(vertical),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
SECTION_GRIP_BOTTOM,
|
SECTION_GRIP_BOTTOM,
|
||||||
center - vertical * data.bottom_height,
|
center - vertical * data.bottom_height,
|
||||||
tangent,
|
tangent,
|
||||||
vertical,
|
Some(vertical),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
definitions
|
definitions
|
||||||
@@ -1818,7 +1832,7 @@ fn section_plane_edge_grips(data: &SectionObjectData) -> Vec<GripDef> {
|
|||||||
is_midpoint: false,
|
is_midpoint: false,
|
||||||
shape: crate::scene::model::object::GripShape::Rectangle,
|
shape: crate::scene::model::object::GripShape::Rectangle,
|
||||||
dir: Some(glam::DVec3::new(edge.x, edge.y, edge.z)),
|
dir: Some(glam::DVec3::new(edge.x, edge.y, edge.z)),
|
||||||
axis: Some(glam::DVec3::new(axis.x, axis.y, axis.z)),
|
axis: axis.map(|axis| glam::DVec3::new(axis.x, axis.y, axis.z)),
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
@@ -1850,19 +1864,25 @@ fn apply_section_plane_edge_grip(
|
|||||||
};
|
};
|
||||||
let span = (*last - *first).length();
|
let span = (*last - *first).length();
|
||||||
let minimum_span = 1.0e-6;
|
let minimum_span = 1.0e-6;
|
||||||
|
let in_plane_vertical = normalized(
|
||||||
|
vertical - tangent * vertical.dot(&tangent),
|
||||||
|
vertical,
|
||||||
|
);
|
||||||
|
let vertical_change = in_plane_vertical * delta.dot(&in_plane_vertical);
|
||||||
match grip_id {
|
match grip_id {
|
||||||
SECTION_GRIP_LEFT => {
|
SECTION_GRIP_LEFT => {
|
||||||
let change = delta
|
let tangent_change = delta
|
||||||
.dot(&tangent)
|
.dot(&tangent)
|
||||||
.min((span - minimum_span).max(0.0));
|
.min((span - minimum_span).max(0.0));
|
||||||
data.vertices[0] = data.vertices[0] + tangent * change;
|
data.vertices[0] = data.vertices[0] + tangent * tangent_change + vertical_change;
|
||||||
}
|
}
|
||||||
SECTION_GRIP_RIGHT => {
|
SECTION_GRIP_RIGHT => {
|
||||||
let last = data.vertices.len() - 1;
|
let last = data.vertices.len() - 1;
|
||||||
let change = delta
|
let tangent_change = delta
|
||||||
.dot(&tangent)
|
.dot(&tangent)
|
||||||
.max((-span + minimum_span).min(0.0));
|
.max((-span + minimum_span).min(0.0));
|
||||||
data.vertices[last] = data.vertices[last] + tangent * change;
|
data.vertices[last] =
|
||||||
|
data.vertices[last] + tangent * tangent_change + vertical_change;
|
||||||
}
|
}
|
||||||
SECTION_GRIP_TOP => {
|
SECTION_GRIP_TOP => {
|
||||||
data.top_height = (data.top_height + delta.dot(&vertical)).max(0.0);
|
data.top_height = (data.top_height + delta.dot(&vertical)).max(0.0);
|
||||||
@@ -2113,6 +2133,54 @@ impl Grippable for ExtendedEntity {
|
|||||||
fn apply_grip(&mut self, grip_id: usize, apply: GripApply) {
|
fn apply_grip(&mut self, grip_id: usize, apply: GripApply) {
|
||||||
apply_grip(self, grip_id, apply);
|
apply_grip(self, grip_id, apply);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn grip_menu(&self, grip_id: usize) -> Vec<GripMenuItem> {
|
||||||
|
if grip_id != SECTION_GRIP_STATE {
|
||||||
|
return Vec::new();
|
||||||
|
}
|
||||||
|
let ExtendedEntityData::SectionObject(data) = &self.data else {
|
||||||
|
return Vec::new();
|
||||||
|
};
|
||||||
|
let current = section_kind(self, data);
|
||||||
|
vec![
|
||||||
|
GripMenuItem {
|
||||||
|
label: if current == "Plane" { "✓ Plane" } else { "Plane" },
|
||||||
|
action: GripMenuAction::SectionPlane,
|
||||||
|
},
|
||||||
|
GripMenuItem {
|
||||||
|
label: if current == "Slice" { "✓ Slice" } else { "Slice" },
|
||||||
|
action: GripMenuAction::SectionSlice,
|
||||||
|
},
|
||||||
|
GripMenuItem {
|
||||||
|
label: if current == "Boundary" {
|
||||||
|
"✓ Boundary"
|
||||||
|
} else {
|
||||||
|
"Boundary"
|
||||||
|
},
|
||||||
|
action: GripMenuAction::SectionBoundary,
|
||||||
|
},
|
||||||
|
GripMenuItem {
|
||||||
|
label: if current == "Volume" { "✓ Volume" } else { "Volume" },
|
||||||
|
action: GripMenuAction::SectionVolume,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_grip_menu(&mut self, grip_id: usize, action: GripMenuAction) {
|
||||||
|
if grip_id != SECTION_GRIP_STATE
|
||||||
|
|| !matches!(self.data, ExtendedEntityData::SectionObject(_))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let value = match action {
|
||||||
|
GripMenuAction::SectionPlane => "Plane",
|
||||||
|
GripMenuAction::SectionSlice => "Slice",
|
||||||
|
GripMenuAction::SectionBoundary => "Boundary",
|
||||||
|
GripMenuAction::SectionVolume => "Volume",
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
apply_section_prop(self, "ext_section_state", value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PropertyEditable for ExtendedEntity {
|
impl PropertyEditable for ExtendedEntity {
|
||||||
|
|||||||
@@ -98,6 +98,8 @@ pub enum GripShape {
|
|||||||
Circle,
|
Circle,
|
||||||
/// Screen-offset menu selector.
|
/// Screen-offset menu selector.
|
||||||
Dropdown,
|
Dropdown,
|
||||||
|
/// Menu selector placed immediately beside its anchor grip.
|
||||||
|
DropdownAdjacent,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Describes one grip point for an entity.
|
/// Describes one grip point for an entity.
|
||||||
@@ -180,6 +182,10 @@ pub enum GripMenuAction {
|
|||||||
RefineVertices,
|
RefineVertices,
|
||||||
ShowFit,
|
ShowFit,
|
||||||
ShowControlVertices,
|
ShowControlVertices,
|
||||||
|
SectionPlane,
|
||||||
|
SectionSlice,
|
||||||
|
SectionBoundary,
|
||||||
|
SectionVolume,
|
||||||
MoveWithText,
|
MoveWithText,
|
||||||
StackText,
|
StackText,
|
||||||
UnstackText,
|
UnstackText,
|
||||||
|
|||||||
+10
-3
@@ -13,11 +13,18 @@ pub const GRIP_HALF_PX: f32 = 5.0;
|
|||||||
/// Screen offset for dropdown selectors anchored to a vertex.
|
/// Screen offset for dropdown selectors anchored to a vertex.
|
||||||
pub const GRIP_DROPDOWN_OFFSET_X_PX: f32 = 24.0;
|
pub const GRIP_DROPDOWN_OFFSET_X_PX: f32 = 24.0;
|
||||||
pub const GRIP_DROPDOWN_OFFSET_Y_PX: f32 = 27.0;
|
pub const GRIP_DROPDOWN_OFFSET_Y_PX: f32 = 27.0;
|
||||||
|
pub const GRIP_ADJACENT_DROPDOWN_OFFSET_X_PX: f32 = 12.0;
|
||||||
|
|
||||||
fn marker_screen_position(mut point: Point, shape: GripShape) -> Point {
|
fn marker_screen_position(mut point: Point, shape: GripShape) -> Point {
|
||||||
if shape == GripShape::Dropdown {
|
match shape {
|
||||||
point.x += GRIP_DROPDOWN_OFFSET_X_PX;
|
GripShape::Dropdown => {
|
||||||
point.y += GRIP_DROPDOWN_OFFSET_Y_PX;
|
point.x += GRIP_DROPDOWN_OFFSET_X_PX;
|
||||||
|
point.y += GRIP_DROPDOWN_OFFSET_Y_PX;
|
||||||
|
}
|
||||||
|
GripShape::DropdownAdjacent => {
|
||||||
|
point.x += GRIP_ADJACENT_DROPDOWN_OFFSET_X_PX;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
point
|
point
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-3
@@ -781,7 +781,7 @@ fn draw_grip_marker(
|
|||||||
b.close();
|
b.close();
|
||||||
}),
|
}),
|
||||||
GripShape::Circle => canvas::Path::circle(Point::new(sp.x, sp.y), h),
|
GripShape::Circle => canvas::Path::circle(Point::new(sp.x, sp.y), h),
|
||||||
GripShape::Dropdown => canvas::Path::new(|b| {
|
GripShape::Dropdown | GripShape::DropdownAdjacent => canvas::Path::new(|b| {
|
||||||
b.move_to(Point::new(sp.x - h, sp.y - h * 0.5));
|
b.move_to(Point::new(sp.x - h, sp.y - h * 0.5));
|
||||||
b.line_to(Point::new(sp.x + h, sp.y - h * 0.5));
|
b.line_to(Point::new(sp.x + h, sp.y - h * 0.5));
|
||||||
b.line_to(Point::new(sp.x, sp.y + h));
|
b.line_to(Point::new(sp.x, sp.y + h));
|
||||||
@@ -831,7 +831,10 @@ fn draw_grip_marker(
|
|||||||
} else {
|
} else {
|
||||||
palette.primary.base.color
|
palette.primary.base.color
|
||||||
};
|
};
|
||||||
let fill = if grip.shape == GripShape::Dropdown {
|
let fill = if matches!(
|
||||||
|
grip.shape,
|
||||||
|
GripShape::Dropdown | GripShape::DropdownAdjacent
|
||||||
|
) {
|
||||||
color
|
color
|
||||||
} else {
|
} else {
|
||||||
palette.background.base.color.scale_alpha(0.7)
|
palette.background.base.color.scale_alpha(0.7)
|
||||||
@@ -3797,4 +3800,3 @@ mod selection_visual_color_tests {
|
|||||||
assert!((selection_fill_alpha(100.0, true) - 0.45).abs() < 1e-5);
|
assert!((selection_fill_alpha(100.0, true) - 0.45).abs() < 1e-5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Reference in new issue
Block a user