Merge pull request #1357 from ramox81/codex/vertical-constraint-parity

Match Vertical geometric constraint behavior

# Conflicts:
#	src/app/commands/draw.rs
#	src/scene/parametric_constraints.rs
#	src/ui/overlay.rs
This commit is contained in:
Hakan Seven committed 2026-09-18 18:54:12 +03:00
commit 3ba7b38934
6 files changed
+267 -88

No files matched your search

+75 -20
View File
@@ -5,7 +5,7 @@ use crate::command::{
CadCommand, CmdOption, CmdResult, CoincidentPick, HorizontalConstraintSelection, WorkingPlane,
};
use crate::scene::parametric_constraints::{
directional_axis_endpoints, is_parametric_point_near, ParametricRef,
directional_axis_endpoints, is_parametric_point_near, ConstraintKind, ParametricRef,
};
#[derive(Clone, Copy)]
@@ -15,8 +15,10 @@ enum Step {
SecondPoint(CoincidentPick),
}
/// Interactive front end for the Horizontal geometric constraint.
/// Interactive front end for the Horizontal and Vertical geometric
/// constraints — one flow, mirrored across the working plane's X or Y axis.
pub struct HorizontalConstraintCommand {
kind: ConstraintKind,
step: Step,
picked_entity: Option<EntityType>,
plane: WorkingPlane,
@@ -24,13 +26,51 @@ pub struct HorizontalConstraintCommand {
impl HorizontalConstraintCommand {
pub fn new() -> Self {
Self::for_kind(ConstraintKind::Horizontal)
}
pub fn vertical() -> Self {
Self::for_kind(ConstraintKind::Vertical)
}
fn for_kind(kind: ConstraintKind) -> Self {
Self {
kind,
step: Step::ObjectOrTwoPoints,
picked_entity: None,
plane: WorkingPlane::default(),
}
}
/// Re-enter the 2Points flow after the host rejected a pick: back at
/// the first point, or at the second with `first` kept.
pub fn resume(kind: ConstraintKind, first: Option<CoincidentPick>) -> Self {
let mut command = Self::for_kind(kind);
command.step = match first {
Some(first) => Step::SecondPoint(first),
None => Step::FirstPoint,
};
command
}
pub fn kind(&self) -> ConstraintKind {
self.kind
}
fn axis(&self) -> &'static str {
match self.kind {
ConstraintKind::Vertical => "Vertical",
_ => "Horizontal",
}
}
fn label(&self) -> &'static str {
match self.kind {
ConstraintKind::Vertical => "Vertical constraint",
_ => "Horizontal constraint",
}
}
fn point_pick(handle: Option<Handle>, point: DVec3) -> CoincidentPick {
CoincidentPick {
handle,
@@ -40,21 +80,22 @@ impl HorizontalConstraintCommand {
}
fn direction(&self) -> acadrust::types::Vector3 {
let direction = self.plane.x.normalize_or(DVec3::X);
let direction = match self.kind {
ConstraintKind::Vertical => self.plane.y.normalize_or(DVec3::Y),
_ => self.plane.x.normalize_or(DVec3::X),
};
acadrust::types::Vector3::new(direction.x, direction.y, direction.z)
}
fn invalid_object() -> CmdResult {
CmdResult::ReportError(
"Horizontal: select a line, straight polyline segment, text, MText, or an ellipse axis."
.to_string(),
)
fn invalid_object(&self) -> CmdResult {
CmdResult::ReportError(format!(
"Invalid selection for {}. Select a line segment, polyline segment, text, MText, major or minor axis of ellipse or elliptical arc.",
self.axis()
))
}
fn invalid_point() -> CmdResult {
CmdResult::ReportError(
"Horizontal: select an endpoint, center, midpoint, or polyline vertex.".to_string(),
)
CmdResult::ReportError("No valid constraint point found.".to_string())
}
fn segment_is_straight(entity: &EntityType, index: usize) -> bool {
@@ -153,27 +194,40 @@ impl HorizontalConstraintCommand {
}
}
fn finish_points(&self, first: CoincidentPick, second: CoincidentPick) -> CmdResult {
fn finish_points(&mut self, first: CoincidentPick, second: CoincidentPick) -> CmdResult {
// The reference refuses the same pick twice and asks again for the
// second point (the host repeats this check on the resolved refs).
if first.handle == second.handle && (first.point - second.point).length() <= 1.0e-9 {
self.step = Step::SecondPoint(first);
return CmdResult::ReportError(
"The object or point is already selected. Select a different object or constraint point."
.to_string(),
);
}
CmdResult::AddHorizontalConstraint {
kind: self.kind,
selection: HorizontalConstraintSelection::Points(first, second),
direction: self.direction(),
label: "Horizontal constraint",
label: self.label(),
}
}
}
impl CadCommand for HorizontalConstraintCommand {
fn name(&self) -> &'static str {
"GCHORIZONTAL"
match self.kind {
ConstraintKind::Vertical => "GCVERTICAL",
_ => "GCHORIZONTAL",
}
}
fn prompt(&self) -> String {
match self.step {
Step::ObjectOrTwoPoints => {
"GCHORIZONTAL Select an object or [2Points] <2Points>:".to_string()
format!("{} Select an object or [2Points] <2Points>:", self.name())
}
Step::FirstPoint => "GCHORIZONTAL Select first point:".to_string(),
Step::SecondPoint(_) => "GCHORIZONTAL Select second point:".to_string(),
Step::FirstPoint => format!("{} Select first point:", self.name()),
Step::SecondPoint(_) => format!("{} Select second point:", self.name()),
}
}
@@ -237,12 +291,13 @@ impl CadCommand for HorizontalConstraintCommand {
match self.step {
Step::ObjectOrTwoPoints => {
let Some(reference) = Self::picked_reference(&entity, handle, point) else {
return Self::invalid_object();
return self.invalid_object();
};
CmdResult::AddHorizontalConstraint {
kind: self.kind,
selection: HorizontalConstraintSelection::Reference(reference),
direction: self.direction(),
label: "Horizontal constraint",
label: self.label(),
}
}
Step::FirstPoint | Step::SecondPoint(_) => {
@@ -339,5 +394,5 @@ mod tests {
}
inventory::submit!(crate::command::CommandRegistration {
names: &["GCHORIZONTAL"]
names: &["GCHORIZONTAL", "GCVERTICAL"]
});