feat: node graph overlay; move to opencadcodec/opencadkernel/opencadgraph

The codec and kernel repositories and crates are renamed opencadcodec and
opencadkernel. Inside OCS they are depended on under short keys, so code
reads `codec::`, `kernel::` and the new `graph::` (opencadgraph, the node
graph engine). File-format and protocol spellings stay as they were: the
ACIS product id `acadrust`, the ACADRUST_* switches and the plugin
version keys `acadrust_version` / `acadrust_source`.

A button left of MCP on the command line opens a translucent node graph
over the viewport, with a palette of object, input, math, logic, list,
text and geometry nodes added by click or drag. An object node owns
drawing entities and its rows are the Properties panel's own rows for
them: the single-object page is extracted from refresh_properties as
entity_property_sections, values use the control protocol's
property_json, and writes go through set_property_value with a property
target override, so the panel, MCP and the graph share one read/write
path. A list reaching an object row makes one entity per element. Each
graph action is one undo step; the canvas pans and zooms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Hakan SevenandClaude Opus 5.5 committed 2026-09-25 07:34:23 +03:00
1 parent 56c04408d6
commit f537aff75c
430 files changed
+9925 -8540

No files matched your search

+7 -7
View File
@@ -10,7 +10,7 @@
// With 2 pairs: translate + rotate (+ optional uniform scale to fit)
// With 3 pairs: rigid 3D placement without scaling
use acadrust::Handle;
use codec::Handle;
use glam::DVec3;
use crate::t;
@@ -127,7 +127,7 @@ impl CadCommand for AlignCommand {
}
AlignState::Src2 => {
let pair = [self.src1.unwrap().to_array(), pt.to_array()];
if cadkernel::space::align_point_pairs(&pair, &pair, false).is_none() {
if kernel::space::align_point_pairs(&pair, &pair, false).is_none() {
return CmdResult::NeedPoint;
}
self.src2 = Some(pt);
@@ -136,7 +136,7 @@ impl CadCommand for AlignCommand {
}
AlignState::Dst2 => {
let pair = [self.dst1.unwrap().to_array(), pt.to_array()];
if cadkernel::space::align_point_pairs(&pair, &pair, false).is_none() {
if kernel::space::align_point_pairs(&pair, &pair, false).is_none() {
return CmdResult::NeedPoint;
}
self.dst2 = Some(pt);
@@ -145,7 +145,7 @@ impl CadCommand for AlignCommand {
}
AlignState::Src3 => {
let frame = [self.src1.unwrap().to_array(), self.src2.unwrap().to_array(), pt.to_array()];
if cadkernel::space::align_point_pairs(&frame, &frame, false).is_none() {
if kernel::space::align_point_pairs(&frame, &frame, false).is_none() {
return CmdResult::NeedPoint;
}
self.src3 = Some(pt);
@@ -295,10 +295,10 @@ impl AlignCommand {
source.push(s3.to_array());
target.push(d3.to_array());
}
let Some(matrix) = cadkernel::space::align_point_pairs(&source, &target, with_scale)
let Some(matrix) = kernel::space::align_point_pairs(&source, &target, with_scale)
else { return CmdResult::NeedPoint; };
CmdResult::TransformSelected(self.handles.clone(), EntityTransform::Affine(
acadrust::types::Transform::from_matrix(acadrust::types::Matrix4 { m: matrix }),
codec::types::Transform::from_matrix(codec::types::Matrix4 { m: matrix }),
))
}
}
@@ -340,7 +340,7 @@ mod tests {
};
assert_eq!(handles, vec![handle]);
for (from, expected) in source.into_iter().zip(target) {
let actual = transform.apply(acadrust::types::Vector3::new(from.x, from.y, from.z));
let actual = transform.apply(codec::types::Vector3::new(from.x, from.y, from.z));
let actual = DVec3::new(actual.x, actual.y, actual.z);
assert!(actual.abs_diff_eq(expected, 1.0e-12));
}
+2 -2
View File
@@ -13,7 +13,7 @@
// Polar array: copies rotated around a center point by a total angle.
// 1. Center point → 2. Item count (text) → 3. Total angle in degrees (text)
use acadrust::Handle;
use codec::Handle;
use glam::DVec3;
use crate::t;
@@ -456,7 +456,7 @@ impl CadCommand for ArrayPolarCommand {
// 2. Enter item count (total, including the original at the path start)
// → Returns BatchCopy with Translate transforms derived from path samples.
use acadrust::EntityType;
use codec::EntityType;
use crate::entities::curve::entity_curve;
use std::f64::consts::PI as FPI;
use std::f64::consts::TAU as FTAU;
+3 -3
View File
@@ -15,7 +15,7 @@ use crate::scene::model::wire_model::WireModel;
pub struct AtteditCommand {
/// The picked block reference, once the user clicks one.
picked: Option<acadrust::Handle>,
picked: Option<codec::Handle>,
}
impl AtteditCommand {
@@ -37,7 +37,7 @@ impl CadCommand for AtteditCommand {
self.picked.is_none()
}
fn on_entity_pick(&mut self, handle: acadrust::Handle, _pt: DVec3) -> CmdResult {
fn on_entity_pick(&mut self, handle: codec::Handle, _pt: DVec3) -> CmdResult {
if handle.is_null() {
return CmdResult::NeedPoint;
}
@@ -57,7 +57,7 @@ impl CadCommand for AtteditCommand {
vec![]
}
fn attedit_pending_handle(&self) -> Option<acadrust::Handle> {
fn attedit_pending_handle(&self) -> Option<codec::Handle> {
self.picked
}
}
+7 -7
View File
@@ -5,13 +5,13 @@
//! first and second derivatives reproduce the source curves' tangent and
//! geometric curvature (G2).
use acadrust::entities::{EntityCommon, Spline};
use cadkernel::space::curve as space_curve;
use acadrust::types::Vector3;
use acadrust::{EntityType, Handle};
use codec::entities::{EntityCommon, Spline};
use kernel::space::curve as space_curve;
use codec::types::Vector3;
use codec::{EntityType, Handle};
use glam::DVec3;
use crate::t;
use cadkernel::space::NurbsCurve3;
use kernel::space::NurbsCurve3;
use crate::command::{CadCommand, CmdOption, CmdResult};
use crate::entities::common::BulgeArc;
@@ -447,7 +447,7 @@ fn polyline_endpoint(points: &[DVec3], click: DVec3) -> Option<EndpointFrame> {
}
}
fn arc_endpoint(arc: &acadrust::entities::Arc, click: DVec3) -> Option<EndpointFrame> {
fn arc_endpoint(arc: &codec::entities::Arc, click: DVec3) -> Option<EndpointFrame> {
if arc.radius <= EPS {
return None;
}
@@ -479,7 +479,7 @@ fn arc_endpoint(arc: &acadrust::entities::Arc, click: DVec3) -> Option<EndpointF
})
}
fn lw_point(poly: &acadrust::entities::LwPolyline, index: usize) -> DVec3 {
fn lw_point(poly: &codec::entities::LwPolyline, index: usize) -> DVec3 {
let normal = to_dvec(poly.normal).normalize_or_zero();
let p = poly.vertices[index].location;
ocs_to_dvec(p.x, p.y, poly.elevation, normal)
+3 -3
View File
@@ -15,7 +15,7 @@
// block record in place, so there is no copy-back and no transform — the space
// render filter (`current_layout_block_handle`) already draws only the block.
use acadrust::{EntityType, Handle};
use codec::{EntityType, Handle};
use glam::DVec3;
use crate::command::{CadCommand, CmdResult};
@@ -66,7 +66,7 @@ pub struct BlockEditSession {
/// Rebasing the origin moves them with the definition; Discard restores
/// their exact on-entry state.
pub reference_attributes:
Vec<(Handle, Vec<acadrust::entities::AttributeEntity>)>,
Vec<(Handle, Vec<codec::entities::AttributeEntity>)>,
/// Camera state of the space that was active when BEDIT began, restored
/// on Save/Discard so the view returns exactly where it was (#425).
pub return_camera: crate::scene::view::camera::Camera,
@@ -75,7 +75,7 @@ pub struct BlockEditSession {
pub editor_camera: crate::scene::view::camera::Camera,
/// Transient UCS being manipulated in this block tab. On commit its full
/// frame is baked into the definition and this returns to `None`.
pub editor_ucs: Option<acadrust::tables::Ucs>,
pub editor_ucs: Option<codec::tables::Ucs>,
}
// ── BEDIT pick command ─────────────────────────────────────────────────────
+13 -13
View File
@@ -8,11 +8,11 @@
// BREAK @ (at-sign as second point) → Break at a single point (splits without gap).
use crate::modules::draw::modify::spline_ops::{spline_cut, spline_nearest_t, spline_range};
use acadrust::entities::{
use codec::entities::{
Arc as ArcEnt, Ellipse as EllipseEnt, Line as LineEnt, LwPolyline, Spline as SplineEnt,
};
use acadrust::types::Vector3;
use acadrust::{EntityType, Handle};
use codec::types::Vector3;
use codec::{EntityType, Handle};
use glam::DVec3;
use crate::t;
@@ -88,12 +88,12 @@ fn break_line(line: &LineEnt, p1: DVec3, p2: DVec3) -> Vec<EntityType> {
result
}
fn picked_spans(curve: &cadkernel::space::PlanarCurve, p1: DVec3, p2: DVec3) -> Option<Vec<[f64; 2]>> {
fn picked_spans(curve: &kernel::space::PlanarCurve, p1: DVec3, p2: DVec3) -> Option<Vec<[f64; 2]>> {
let first = curve.plane.project(p1.to_array())?;
let second = curve.plane.project(p2.to_array())?;
let a = cadkernel::geom2d::closest_point(&curve.curve, first).t;
let b = cadkernel::geom2d::closest_point(&curve.curve, second).t;
cadkernel::geom2d::break_spans(&curve.curve, a, b, cadkernel::geom2d::Tolerance::new(1e-9))
let a = kernel::geom2d::closest_point(&curve.curve, first).t;
let b = kernel::geom2d::closest_point(&curve.curve, second).t;
kernel::geom2d::break_spans(&curve.curve, a, b, kernel::geom2d::Tolerance::new(1e-9))
}
fn break_arc(arc: &ArcEnt, p1: DVec3, p2: DVec3) -> Vec<EntityType> {
@@ -101,7 +101,7 @@ fn break_arc(arc: &ArcEnt, p1: DVec3, p2: DVec3) -> Vec<EntityType> {
let Some(spans) = picked_spans(&curve, p1, p2) else {
return vec![EntityType::Arc(arc.clone())];
};
let cadkernel::geom2d::Curve::Arc(geometry) = &curve.curve else { unreachable!() };
let kernel::geom2d::Curve::Arc(geometry) = &curve.curve else { unreachable!() };
spans.into_iter().map(|[from, to]| {
let mut result = arc.clone();
result.common.handle = Handle::NULL;
@@ -111,7 +111,7 @@ fn break_arc(arc: &ArcEnt, p1: DVec3, p2: DVec3) -> Vec<EntityType> {
}).collect()
}
fn break_circle(circle: &acadrust::entities::Circle, p1: DVec3, p2: DVec3) -> Vec<EntityType> {
fn break_circle(circle: &codec::entities::Circle, p1: DVec3, p2: DVec3) -> Vec<EntityType> {
let curve = crate::entities::curve::circle_curve(circle);
let Some(spans) = picked_spans(&curve, p1, p2) else {
return vec![EntityType::Circle(circle.clone())];
@@ -133,7 +133,7 @@ fn break_lwpolyline(p: &LwPolyline, p1: DVec3, p2: DVec3) -> Vec<EntityType> {
let unchanged = || vec![EntityType::LwPolyline(p.clone())];
let Some(curve) = crate::entities::curve::lwpolyline_curve(p) else { return unchanged(); };
let Some(spans) = picked_spans(&curve, p1, p2) else { return unchanged(); };
let cadkernel::geom2d::Curve::Polyline(geometry) = &curve.curve else { unreachable!() };
let kernel::geom2d::Curve::Polyline(geometry) = &curve.curve else { unreachable!() };
let mut result = Vec::with_capacity(spans.len());
for [from, to] in spans {
let Some(range) = geometry.ranged(from, to) else { return unchanged(); };
@@ -172,7 +172,7 @@ fn break_ellipse(ell: &EllipseEnt, p1: DVec3, p2: DVec3) -> Vec<EntityType> {
let Some(spans) = picked_spans(&curve, p1, p2) else {
return vec![EntityType::Ellipse(ell.clone())];
};
let cadkernel::geom2d::Curve::Ellipse(geometry) = &curve.curve else { unreachable!() };
let kernel::geom2d::Curve::Ellipse(geometry) = &curve.curve else { unreachable!() };
spans.into_iter().map(|[from, to]| {
let mut result = ell.clone();
result.common.handle = Handle::NULL;
@@ -387,8 +387,8 @@ inventory::submit!(crate::command::CommandRegistration { names: &["BREAK"] });
#[cfg(test)]
mod tests {
use super::*;
use acadrust::entities::LwVertex;
use acadrust::types::Vector2;
use codec::entities::LwVertex;
use codec::types::Vector2;
#[test]
fn first_option_replaces_the_selection_point_and_at_reuses_it() {
+1 -1
View File
@@ -5,7 +5,7 @@
// Step 1: pick base point
// Step 2+: each click makes another copy at (click - base); Enter to finish.
use acadrust::Handle;
use codec::Handle;
use glam::DVec3;
use crate::command::{CadCommand, CmdResult, EntityTransform};
+1 -1
View File
@@ -5,7 +5,7 @@
//! as TRIM, to nearby boundary entities. Keeping this compact command-local
//! index avoids cloning/scanning the complete drawing on every mouse move.
use acadrust::{EntityType, Handle};
use codec::{EntityType, Handle};
use rustc_hash::{FxHashMap, FxHashSet};
use crate::scene::convert::tess::entity_world_aabb_f64;
+44 -44
View File
@@ -8,7 +8,7 @@
// Polyline2D → Lines + Arcs
// Polyline3D → Lines
// Polyline → Lines
// Insert → constituent entities (via acadrust explode_from_document)
// Insert → constituent entities (via opencadcodec explode_from_document)
// MLine → Lines (spine + offset lines per miter direction)
// Dimension → Lines (extension + dimension + arrows) + Text
//
@@ -16,15 +16,15 @@
use super::geom::normalize_angle as norm_angle;
use acadrust::entities::EntityCommon;
use acadrust::entities::{
use codec::entities::EntityCommon;
use codec::entities::{
Arc as ArcEnt, Block, BlockEnd, Circle as CircleEnt, Dimension, Line as LineEnt, LwPolyline,
MLine,
};
use acadrust::entities::{Polyline, Polyline2D};
use acadrust::tables::BlockRecord;
use acadrust::types::Vector3;
use acadrust::{CadDocument, EntityType, Handle};
use codec::entities::{Polyline, Polyline2D};
use codec::tables::BlockRecord;
use codec::types::Vector3;
use codec::{CadDocument, EntityType, Handle};
use crate::command::{CadCommand, CmdResult, WorkingPlane};
use crate::entities::curve::lwpolyline_world_xy;
@@ -64,7 +64,7 @@ pub fn explode_polyline_segments(entity: &EntityType) -> Vec<EntityType> {
pub fn explode_entity(entity: &EntityType, document: &CadDocument) -> Vec<EntityType> {
match entity {
EntityType::Line(line) => {
let Some(association) = acadrust::entities::CenterMarkAssociation::read(
let Some(association) = codec::entities::CenterMarkAssociation::read(
&line.common.extended_data,
) else {
return vec![];
@@ -74,7 +74,7 @@ pub fn explode_entity(entity: &EntityType, document: &CadDocument) -> Vec<Entity
.map(|segment| {
let mut common = line.common.clone();
common.handle = Handle::NULL;
acadrust::entities::CenterMarkAssociation::remove(&mut common.extended_data);
codec::entities::CenterMarkAssociation::remove(&mut common.extended_data);
EntityType::Line(LineEnt {
common,
start: Vector3::new(segment[0].x, segment[0].y, segment[0].z),
@@ -141,7 +141,7 @@ fn explode_polyline(p: &Polyline) -> Vec<EntityType> {
result
}
fn explode_polyline3d(p: &acadrust::entities::Polyline3D) -> Vec<EntityType> {
fn explode_polyline3d(p: &codec::entities::Polyline3D) -> Vec<EntityType> {
let n = p.vertices.len();
if n < 2 {
return vec![];
@@ -309,7 +309,7 @@ fn bulge_to_arc(
) -> Option<EntityType> {
let ba = crate::entities::common::BulgeArc::from_bulge(p0, p1, bulge)?;
// acadrust Arc is always CCW from start_angle to end_angle. Negative
// opencadcodec Arc is always CCW from start_angle to end_angle. Negative
// bulge means the polyline goes p0→p1 the CW way around the centre,
// which is the same circular arc traversed p1→p0 the CCW way — so
// swap endpoints when bulge < 0.
@@ -340,15 +340,15 @@ fn explode_mline(ml: &MLine) -> Vec<EntityType> {
if n < 2 {
return vec![];
}
let closed = ml.flags.contains(acadrust::entities::MLineFlags::CLOSED);
let closed = ml.flags.contains(codec::entities::MLineFlags::CLOSED);
let scale = ml.scale_factor;
let n_segs = if closed { n } else { n - 1 };
let mut result = Vec::new();
// Helper: build a Line from two Vector3 positions.
let make_line = |common: &acadrust::entities::EntityCommon,
s: &acadrust::types::Vector3,
e: &acadrust::types::Vector3|
let make_line = |common: &codec::entities::EntityCommon,
s: &codec::types::Vector3,
e: &codec::types::Vector3|
-> EntityType {
let mut c = common.clone();
c.handle = Handle::NULL;
@@ -395,7 +395,7 @@ fn explode_mline(ml: &MLine) -> Vec<EntityType> {
/// Convert a Dimension entity into Lines (geometry) + Text (label).
/// A NULL-handle line segment for a baked dimension block.
fn dim_seg(a: Vector3, b: Vector3, common: &acadrust::entities::EntityCommon) -> EntityType {
fn dim_seg(a: Vector3, b: Vector3, common: &codec::entities::EntityCommon) -> EntityType {
let mut c = common.clone();
c.handle = Handle::NULL;
EntityType::Line(LineEnt {
@@ -408,8 +408,8 @@ fn dim_seg(a: Vector3, b: Vector3, common: &acadrust::entities::EntityCommon) ->
fn dim_geom_entities(
geometry: &crate::scene::convert::tessellate::DimGeom,
ext_common: &acadrust::entities::EntityCommon,
dim_common: &acadrust::entities::EntityCommon,
ext_common: &codec::entities::EntityCommon,
dim_common: &codec::entities::EntityCommon,
) -> Vec<EntityType> {
let mut entities = Vec::new();
let point = |value: [f32; 3]| {
@@ -426,7 +426,7 @@ fn dim_geom_entities(
}
}
for triangle in geometry.arrow_fill.chunks_exact(3) {
let mut solid = acadrust::entities::Solid::triangle(
let mut solid = codec::entities::Solid::triangle(
point(triangle[0]),
point(triangle[1]),
point(triangle[2]),
@@ -448,7 +448,7 @@ fn dim_terminator(
dx: f64,
dy: f64,
arrow: &crate::scene::convert::tessellate::ArrowKind,
common: &acadrust::entities::EntityCommon,
common: &codec::entities::EntityCommon,
) -> Vec<EntityType> {
use crate::scene::convert::tessellate::ArrowKind as A;
let (dx, dy) = norm2(dx, dy, 1.0, 0.0);
@@ -459,7 +459,7 @@ fn dim_terminator(
};
let mut out: Vec<EntityType> = Vec::new();
let tri = |a: Vector3, b: Vector3, c: Vector3, out: &mut Vec<EntityType>| {
let mut s = acadrust::entities::Solid::triangle(a, b, c);
let mut s = codec::entities::Solid::triangle(a, b, c);
s.common = common.clone();
s.common.handle = Handle::NULL;
out.push(EntityType::Solid(s));
@@ -565,7 +565,7 @@ fn terminator_circle(
center: Vector3,
r: f64,
filled: bool,
common: &acadrust::entities::EntityCommon,
common: &codec::entities::EntityCommon,
out: &mut Vec<EntityType>,
) {
const N: usize = 16;
@@ -580,7 +580,7 @@ fn terminator_circle(
}
if filled {
for i in 0..N {
let mut s = acadrust::entities::Solid::triangle(center, ring[i], ring[i + 1]);
let mut s = codec::entities::Solid::triangle(center, ring[i], ring[i + 1]);
s.common = common.clone();
s.common.handle = Handle::NULL;
out.push(EntityType::Solid(s));
@@ -593,7 +593,7 @@ fn dim_center_mark(
center: Vector3,
dimcen: f64,
radius: f64,
common: &acadrust::entities::EntityCommon,
common: &codec::entities::EntityCommon,
) -> Vec<EntityType> {
let mag = dimcen.abs();
if mag < 1e-9 {
@@ -624,7 +624,7 @@ fn dim_arc_segs(
radius: f64,
a1: f64,
a2: f64,
common: &acadrust::entities::EntityCommon,
common: &codec::entities::EntityCommon,
) -> Vec<EntityType> {
use std::f64::consts::PI;
let mut sweep = a2 - a1;
@@ -644,7 +644,7 @@ fn dim_arc_segs_with_sweep(
radius: f64,
start: f64,
sweep: f64,
common: &acadrust::entities::EntityCommon,
common: &codec::entities::EntityCommon,
) -> Vec<EntityType> {
use std::f64::consts::PI;
let steps = 12usize.max((sweep.abs() / (PI / 36.0)).ceil() as usize);
@@ -759,25 +759,25 @@ fn dim_metrics(dim: &Dimension, doc: &CadDocument) -> DimMetrics {
/// A DIMCLR* of 0 (ByBlock) or 256 (ByLayer) keeps the dimension's own colour
/// so the block inherits it; a specific ACI overrides. DIMLW* < 0 (ByBlock /
/// ByLayer) keeps the dimension's lineweight.
fn dim_common(base: &acadrust::entities::EntityCommon, clr: i16, lw: i16) -> acadrust::entities::EntityCommon {
fn dim_common(base: &codec::entities::EntityCommon, clr: i16, lw: i16) -> codec::entities::EntityCommon {
let mut c = base.clone();
c.handle = Handle::NULL;
if clr != 0 && clr != 256 {
c.color = acadrust::types::Color::from_index(clr);
c.color = codec::types::Color::from_index(clr);
c.color_name = None;
c.color_book_handle = None;
}
if lw >= 0 {
c.line_weight = acadrust::types::LineWeight::from_value(lw);
c.line_weight = codec::types::LineWeight::from_value(lw);
}
c
}
fn with_dim_linetype(
mut common: acadrust::entities::EntityCommon,
mut common: codec::entities::EntityCommon,
doc: &CadDocument,
handle: Handle,
) -> acadrust::entities::EntityCommon {
) -> codec::entities::EntityCommon {
common.linetype = doc
.line_types
.iter()
@@ -800,8 +800,8 @@ fn angular_block_segs(
p2: Vector3,
arc_loc: Vector3,
met: &DimMetrics,
ext_c: &acadrust::entities::EntityCommon,
dim_c: &acadrust::entities::EntityCommon,
ext_c: &codec::entities::EntityCommon,
dim_c: &codec::entities::EntityCommon,
explicit_sweep: Option<(f64, f64)>,
) -> Vec<EntityType> {
use std::f64::consts::PI;
@@ -853,7 +853,7 @@ fn angular_block_segs(
/// The text anchor for a radial leader: the saved text middle point when set,
/// else the midpoint of `a` and `b` — mirroring the live `dimension_text_position`.
fn dim_text_anchor(
base: &acadrust::entities::dimension::DimensionBase,
base: &codec::entities::dimension::DimensionBase,
a: Vector3,
b: Vector3,
) -> Vector3 {
@@ -1495,15 +1495,15 @@ impl CadCommand for ExplodeCommand {
#[cfg(test)]
mod tests {
use super::*;
use acadrust::entities::DimensionLinear;
use codec::entities::DimensionLinear;
#[test]
fn minsert_too_large_to_materialize_is_left_intact() {
let mut doc = CadDocument::new();
let mut block = acadrust::tables::BlockRecord::new("B1");
let mut block = codec::tables::BlockRecord::new("B1");
block.handle = doc.allocate_handle();
doc.block_records.add(block).unwrap();
let mut insert = acadrust::entities::Insert::new("B1", Vector3::new(0.0, 0.0, 0.0));
let mut insert = codec::entities::Insert::new("B1", Vector3::new(0.0, 0.0, 0.0));
insert.row_count = u16::MAX;
insert.column_count = u16::MAX;
assert!(explode_entity(&EntityType::Insert(insert), &doc).is_empty());
@@ -1512,12 +1512,12 @@ mod tests {
#[test]
fn unexplodable_block_is_not_exploded() {
let mut doc = CadDocument::new();
let mut block = acadrust::tables::BlockRecord::new("NO_EXPLODE");
let mut block = codec::tables::BlockRecord::new("NO_EXPLODE");
block.handle = doc.allocate_handle();
block.explodable = false;
doc.block_records.add(block).unwrap();
let insert = acadrust::entities::Insert::new("NO_EXPLODE", Vector3::new(0.0, 0.0, 0.0));
let insert = codec::entities::Insert::new("NO_EXPLODE", Vector3::new(0.0, 0.0, 0.0));
assert!(explode_entity(&EntityType::Insert(insert), &doc).is_empty());
}
@@ -1772,7 +1772,7 @@ mod tests {
// two extension lines plus many arc chords.
#[test]
fn angular_dim_bakes_an_arc() {
use acadrust::entities::DimensionAngular3Pt;
use codec::entities::DimensionAngular3Pt;
let mut doc = CadDocument::new();
let mut d = DimensionAngular3Pt::new(
Vector3::new(0.0, 0.0, 0.0),
@@ -1799,7 +1799,7 @@ mod tests {
// Diameter endpoints stay equidistant from the circle center.
#[test]
fn diameter_dim_bakes_through_center() {
use acadrust::entities::DimensionDiameter;
use codec::entities::DimensionDiameter;
let mut doc = CadDocument::new();
let center = Vector3::new(3.0, 4.0, 0.0);
let edge = Vector3::new(8.0, 4.0, 0.0); // radius 5 along +x
@@ -1864,8 +1864,8 @@ mod tests {
// must carry the source thickness instead of resetting to 0 (#916).
#[test]
fn explode_keeps_lwpolyline_thickness() {
use acadrust::entities::LwVertex;
use acadrust::types::Vector2;
use codec::entities::LwVertex;
use codec::types::Vector2;
let mut pl = LwPolyline::new();
// One straight span + one bulged span, so the result holds a Line and
@@ -1900,7 +1900,7 @@ mod tests {
#[test]
fn explode_keeps_polyline2d_extrusion() {
use acadrust::entities::Vertex2D;
use codec::entities::Vertex2D;
let mut pl = Polyline2D::new();
pl.vertices = vec![
+10 -10
View File
@@ -9,19 +9,19 @@
// Pick two lines (line-only; arcs are not chamferable).
// Finds intersection, backs off dist1 along line 1 and dist2 along line 2.
use acadrust::entities::{Arc as ArcEnt, Line as LineEnt, LwPolyline};
use codec::entities::{Arc as ArcEnt, Line as LineEnt, LwPolyline};
// Shared plane geometry, from cadkernel via the local adapters.
// Shared plane geometry, from opencadkernel via the local adapters.
use super::geom;
use super::geom::{arc_points as arc_pts, line_line as ll, normalize_angle as norm_angle};
use cadkernel::geom2d::{
use kernel::geom2d::{
circle_circle_points as circle_circle_pts, fillet_between_rays, fillets_between, line_circle,
Arc as KernelArc, Curve as KernelCurve, Fillet as KernelFillet,
Line as KernelLine, Tolerance,
};
use acadrust::entities::EntityCommon;
use acadrust::types::Vector3;
use acadrust::{EntityType, Handle};
use codec::entities::EntityCommon;
use codec::types::Vector3;
use codec::{EntityType, Handle};
use glam::DVec3;
use crate::t;
@@ -1552,7 +1552,7 @@ impl CadCommand for FilletCommand {
self.undo_last()
}
fn on_document_undone(&mut self, document: &acadrust::CadDocument) {
fn on_document_undone(&mut self, document: &codec::CadDocument) {
self.all_entities = document
.entities()
.map(crate::entities::curve::entity_with_lwpolyline_world_xy)
@@ -2147,7 +2147,7 @@ impl CadCommand for ChamferCommand {
self.undo_last()
}
fn on_document_undone(&mut self, document: &acadrust::CadDocument) {
fn on_document_undone(&mut self, document: &codec::CadDocument) {
self.all_entities = document
.entities()
.map(crate::entities::curve::entity_with_lwpolyline_world_xy)
@@ -2377,7 +2377,7 @@ mod tests {
/// by an infinite factor: the center becomes (inf, NaN).
fn overflowed_arc(sweep_sign: f64) -> ArcEnt {
let mut arc = ArcEnt::new();
arc.center = acadrust::types::Vector3::new(f64::INFINITY, f64::NAN, 0.0);
arc.center = codec::types::Vector3::new(f64::INFINITY, f64::NAN, 0.0);
arc.radius = f64::INFINITY;
arc.start_angle = 0.0;
arc.end_angle = sweep_sign * std::f64::consts::PI;
@@ -2410,7 +2410,7 @@ mod tests {
assert!(matches!(command.on_text_input("U"), Some(CmdResult::UndoDocument)));
assert_eq!(keywords(&command), ["P", "R"]);
// The host hands the restored document back; the cache follows it.
let mut doc = acadrust::CadDocument::new();
let mut doc = codec::CadDocument::new();
let _ = doc.add_entity(line(0.0, 0.0, 20.0, 0.0, 7));
command.on_document_undone(&doc);
assert_eq!(command.all_entities.len(), 1);
+3 -3
View File
@@ -1,4 +1,4 @@
//! Adapters between the editing commands and `cadkernel`'s plane geometry.
//! Adapters between the editing commands and `opencadkernel`'s plane geometry.
//!
//! The kernel takes points as `[f64; 2]` and hands tessellation back as
//! `[f64; 3]`. The commands here grew up passing loose `x, y` scalars and
@@ -11,11 +11,11 @@
//! resident render path splits the `f64` into a high/low pair instead. Only a
//! caller knows which of the two it is.
use cadkernel::geom2d::{self, Ellipse};
use kernel::geom2d::{self, Ellipse};
/// Re-exported unchanged: these already speak in plain `f64`, so there is no
/// call-shape difference for this module to absorb.
pub use cadkernel::geom2d::{arc_parameter, lerp, normalize_angle};
pub use kernel::geom2d::{arc_parameter, lerp, normalize_angle};
/// Preview geometry keeps the density the commands have always used.
const SEGMENTS_PER_RADIAN: f64 = geom2d::DEFAULT_SEGMENTS_PER_RADIAN;
+28 -28
View File
@@ -9,8 +9,8 @@
//
// Workflow: select objects then press Enter to join.
use acadrust::types::{Vector2, Vector3};
use acadrust::{EntityType, Handle};
use codec::types::{Vector2, Vector3};
use codec::{EntityType, Handle};
use glam::DVec3;
use crate::command::{CadCommand, CmdResult};
@@ -48,7 +48,7 @@ impl CadCommand for JoinCommand {
fn on_text_input(&mut self,text:&str) -> Option<CmdResult> {
if matches!(text.trim().to_ascii_uppercase().as_str(),"L"|"CLOSE") {
if let Some((handle,EntityType::Arc(arc)))=&self.source {
let mut circle=acadrust::entities::Circle::new();
let mut circle=codec::entities::Circle::new();
circle.common=arc.common.clone();circle.common.handle=Handle::NULL;
circle.center=arc.center.clone();circle.normal=arc.normal.clone();circle.radius=arc.radius;circle.thickness=arc.thickness;
return Some(CmdResult::ReplaceMany(vec![(*handle,vec![EntityType::Circle(circle)])],Vec::new()));
@@ -95,23 +95,23 @@ pub fn join_to_source(source: &EntityType, candidates: &[(Handle, &EntityType)])
let next = match (&result, *candidate) {
(EntityType::Line(a), EntityType::Line(b)) => {
let point = |p: &Vector3| [p.x,p.y,p.z];
cadkernel::space::source_join::join_collinear_lines([point(&a.start),point(&a.end)],[point(&b.start),point(&b.end)],JOIN_EPS).map(|span| {
kernel::space::source_join::join_collinear_lines([point(&a.start),point(&a.end)],[point(&b.start),point(&b.end)],JOIN_EPS).map(|span| {
let mut line = a.clone(); line.start = Vector3::new(span[0][0],span[0][1],span[0][2]); line.end = Vector3::new(span[1][0],span[1][1],span[1][2]); EntityType::Line(line)
})
}
(EntityType::Arc(a), EntityType::Arc(b)) => {
let data = |a: &acadrust::entities::Arc| ([a.center.x,a.center.y,a.center.z],[a.normal.x,a.normal.y,a.normal.z],a.radius,[a.start_angle,a.end_angle]);
cadkernel::space::source_join::join_cocircular_arcs(data(a),data(b),JOIN_EPS).map(|span| {
let data = |a: &codec::entities::Arc| ([a.center.x,a.center.y,a.center.z],[a.normal.x,a.normal.y,a.normal.z],a.radius,[a.start_angle,a.end_angle]);
kernel::space::source_join::join_cocircular_arcs(data(a),data(b),JOIN_EPS).map(|span| {
if span[1]-span[0] >= std::f64::consts::TAU {
let mut circle = acadrust::entities::Circle::new(); circle.common = a.common.clone(); circle.center = a.center.clone(); circle.normal = a.normal.clone(); circle.radius = a.radius; circle.thickness = a.thickness; EntityType::Circle(circle)
let mut circle = codec::entities::Circle::new(); circle.common = a.common.clone(); circle.center = a.center.clone(); circle.normal = a.normal.clone(); circle.radius = a.radius; circle.thickness = a.thickness; EntityType::Circle(circle)
} else { let mut arc = a.clone(); arc.start_angle = span[0]; arc.end_angle = span[1].rem_euclid(std::f64::consts::TAU); EntityType::Arc(arc) }
})
}
(EntityType::Spline(source), candidate) => {
let curve = |s:&acadrust::entities::Spline| {
let curve = |s:&codec::entities::Spline| {
if s.flags.closed || s.flags.periodic {return None;}
if s.control_points.is_empty() && s.fit_points.len() >= 2 {
use cadkernel::space::{NurbsCurve3, Parameterization};
use kernel::space::{NurbsCurve3, Parameterization};
let points: Vec<_> = s.fit_points.iter().map(|point| [point.x,point.y,point.z]).collect();
let parameterization = match s.knot_parameterization {
1 => Parameterization::Centripetal, 2 => Parameterization::Uniform, _ => Parameterization::Chord,
@@ -122,22 +122,22 @@ pub fn join_to_source(source: &EntityType, candidates: &[(Handle, &EntityType)])
.compact_knots(s.control_tolerance.max(1e-9));
}
let weights=if s.weights.is_empty(){vec![1.0;s.control_points.len()]}else{s.weights.clone()};
cadkernel::space::NurbsCurve3::new_strict(s.degree as usize,s.control_points.iter().map(|p|[p.x,p.y,p.z]).collect(),s.knots.clone(),weights)
kernel::space::NurbsCurve3::new_strict(s.degree as usize,s.control_points.iter().map(|p|[p.x,p.y,p.z]).collect(),s.knots.clone(),weights)
};
curve(source).and_then(|a| {
let b=match candidate {
EntityType::Spline(s)=>curve(s),
EntityType::Line(line)=>cadkernel::space::source_join::line_as_nurbs([[line.start.x,line.start.y,line.start.z],[line.end.x,line.end.y,line.end.z]],a.degree()),
EntityType::Line(line)=>kernel::space::source_join::line_as_nurbs([[line.start.x,line.start.y,line.start.z],[line.end.x,line.end.y,line.end.z]],a.degree()),
_=>None,
}?;
let joined=cadkernel::space::source_join::join_nurbs_curves(&a,&b,JOIN_EPS)?;
let joined=kernel::space::source_join::join_nurbs_curves(&a,&b,JOIN_EPS)?;
let mut spline=source.clone();
spline.degree=joined.degree() as i32;
spline.control_points=joined.control_points().iter().map(|p|Vector3::new(p[0],p[1],p[2])).collect();
spline.knots=joined.knots().to_vec();spline.weights=joined.weights().to_vec();
spline.fit_points.clear();spline.flags.rational=joined.is_rational();
spline.dwg_flags1 &= !1;spline.dxf_flags &= !(32 | 1024);
spline.flags.planar=cadkernel::space::are_coplanar(joined.control_points(),&[]);
spline.flags.planar=kernel::space::are_coplanar(joined.control_points(),&[]);
spline.begin_tangent=Vector3::new(0.0,0.0,0.0);spline.end_tangent=Vector3::new(0.0,0.0,0.0);
Some(EntityType::Spline(spline))
})
@@ -261,7 +261,7 @@ fn segs_of(e: &EntityType) -> Option<Vec<Seg>> {
if normal.x.abs() > 1e-12 || normal.y.abs() > 1e-12 {
return None;
}
let cadkernel::geom2d::Curve::Polyline(curve) =
let kernel::geom2d::Curve::Polyline(curve) =
crate::entities::curve::entity_curve_xy(&EntityType::Polyline2D(p.clone()))?
else {
return None;
@@ -327,7 +327,7 @@ pub fn join_entities(entities: &[(Handle, &EntityType)]) -> Option<(Vec<Handle>,
// An open run of collinear straight segments collapses back to one Line.
if !closed && !has_arc && chain.iter().all(|segment| segment.widths == Some((0.0,0.0))) && !matches!(entities[0].1, EntityType::LwPolyline(_) | EntityType::Polyline2D(_)) && is_collinear(&verts) {
let mut line = acadrust::entities::Line::new();
let mut line = codec::entities::Line::new();
line.common = common;
line.common.handle = Handle::NULL;
line.start = v3(verts.first().unwrap().0);
@@ -342,10 +342,10 @@ pub fn join_entities(entities: &[(Handle, &EntityType)]) -> Option<(Vec<Handle>,
return None;
}
let flipped = thickness != 0.0 && normal.z < 0.0;
let lw_verts: Vec<acadrust::entities::LwVertex> = verts
let lw_verts: Vec<codec::entities::LwVertex> = verts
.iter().enumerate()
.map(|(index,(p, bulge))| {
let mut v = acadrust::entities::LwVertex::new(Vector2::new(
let mut v = codec::entities::LwVertex::new(Vector2::new(
if flipped { -p.x } else { p.x },
p.y,
));
@@ -356,7 +356,7 @@ pub fn join_entities(entities: &[(Handle, &EntityType)]) -> Option<(Vec<Handle>,
v
})
.collect();
let mut pl = acadrust::entities::LwPolyline::new();
let mut pl = codec::entities::LwPolyline::new();
pl.common = common;
pl.common.handle = Handle::NULL;
pl.vertices = lw_verts;
@@ -372,8 +372,8 @@ pub fn join_entities(entities: &[(Handle, &EntityType)]) -> Option<(Vec<Handle>,
let output = crate::entities::curve::ocs_plane(source.normal.clone(),source.elevation);
polyline.vertices = pl.vertices.iter().map(|vertex| {
let point = output.project(input.point_at([vertex.location.x,vertex.location.y]))?;
let mut result = acadrust::entities::polyline::Vertex2D::new(Vector3::new(point[0],point[1],source.elevation));
result.bulge = if cadkernel::space::Vec3::from(input.normal()?).dot(cadkernel::space::Vec3::from(output.normal()?)) < 0.0 {-vertex.bulge} else {vertex.bulge}; result.start_width = vertex.start_width; result.end_width = vertex.end_width;
let mut result = codec::entities::polyline::Vertex2D::new(Vector3::new(point[0],point[1],source.elevation));
result.bulge = if kernel::space::Vec3::from(input.normal()?).dot(kernel::space::Vec3::from(output.normal()?)) < 0.0 {-vertex.bulge} else {vertex.bulge}; result.start_width = vertex.start_width; result.end_width = vertex.end_width;
Some(result)
}).collect::<Option<Vec<_>>>()?;
polyline.start_width = 0.0; polyline.end_width = 0.0;
@@ -397,12 +397,12 @@ pub fn join_entities(entities: &[(Handle, &EntityType)]) -> Option<(Vec<Handle>,
if thickness != 0.0 {
return None;
}
let mut pl = acadrust::entities::Polyline3D::new();
let mut pl = codec::entities::Polyline3D::new();
pl.common = common;
pl.common.handle = Handle::NULL;
pl.vertices = verts
.iter()
.map(|(p, _)| acadrust::entities::Vertex3DPolyline::new(v3(*p)))
.map(|(p, _)| codec::entities::Vertex3DPolyline::new(v3(*p)))
.collect();
if closed {
pl.close();
@@ -460,8 +460,8 @@ fn stitch(mut segs: Vec<Seg>) -> Option<(Vec<Seg>, bool)> {
#[cfg(test)]
mod join_tests {
use super::*;
use acadrust::entities::{Line as LineEnt, LwVertex};
use acadrust::Handle;
use codec::entities::{Line as LineEnt, LwVertex};
use codec::Handle;
fn line(x0: f64, y0: f64, x1: f64, y1: f64, thickness: f64) -> EntityType {
let mut l = LineEnt::new();
@@ -472,7 +472,7 @@ mod join_tests {
}
fn lw_2pts(p0: (f64, f64), p1: (f64, f64), thickness: f64) -> EntityType {
let mut pl = acadrust::entities::LwPolyline::new();
let mut pl = codec::entities::LwPolyline::new();
pl.vertices = vec![
LwVertex::new(Vector2::new(p0.0, p0.1)),
LwVertex::new(Vector2::new(p1.0, p1.1)),
@@ -554,7 +554,7 @@ mod join_tests {
fn source_join_elevates_splines_and_clears_fit_representation() {
let source_handle = Handle::new(21);
let candidate_handle = Handle::new(22);
let mut source = acadrust::entities::Spline::new();
let mut source = codec::entities::Spline::new();
source.common.handle = source_handle;
source.degree = 1;
source.control_points = vec![
@@ -565,7 +565,7 @@ mod join_tests {
source.fit_points = vec![Vector3::new(0.0, 0.0, 0.0)];
source.dwg_flags1 = 1;
source.dxf_flags = 32 | 1024;
let mut candidate = acadrust::entities::Spline::new();
let mut candidate = codec::entities::Spline::new();
candidate.degree = 3;
candidate.control_points = vec![
Vector3::new(1.0, 0.0, 0.0),
@@ -595,7 +595,7 @@ mod join_tests {
#[test]
fn arc_source_accepts_the_typed_close_option() {
let handle = Handle::new(31);
let mut arc = acadrust::entities::Arc::new();
let mut arc = codec::entities::Arc::new();
arc.common.handle = handle;
arc.center = Vector3::new(2.0, 3.0, 4.0);
arc.radius = 5.0;
+12 -12
View File
@@ -8,12 +8,12 @@
// The entity is modified at whichever end is closest to the pick point.
use crate::modules::draw::modify::spline_ops::{spline_cut, spline_to_nurbs};
use acadrust::entities::{
use codec::entities::{
Spline as SplineEnt,
};
use cadkernel::geom2d::Curve;
use acadrust::types::Vector3;
use acadrust::{EntityType, Handle};
use kernel::geom2d::Curve;
use codec::types::Vector3;
use codec::{EntityType, Handle};
use glam::{DVec3, Vec3};
use crate::t;
@@ -249,7 +249,7 @@ pub fn lengthen_entity(entity: &EntityType, pick_pt: Vec3, mode: &LenMode) -> Op
}
fn lengthen_entity_precise(entity: &EntityType, pick: DVec3, mode: &LenMode) -> Option<EntityType> {
use cadkernel::space::lengthen::{LengthChange, lengthen_line, lengthen_arc};
use kernel::space::lengthen::{LengthChange, lengthen_line, lengthen_arc};
let change = match mode {
LenMode::Delta(value) => LengthChange::Delta(*value),
LenMode::Total(value) => LengthChange::Total(*value),
@@ -279,7 +279,7 @@ fn lengthen_entity_precise(entity: &EntityType, pick: DVec3, mode: &LenMode) ->
}
EntityType::Ellipse(ellipse) => {
let curve = crate::entities::curve::ellipse_curve(ellipse)?;
let (start, end) = cadkernel::space::lengthen::lengthen_ellipse(&curve, pick.to_array(), change)?;
let (start, end) = kernel::space::lengthen::lengthen_ellipse(&curve, pick.to_array(), change)?;
let mut result = ellipse.clone();
result.start_parameter = start;
result.end_parameter = end;
@@ -288,7 +288,7 @@ fn lengthen_entity_precise(entity: &EntityType, pick: DVec3, mode: &LenMode) ->
EntityType::Spline(s) => lengthen_spline(s, pick.as_vec3(), mode),
EntityType::LwPolyline(polyline) => {
let curve = crate::entities::curve::lwpolyline_curve(polyline)?;
let vertices = cadkernel::space::lengthen::lengthen_polyline(&curve, pick.to_array(), change)?;
let vertices = kernel::space::lengthen::lengthen_polyline(&curve, pick.to_array(), change)?;
let mut result = polyline.clone();
result.vertices = vertices.into_iter().map(|vertex| {
let mut value = polyline.vertices[vertex.source].clone();
@@ -365,7 +365,7 @@ mod tests {
use super::*;
fn spatial_line() -> EntityType {
EntityType::Line(acadrust::entities::Line::from_points(
EntityType::Line(codec::entities::Line::from_points(
Vector3::new(0.0, 0.0, 0.0),
Vector3::new(0.0, 0.0, 4.0),
))
@@ -422,7 +422,7 @@ mod tests {
#[test]
fn circular_arc_accepts_total_angle_in_radians() {
let arc = acadrust::entities::Arc::from_center_radius_angles(
let arc = codec::entities::Arc::from_center_radius_angles(
Vector3::ZERO,
2.0,
0.0,
@@ -447,13 +447,13 @@ mod tests {
#[test]
fn polyline_lengthening_preserves_plane_and_interpolates_widths() {
let mut polyline = acadrust::entities::LwPolyline::new();
let mut polyline = codec::entities::LwPolyline::new();
polyline.normal = Vector3::new(0.0, 1.0, 0.0);
polyline.elevation = 5.0;
let mut start = acadrust::entities::LwVertex::from_coords(0.0, 0.0);
let mut start = codec::entities::LwVertex::from_coords(0.0, 0.0);
start.start_width = 2.0;
start.end_width = 4.0;
polyline.vertices = vec![start, acadrust::entities::LwVertex::from_coords(10.0, 0.0)];
polyline.vertices = vec![start, codec::entities::LwVertex::from_coords(10.0, 0.0)];
let entity = EntityType::LwPolyline(polyline.clone());
let pick = DVec3::from_array(crate::entities::curve::lwpolyline_curve(&polyline)
.unwrap().point_at(1.0));
+1 -1
View File
@@ -8,7 +8,7 @@
// No → keep the original, add a mirrored copy
// Yes → flip the original in place (no copy kept)
use acadrust::Handle;
use codec::Handle;
use glam::DVec3;
use crate::t;
+18 -18
View File
@@ -1,8 +1,8 @@
use acadrust::entities::MLine;
use acadrust::objects::MLineStyle;
use acadrust::{EntityType, Handle};
use cadkernel::geom2d::{Curve, Line};
use cadkernel::space::{PlanarCurve, Plane, Vec3 as KernelVec3};
use codec::entities::MLine;
use codec::objects::MLineStyle;
use codec::{EntityType, Handle};
use kernel::geom2d::{Curve, Line};
use kernel::space::{PlanarCurve, Plane, Vec3 as KernelVec3};
use glam::DVec3;
use rustc_hash::FxHashMap as HashMap;
@@ -95,7 +95,7 @@ impl MlineEditCommand {
}
let insert = segment + 1;
let mut vertex = mline.vertices[segment].clone();
vertex.position = acadrust::types::Vector3::new(
vertex.position = codec::types::Vector3::new(
projected.x,
projected.y,
projected.z,
@@ -710,7 +710,7 @@ fn snapshot_segment_data(
mline: &MLine,
segment: usize,
normal: DVec3,
) -> Option<Vec<(acadrust::entities::MLineSegment, f64)>> {
) -> Option<Vec<(codec::entities::MLineSegment, f64)>> {
(0..mline.style_element_count)
.map(|element| {
let endpoints = element_segment(mline, segment, element)?;
@@ -726,7 +726,7 @@ fn restore_shifted_segment_data(
mline: &mut MLine,
segment: usize,
normal: DVec3,
source: Vec<(acadrust::entities::MLineSegment, f64)>,
source: Vec<(codec::entities::MLineSegment, f64)>,
moved_start: bool,
) {
for (element, (source, source_length)) in source.into_iter().enumerate() {
@@ -1057,7 +1057,7 @@ fn move_closest_end(
};
let peripheral = peripheral_segment
.and_then(|segment| snapshot_segment_data(mline, segment, normal).map(|data| (segment, data)));
mline.vertices[index].position = acadrust::types::Vector3::new(
mline.vertices[index].position = codec::types::Vector3::new(
intersection.x,
intersection.y,
intersection.z,
@@ -1096,11 +1096,11 @@ fn segment_intersection(
second: (DVec3, DVec3),
normal: DVec3,
) -> Option<(DVec3, f64, f64, f64)> {
let axis = (cadkernel::space::Vec3::from(first.1.to_array())
- cadkernel::space::Vec3::from(first.0.to_array()))
let axis = (kernel::space::Vec3::from(first.1.to_array())
- kernel::space::Vec3::from(first.0.to_array()))
.normalize()?
.to_array();
let plane = cadkernel::space::Plane::orthonormal(
let plane = kernel::space::Plane::orthonormal(
first.0.to_array(),
axis,
normal.to_array(),
@@ -1111,15 +1111,15 @@ fn segment_intersection(
second.0.to_array(),
second.1.to_array(),
];
let tolerance = cadkernel::space::coplanarity_tolerance(&points);
let tolerance = kernel::space::coplanarity_tolerance(&points);
if !plane.contains(points[2], tolerance) || !plane.contains(points[3], tolerance) {
return None;
}
let p = cadkernel::geom2d::Vec2::from(plane.project(points[0])?);
let q = cadkernel::geom2d::Vec2::from(plane.project(points[2])?);
let r = cadkernel::geom2d::Vec2::from(plane.project(points[1])?) - p;
let s = cadkernel::geom2d::Vec2::from(plane.project(points[3])?) - q;
let (t, u) = cadkernel::geom2d::intersect::line_line(
let p = kernel::geom2d::Vec2::from(plane.project(points[0])?);
let q = kernel::geom2d::Vec2::from(plane.project(points[2])?);
let r = kernel::geom2d::Vec2::from(plane.project(points[1])?) - p;
let s = kernel::geom2d::Vec2::from(plane.project(points[3])?) - q;
let (t, u) = kernel::geom2d::intersect::line_line(
p.to_array(),
r.to_array(),
q.to_array(),
+7 -7
View File
@@ -1,6 +1,6 @@
use acadrust::{CadDocument, EntityType, Handle};
use codec::{CadDocument, EntityType, Handle};
use glam::DVec3;
use acadrust::nested_copy::{NestedCopyMode, NestedCopySymbolNames};
use codec::nested_copy::{NestedCopyMode, NestedCopySymbolNames};
use std::collections::{HashMap, HashSet};
use crate::command::{CadCommand, CmdOption, CmdResult, EntityTransform};
use crate::entities::traits::EntityTypeOps;
@@ -101,13 +101,13 @@ impl CadCommand for NcopyCommand {
let nearest = entities.iter().enumerate().filter_map(|(index, entity)| {
let distance = if let Some(curve) = crate::entities::curve::entity_curve(entity) {
let projected = curve.plane.project([point.x, point.y, point.z])?;
cadkernel::geom2d::closest_point(&curve.curve, projected).distance
kernel::geom2d::closest_point(&curve.curve, projected).distance
.hypot(curve.plane.distance_to(point.to_array())?)
} else {
let path = self.hit_paths.get(&handle)?.get(index)?;
let point = cadkernel::space::Vec3::from(point.to_array());
let point = kernel::space::Vec3::from(point.to_array());
if path.len() == 1 {
point.distance(cadkernel::space::Vec3::from(path[0]))
point.distance(kernel::space::Vec3::from(path[0]))
} else {
path.windows(2).filter(|segment| segment.iter().flatten().all(|v| v.is_finite()))
.map(|segment| point.distance_to_segment(segment[0].into(), segment[1].into()))
@@ -197,8 +197,8 @@ impl CadCommand for NcopyCommand {
#[cfg(test)]
mod tests {
use super::*;
use acadrust::entities::Line;
use acadrust::types::Vector3;
use codec::entities::Line;
use codec::types::Vector3;
#[test]
fn placement_moves_only_geometry_and_keeps_source_style() {
+11 -11
View File
@@ -12,16 +12,16 @@
use crate::entities::curve::{entity_curve, lwpolyline_world_xy};
use crate::modules::draw::modify::spline_ops::spline_sample_xy;
use acadrust::entities::LwVertex;
use acadrust::entities::{
use codec::entities::LwVertex;
use codec::entities::{
Arc as ArcEnt, Circle as CircleEnt, Ellipse as EllipseEnt, Line as LineEnt, LwPolyline,
Spline as SplineEnt, XLine as XLineEnt,
};
use acadrust::{EntityType, Handle};
use codec::{EntityType, Handle};
// Polyline offsetting, and the angle normalisation that goes with it, come
// from the kernel; only the entity conversion stays here.
use cadkernel::geom2d::nurbs::clamped_uniform_knots;
use cadkernel::geom2d::{
use kernel::geom2d::nurbs::clamped_uniform_knots;
use kernel::geom2d::{
offset_polyline, Polyline as KernelPolyline, PolylineVertex as KernelVertex,
};
use glam::{DVec3, Vec3};
@@ -269,7 +269,7 @@ fn offset_spline(spl: &SplineEnt, dist: f64, side_pt: Vec3) -> Option<EntityType
})?;
// Offset each sample point along the local normal.
let offset_pts: Vec<acadrust::types::Vector3> = pts
let offset_pts: Vec<codec::types::Vector3> = pts
.iter()
.enumerate()
.map(|(i, p)| {
@@ -290,14 +290,14 @@ fn offset_spline(spl: &SplineEnt, dist: f64, side_pt: Vec3) -> Option<EntityType
let nx = -dy / len; // left perpendicular
let ny = dx / len;
let z = spl.control_points.first().map(|v| v.z).unwrap_or(0.0);
acadrust::types::Vector3::new(p[0] + sign * nx * dist, p[1] + sign * ny * dist, z)
codec::types::Vector3::new(p[0] + sign * nx * dist, p[1] + sign * ny * dist, z)
})
.collect();
let _ = ts_knot;
// Build a new spline from the offset control points (treat sample pts as fit pts → ctrl pts).
let degree = spl.degree.max(1) as usize;
let new_ctrl: Vec<acadrust::types::Vector3> = offset_pts;
let new_ctrl: Vec<codec::types::Vector3> = offset_pts;
let n_ctrl = new_ctrl.len();
let mut new_spl = spl.clone();
new_spl.common.handle = Handle::NULL;
@@ -364,7 +364,7 @@ fn perp_distance(entity: &EntityType, pt: Vec3) -> f64 {
// ── Wire preview points ─────────────────────────────────────────────────────
/// Preview density, matching the figure the drawn wires use.
const PREVIEW_SEGMENTS_PER_RADIAN: f64 = cadkernel::geom2d::DEFAULT_SEGMENTS_PER_RADIAN;
const PREVIEW_SEGMENTS_PER_RADIAN: f64 = kernel::geom2d::DEFAULT_SEGMENTS_PER_RADIAN;
/// Preview points for an entity, from its own curve.
///
@@ -989,7 +989,7 @@ inventory::submit!(crate::command::CommandRegistration { names: &["OFFSET"] });
#[cfg(test)]
mod offset_tests {
use super::*;
use acadrust::types::Vector2;
use codec::types::Vector2;
fn rect(corners: &[[f64; 2]]) -> LwPolyline {
LwPolyline {
@@ -1049,7 +1049,7 @@ mod option_tests {
use super::*;
fn line(x1: f64, y1: f64, x2: f64, y2: f64, handle: u64) -> EntityType {
let mut line = acadrust::entities::Line::from_coords(x1, y1, 0.0, x2, y2, 0.0);
let mut line = codec::entities::Line::from_coords(x1, y1, 0.0, x2, y2, 0.0);
line.common.handle = Handle::new(handle);
EntityType::Line(line)
}
+5 -5
View File
@@ -1,4 +1,4 @@
use acadrust::{EntityType, Handle};
use codec::{EntityType, Handle};
use glam::DVec3;
use crate::command::{CadCommand, CmdOption, CmdResult};
@@ -104,7 +104,7 @@ impl CadCommand for OverkillCommand {
pub fn normalized(entity:&EntityType, ignore:u16) -> EntityType {
let mut e=entity.clone();
let common=e.common_mut();
let default=acadrust::entities::EntityCommon::new();
let default=codec::entities::EntityCommon::new();
common.handle=Handle::NULL;
if ignore&1!=0 {common.color=default.color;common.color_name=None;common.color_book_handle=None;}
if ignore&2!=0 {common.layer.clear();}
@@ -125,7 +125,7 @@ pub fn optimize(entity:&mut EntityType, tolerance:f64) {
// Width changes and bulged spans carry geometry that must be retained.
if p.vertices.iter().any(|v|v.bulge!=0.0||v.start_width!=0.0||v.end_width!=0.0) {return;}
let points:Vec<_>=p.vertices.iter().map(|v|[v.location.x,v.location.y,p.elevation]).collect();
let indices=cadkernel::space::simplify_linear_chain(&points,tolerance);
let indices=kernel::space::simplify_linear_chain(&points,tolerance);
if indices.len()>=if p.is_closed{3}else{2} {
p.vertices=indices.into_iter().map(|i|p.vertices[i].clone()).collect();
}
@@ -135,8 +135,8 @@ pub fn optimize(entity:&mut EntityType, tolerance:f64) {
#[cfg(test)]
mod tests {
use super::*;
use acadrust::entities::{Line, LwPolyline};
use acadrust::types::{Color, Vector2};
use codec::entities::{Line, LwPolyline};
use codec::types::{Color, Vector2};
#[test]
fn normalization_ignores_only_the_requested_properties() {
+46 -46
View File
@@ -1,10 +1,10 @@
// PEDIT edits polylines and polygon meshes.
use acadrust::entities::LwVertex;
use acadrust::types::{Vector2, Vector3};
use acadrust::{EntityType, Handle};
use cadkernel::geom2d::nurbs::clamped_uniform_knots;
use cadkernel::geom2d::NurbsCurve;
use codec::entities::LwVertex;
use codec::types::{Vector2, Vector3};
use codec::{EntityType, Handle};
use kernel::geom2d::nurbs::clamped_uniform_knots;
use kernel::geom2d::NurbsCurve;
use glam::DVec3;
use rustc_hash::FxHashMap as HashMap;
@@ -12,7 +12,7 @@ use crate::command::{CadCommand, CmdResult};
use crate::t;
const TAU: f64 = std::f64::consts::TAU;
pub use cadkernel::space::endpoint_join::JoinType;
pub use kernel::space::endpoint_join::JoinType;
static JOIN_TYPE: std::sync::Mutex<JoinType> = std::sync::Mutex::new(JoinType::Extend);
static JOIN_FUZZ: std::sync::Mutex<f64> = std::sync::Mutex::new(0.0);
@@ -70,7 +70,7 @@ pub struct PeditCommand {
vertex_tangents: HashMap<usize, f64>,
pending_tangents: Option<HashMap<usize, f64>>,
tangent_history: Vec<HashMap<usize, f64>>,
mesh_smooth_type: acadrust::entities::polygon_mesh::SurfaceSmoothType,
mesh_smooth_type: codec::entities::polygon_mesh::SurfaceSmoothType,
mesh_smooth_density: (i16, i16),
mesh_vertex_default: isize,
pending_mesh_closed: Option<(bool, bool)>,
@@ -84,7 +84,7 @@ impl PeditCommand {
surface_u_density: i16,
surface_v_density: i16,
) -> Self {
use acadrust::entities::polygon_mesh::SurfaceSmoothType;
use codec::entities::polygon_mesh::SurfaceSmoothType;
let mesh_smooth_type = match surface_type {
5 => SurfaceSmoothType::Quadratic,
@@ -622,7 +622,7 @@ impl CadCommand for PeditCommand {
handle,
op: PeditOp::SetMeshSmooth {
smooth:
acadrust::entities::polygon_mesh::SurfaceSmoothType::NoSmooth,
codec::entities::polygon_mesh::SurfaceSmoothType::NoSmooth,
m_density: self.mesh_smooth_density.0,
n_density: self.mesh_smooth_density.1,
},
@@ -741,7 +741,7 @@ impl CadCommand for PeditCommand {
_ => return CmdResult::NeedPoint,
};
let Some(local) = crate::entities::curve::ocs_plane(normal, elevation).project([point.x, point.y, point.z]) else { return CmdResult::NeedPoint; };
let delta = cadkernel::geom2d::Vec2::new(local[0] - location[0], local[1] - location[1]);
let delta = kernel::geom2d::Vec2::new(local[0] - location[0], local[1] - location[1]);
if !delta.length_squared().is_finite() || delta.length_squared() <= 1e-24 { return CmdResult::NeedPoint; }
self.vertex_tangents.insert(index, delta.angle().rem_euclid(TAU));
self.mode = Mode::PolyVertex(index);
@@ -838,7 +838,7 @@ pub enum PeditOp {
SetMeshClosedM(bool),
SetMeshClosedN(bool),
SetMeshSmooth {
smooth: acadrust::entities::polygon_mesh::SurfaceSmoothType,
smooth: codec::entities::polygon_mesh::SurfaceSmoothType,
m_density: i16,
n_density: i16,
},
@@ -878,9 +878,9 @@ pub fn edit_vertex_range(entity: &EntityType, first: usize, last: usize, split:
}
match entity {
EntityType::LwPolyline(polyline) => edit!(polyline, polyline.is_closed, LwPolyline,
|value: &mut acadrust::entities::LwPolyline| { value.is_closed = false; }),
|value: &mut codec::entities::LwPolyline| { value.is_closed = false; }),
EntityType::Polyline2D(polyline) => edit!(polyline, polyline.flags.is_closed(), Polyline2D,
|value: &mut acadrust::entities::Polyline2D| { value.flags.set_closed(false); }),
|value: &mut codec::entities::Polyline2D| { value.flags.set_closed(false); }),
_ => None,
}
}
@@ -929,7 +929,7 @@ pub fn apply_pedit(entity: &mut EntityType, op: &PeditOp) -> bool {
if *index >= polyline.vertices.len() { return false; }
let location = Vector3::new(local[0], local[1], polyline.elevation);
if *insert {
polyline.vertices.insert(index + 1, acadrust::entities::polyline::Vertex2D::new(location));
polyline.vertices.insert(index + 1, codec::entities::polyline::Vertex2D::new(location));
} else { polyline.vertices[*index].location = location; }
true
}
@@ -948,10 +948,10 @@ pub fn apply_pedit(entity: &mut EntityType, op: &PeditOp) -> bool {
true
}
EntityType::Polyline2D(polyline) => {
let flag = acadrust::entities::polyline::PolylineFlags::LINETYPE_CONTINUOUS;
let flag = codec::entities::polyline::PolylineFlags::LINETYPE_CONTINUOUS;
let bits = polyline.flags.bits();
if (bits & flag.bits() != 0) == *enabled { return false; }
polyline.flags = acadrust::entities::polyline::PolylineFlags::from_bits(
polyline.flags = codec::entities::polyline::PolylineFlags::from_bits(
if *enabled { bits | flag.bits() } else { bits & !flag.bits() }
);
true
@@ -1014,7 +1014,7 @@ pub fn apply_pedit(entity: &mut EntityType, op: &PeditOp) -> bool {
return false;
}
mesh.flags.set(
acadrust::entities::polygon_mesh::PolygonMeshFlags::CLOSED_M,
codec::entities::polygon_mesh::PolygonMeshFlags::CLOSED_M,
*closed,
);
true
@@ -1027,7 +1027,7 @@ pub fn apply_pedit(entity: &mut EntityType, op: &PeditOp) -> bool {
return false;
}
mesh.flags.set(
acadrust::entities::polygon_mesh::PolygonMeshFlags::CLOSED_N,
codec::entities::polygon_mesh::PolygonMeshFlags::CLOSED_N,
*closed,
);
true
@@ -1043,7 +1043,7 @@ pub fn apply_pedit(entity: &mut EntityType, op: &PeditOp) -> bool {
let mut changed = mesh.smooth_type != *smooth;
mesh.smooth_type = *smooth;
if mesh.smooth_type
!= acadrust::entities::polygon_mesh::SurfaceSmoothType::NoSmooth
!= codec::entities::polygon_mesh::SurfaceSmoothType::NoSmooth
{
let m_density = (*m_density).clamp(2, 200);
let n_density = (*n_density).clamp(2, 200);
@@ -1074,7 +1074,7 @@ pub fn apply_pedit(entity: &mut EntityType, op: &PeditOp) -> bool {
{
return false;
}
vertex.location = acadrust::types::Vector3::new(point.x, point.y, point.z);
vertex.location = codec::types::Vector3::new(point.x, point.y, point.z);
true
}
_ => false,
@@ -1087,7 +1087,7 @@ pub fn apply_pedit(entity: &mut EntityType, op: &PeditOp) -> bool {
/// A Line or Arc as an equivalent 2-vertex LwPolyline (common carried over,
/// handle NULL for the replace flow). `None` for anything else.
pub fn convert_to_polyline(entity: &EntityType) -> Option<EntityType> {
let mut pl = acadrust::LwPolyline::new();
let mut pl = codec::LwPolyline::new();
match entity {
EntityType::Line(l) => {
let normal = DVec3::new(l.normal.x, l.normal.y, l.normal.z).try_normalize()?;
@@ -1098,7 +1098,7 @@ pub fn convert_to_polyline(entity: &EntityType) -> Option<EntityType> {
normal.x, normal.y, normal.z,
));
let plane = crate::entities::curve::ocs_plane(normal.clone(), elevation);
let tolerance = cadkernel::space::coplanarity_tolerance(&[start, end]);
let tolerance = kernel::space::coplanarity_tolerance(&[start, end]);
if !plane.contains(end, tolerance) {
return None;
}
@@ -1154,10 +1154,10 @@ mod convert_tests {
// onto the new entity instead of resetting to 0 (#916).
#[test]
fn convert_keeps_line_thickness() {
let mut l = acadrust::entities::Line::new();
l.start = acadrust::types::Vector3::new(2.0, 3.0, 4.0);
l.end = acadrust::types::Vector3::new(2.0, 5.0, 6.0);
l.normal = acadrust::types::Vector3::new(1.0, 0.0, 0.0);
let mut l = codec::entities::Line::new();
l.start = codec::types::Vector3::new(2.0, 3.0, 4.0);
l.end = codec::types::Vector3::new(2.0, 5.0, 6.0);
l.normal = codec::types::Vector3::new(1.0, 0.0, 0.0);
l.thickness = 3.5;
let Some(EntityType::LwPolyline(pl)) = convert_to_polyline(&EntityType::Line(l)) else {
panic!("line must convert");
@@ -1167,15 +1167,15 @@ mod convert_tests {
"converted polyline must keep source thickness, got {}",
pl.thickness
);
assert_eq!(pl.normal, acadrust::types::Vector3::new(1.0, 0.0, 0.0));
assert_eq!(pl.normal, codec::types::Vector3::new(1.0, 0.0, 0.0));
assert!((pl.elevation - 2.0).abs() < 1e-12);
}
#[test]
fn convert_keeps_arc_thickness() {
let mut a = acadrust::entities::Arc::new();
a.center = acadrust::types::Vector3::new(0.0, 0.0, 4.0);
a.normal = acadrust::types::Vector3::new(0.0, 1.0, 0.0);
let mut a = codec::entities::Arc::new();
a.center = codec::types::Vector3::new(0.0, 0.0, 4.0);
a.normal = codec::types::Vector3::new(0.0, 1.0, 0.0);
a.radius = 5.0;
a.start_angle = 0.0;
a.end_angle = std::f64::consts::FRAC_PI_2;
@@ -1188,7 +1188,7 @@ mod convert_tests {
"converted polyline must keep negative thickness, got {}",
pl.thickness
);
assert_eq!(pl.normal, acadrust::types::Vector3::new(0.0, 1.0, 0.0));
assert_eq!(pl.normal, codec::types::Vector3::new(0.0, 1.0, 0.0));
assert!((pl.elevation - 4.0).abs() < 1e-12);
}
}
@@ -1196,7 +1196,7 @@ mod convert_tests {
// ── Curve fitting ─────────────────────────────────────────────────────────
fn fit_entity(entity: &EntityType, overrides: &[(usize, f64)]) -> Option<EntityType> {
use acadrust::entities::{Polyline2D, PolylineFlags, Vertex2D, VertexFlags};
use codec::entities::{Polyline2D, PolylineFlags, Vertex2D, VertexFlags};
let mut polyline = match entity {
EntityType::Polyline2D(p) => p.clone(),
EntityType::LwPolyline(p) => {
@@ -1231,7 +1231,7 @@ fn fit_entity(entity: &EntityType, overrides: &[(usize, f64)]) -> Option<EntityT
}
let points: Vec<_> = originals.iter().map(|v| [v.location.x, v.location.y]).collect();
let directions: Vec<_> = originals.iter().map(|v| (v.flags.bits() & 2 != 0).then(|| [v.curve_tangent.cos(), v.curve_tangent.sin()])).collect();
let fitted = cadkernel::geom2d::fit_arc_chain(&points, polyline.flags.is_closed(), &directions)?;
let fitted = kernel::geom2d::fit_arc_chain(&points, polyline.flags.is_closed(), &directions)?;
let mut vertices = Vec::with_capacity(fitted.len());
for (i, fitted_vertex) in fitted.iter().enumerate() {
let original = &originals[fitted_vertex.source];
@@ -1251,7 +1251,7 @@ fn fit_entity(entity: &EntityType, overrides: &[(usize, f64)]) -> Option<EntityT
Some(EntityType::Polyline2D(polyline))
}
fn defining_vertices(polyline: &acadrust::entities::Polyline2D) -> Vec<acadrust::entities::Vertex2D> {
fn defining_vertices(polyline: &codec::entities::Polyline2D) -> Vec<codec::entities::Vertex2D> {
polyline.vertices.iter().enumerate().filter(|(_, vertex)| vertex.flags.bits() & 1 == 0).map(|(index, vertex)| {
let mut original = vertex.clone();
if let Some(knee) = polyline.vertices[index + 1..].iter().take_while(|next| next.flags.bits() & 1 != 0).last() {
@@ -1265,7 +1265,7 @@ fn decurve_entity(entity: &EntityType) -> Option<EntityType> {
let mut result = match entity {
EntityType::LwPolyline(p) => p.clone(),
EntityType::Polyline2D(p) => {
let mut result = acadrust::LwPolyline::new();
let mut result = codec::LwPolyline::new();
result.common = p.common.clone(); result.normal = p.normal;
result.elevation = p.elevation; result.thickness = p.thickness;
result.is_closed = p.flags.is_closed(); result.plinegen = p.flags.bits() & 128 != 0;
@@ -1299,7 +1299,7 @@ fn decurve_entity(entity: &EntityType) -> Option<EntityType> {
/// out, with the open case clamped by repeating end points and then the two
/// ends pinned back afterwards to undo the drift that leaves — all of which
/// a clamped knot vector expresses directly and exactly.
fn spline_smooth(p: &mut acadrust::LwPolyline) -> bool {
fn spline_smooth(p: &mut codec::LwPolyline) -> bool {
const DEGREE: usize = 3;
const PER_SPAN: usize = 8;
@@ -1350,10 +1350,10 @@ fn spline_smooth(p: &mut acadrust::LwPolyline) -> bool {
#[cfg(test)]
mod editing_tests {
use super::*;
use acadrust::entities::{Polyline2D, Vertex2D};
use codec::entities::{Polyline2D, Vertex2D};
fn lightweight(handle: u64, points: &[[f64; 2]]) -> EntityType {
let mut polyline = acadrust::LwPolyline::new();
let mut polyline = codec::LwPolyline::new();
polyline.common.handle = Handle::new(handle);
polyline.vertices = points
.iter()
@@ -1488,9 +1488,9 @@ mod editing_tests {
#[test]
fn multiple_decline_and_fuzz_join_keep_the_command_active() {
*JOIN_TYPE.lock().unwrap_or_else(|error| error.into_inner()) = JoinType::Extend;
let mut line = acadrust::entities::Line::new();
let mut line = codec::entities::Line::new();
line.common.handle = Handle::new(11);
let mut arc = acadrust::entities::Arc::new();
let mut arc = codec::entities::Arc::new();
arc.common.handle = Handle::new(12);
let mut declined = command(
vec![EntityType::Line(line), EntityType::Arc(arc)],
@@ -1663,14 +1663,14 @@ pub fn join_selection_extend(source: &EntityType, candidates: &[(Handle, &Entity
let b_ends = [true,false].into_iter().filter_map(|start| endpoint(candidate,start).map(|points|(start,points))).collect::<Vec<_>>();
let a_points = a_ends.iter().map(|(_,points)|*points).collect::<Vec<_>>();
let b_points = b_ends.iter().map(|(_,points)|*points).collect::<Vec<_>>();
if let Some((a_index,b_index,joint)) = cadkernel::space::endpoint_join::closest_line_end_join(&a_points,&b_points,fuzz,connector_distance.unwrap_or(0.0),kind) {
if let Some((a_index,b_index,joint)) = kernel::space::endpoint_join::closest_line_end_join(&a_points,&b_points,fuzz,connector_distance.unwrap_or(0.0),kind) {
let (a_start,a_points) = a_ends[a_index]; let (b_start,b_points) = b_ends[b_index];
let mut a = result.clone(); let mut b = (*candidate).clone();
let joined_pair = if let Some(point) = joint {
if move_endpoint(&mut a,a_start,point).is_none() || move_endpoint(&mut b,b_start,point).is_none() {continue;}
super::join::join_to_source(&a,&[(*handle,&b)])
} else {
let mut line = acadrust::entities::Line::new();
let mut line = codec::entities::Line::new();
line.start = Vector3::new(a_points[1][0],a_points[1][1],a_points[1][2]);
line.end = Vector3::new(b_points[1][0],b_points[1][1],b_points[1][2]);
match &a {
@@ -1710,7 +1710,7 @@ pub fn selection_connector_distance(entities: &[(Handle, EntityType)], fuzz: f64
};
if !z.is_finite() { return None; }
let plane = crate::entities::curve::ocs_plane(*normal, z);
let normal = cadkernel::space::Vec3::from(plane.normal()?);
let normal = kernel::space::Vec3::from(plane.normal()?);
let points = vertices.into_iter().map(|point| plane.point_at(point)).collect::<Vec<_>>();
curves.push((normal, points));
}
@@ -1719,10 +1719,10 @@ pub fn selection_connector_distance(entities: &[(Handle, EntityType)], fuzz: f64
return None;
}
let world = curves.iter().flat_map(|(_, points)| points.iter().copied()).collect::<Vec<_>>();
let tolerance = cadkernel::space::coplanarity_tolerance(&world);
let tolerance = kernel::space::coplanarity_tolerance(&world);
let shared = crate::entities::curve::ocs_plane(Vector3::new(normal.x, normal.y, normal.z),
cadkernel::space::Vec3::from(world[0]).dot(*normal));
kernel::space::Vec3::from(world[0]).dot(*normal));
if world.iter().any(|point| !shared.contains(*point, tolerance)) { return None; }
let points = world.into_iter().map(|point| shared.project(point)).collect::<Option<Vec<_>>>()?;
cadkernel::space::endpoint_join::planar_connector_distance(&points, fuzz)
kernel::space::endpoint_join::planar_connector_distance(&points, fuzz)
}
+3 -3
View File
@@ -13,7 +13,7 @@
// with an error message — full matrix inversion for those cases would
// require per-entity matrix transforms not yet in EntityTransform.
use acadrust::{EntityType, Handle};
use codec::{EntityType, Handle};
use glam::DVec3;
use crate::t;
@@ -53,9 +53,9 @@ pub struct RefEditSession {
// ── INSERT placement, as full affine transforms ───────────────────
/// Block-local → world (the INSERT's `get_transform`). Handles OCS,
/// rotation and non-uniform / mirrored scale in one matrix.
pub forward: acadrust::types::Transform,
pub forward: codec::types::Transform,
/// World → block-local, applied on SAVE to bring edits back.
pub inverse: acadrust::types::Transform,
pub inverse: codec::types::Transform,
/// Handle watermark captured before the temp copies were created: every
/// entity allocated at or above it during the session (drawn, pasted,
/// offset, …) belongs to the working set, so Save folds it into the block
+8 -8
View File
@@ -21,8 +21,8 @@
// `CmdResult::NeedPoint` and keeps prompting so nothing is corrupted.
use crate::t;
use acadrust::entities::Spline;
use acadrust::{EntityType, Handle};
use codec::entities::Spline;
use codec::{EntityType, Handle};
use glam::DVec3;
use crate::command::{CadCommand, CmdResult};
@@ -104,7 +104,7 @@ impl ReverseCommand {
/// starting at original vertex `(n-1-i-1).rem_euclid(n)`. The widths follow the
/// Widths are segment-relative. Reversing a segment swaps its start and end
/// widths in addition to moving them to the corresponding reversed segment.
fn reverse_lwpolyline(pl: &acadrust::LwPolyline) -> acadrust::LwPolyline {
fn reverse_lwpolyline(pl: &codec::LwPolyline) -> codec::LwPolyline {
let mut out = pl.clone();
let n = pl.vertices.len();
if n < 2 {
@@ -141,7 +141,7 @@ fn reverse_lwpolyline(pl: &acadrust::LwPolyline) -> acadrust::LwPolyline {
}
/// Reverse a heavy 2D polyline with the same segment semantics as LWPolyline.
fn reverse_polyline2d(pl: &acadrust::entities::Polyline2D) -> acadrust::entities::Polyline2D {
fn reverse_polyline2d(pl: &codec::entities::Polyline2D) -> codec::entities::Polyline2D {
let mut out = pl.clone();
let n = pl.vertices.len();
if n < 2 {
@@ -152,7 +152,7 @@ fn reverse_polyline2d(pl: &acadrust::entities::Polyline2D) -> acadrust::entities
out.start_width = pl.end_width;
out.end_width = pl.start_width;
for vertex in &mut out.vertices {
let tangent_flag = acadrust::entities::VertexFlags::CURVE_FIT_TANGENT.bits();
let tangent_flag = codec::entities::VertexFlags::CURVE_FIT_TANGENT.bits();
if vertex.flags.bits() & tangent_flag != 0 {
vertex.curve_tangent =
(vertex.curve_tangent + std::f64::consts::PI).rem_euclid(std::f64::consts::TAU);
@@ -190,8 +190,8 @@ pub(super) fn reverse_spline(sp: &Spline) -> Spline {
out.knots = curve.reversed().knots().to_vec();
}
out.begin_tangent =
acadrust::types::Vector3::new(-sp.end_tangent.x, -sp.end_tangent.y, -sp.end_tangent.z);
out.end_tangent = acadrust::types::Vector3::new(
codec::types::Vector3::new(-sp.end_tangent.x, -sp.end_tangent.y, -sp.end_tangent.z);
out.end_tangent = codec::types::Vector3::new(
-sp.begin_tangent.x,
-sp.begin_tangent.y,
-sp.begin_tangent.z,
@@ -252,7 +252,7 @@ inventory::submit!(crate::command::CommandRegistration {
#[cfg(test)]
mod tests {
use super::*;
use acadrust::types::Vector3;
use codec::types::Vector3;
#[test]
fn spline_reversal_preserves_knot_spacing_weights_and_tangents() {
+2 -2
View File
@@ -9,8 +9,8 @@
// two points. The new absolute angle is then typed or picked from the center;
// the applied rotation is new-angle - reference-angle.
use acadrust::Handle;
use cadkernel::geom2d::{self, Curve as KernelCurve};
use codec::Handle;
use kernel::geom2d::{self, Curve as KernelCurve};
use glam::DVec3;
use crate::t;
+1 -1
View File
@@ -12,7 +12,7 @@
// measured between two points; the new length may be typed or measured
// from the scale base.
use acadrust::Handle;
use codec::Handle;
use glam::DVec3;
use crate::t;
+10 -10
View File
@@ -1,4 +1,4 @@
use acadrust::Handle;
use codec::Handle;
use glam::DVec3;
use crate::command::{CadCommand, CmdOption, CmdResult};
@@ -6,10 +6,10 @@ static MODE: std::sync::atomic::AtomicU8 = std::sync::atomic::AtomicU8::new(255)
pub fn mode() -> u8 { MODE.load(std::sync::atomic::Ordering::Relaxed) }
pub fn set_mode(value: u8) { MODE.store(value, std::sync::atomic::Ordering::Relaxed); }
pub fn apply_mask(common: &mut acadrust::entities::EntityCommon, mask: u8, change_byblock: bool) -> bool {
pub fn apply_mask(common: &mut codec::entities::EntityCommon, mask: u8, change_byblock: bool) -> bool {
let before = common.clone();
if mask & 1 != 0 && (change_byblock || common.color != acadrust::types::Color::ByBlock) {
common.color = acadrust::types::Color::ByLayer;
if mask & 1 != 0 && (change_byblock || common.color != codec::types::Color::ByBlock) {
common.color = codec::types::Color::ByLayer;
common.color_name = None;
common.color_book_handle = None;
}
@@ -17,8 +17,8 @@ pub fn apply_mask(common: &mut acadrust::entities::EntityCommon, mask: u8, chang
common.linetype = "ByLayer".to_string();
common.linetype_handle = None;
}
if mask & 4 != 0 && (change_byblock || common.line_weight != acadrust::types::LineWeight::ByBlock) {
common.line_weight = acadrust::types::LineWeight::ByLayer;
if mask & 4 != 0 && (change_byblock || common.line_weight != codec::types::LineWeight::ByBlock) {
common.line_weight = codec::types::LineWeight::ByLayer;
}
if mask & 8 != 0 && (change_byblock || common.material_flags != 1) {
common.material_flags = 0;
@@ -28,8 +28,8 @@ pub fn apply_mask(common: &mut acadrust::entities::EntityCommon, mask: u8, chang
common.plotstyle_flags = 0;
common.plotstyle_handle = None;
}
if mask & 128 != 0 && (change_byblock || common.transparency != acadrust::types::Transparency::BY_BLOCK) {
common.transparency = acadrust::types::Transparency::BY_LAYER;
if mask & 128 != 0 && (change_byblock || common.transparency != codec::types::Transparency::BY_BLOCK) {
common.transparency = codec::types::Transparency::BY_LAYER;
}
*common != before
}
@@ -155,8 +155,8 @@ inventory::submit!(crate::command::CommandRegistration { names: &["SETBYLAYERMOD
#[cfg(test)]
mod tests {
use super::*;
use acadrust::entities::EntityCommon;
use acadrust::types::{Color, Transparency};
use codec::entities::EntityCommon;
use codec::types::{Color, Transparency};
#[test]
fn mask_changes_only_requested_properties() {
+8 -8
View File
@@ -1,6 +1,6 @@
// Shared B-spline utilities for modify commands (TRIM, BREAK, OFFSET, LENGTHEN).
//
// The evaluation lives in cadkernel; this file converts a drawing's SPLINE
// The evaluation lives in opencadkernel; this file converts a drawing's SPLINE
// entity to and from it and keeps the knot-domain call shapes the commands
// were written against.
//
@@ -9,11 +9,11 @@
// piece of a NURBS circle into a piece of a parabola — a change that looks
// fine until it is measured.
use acadrust::entities::Spline;
use acadrust::types::Vector3;
use acadrust::Handle;
use cadkernel::geom2d::{NurbsCurve, Parameterization};
use cadkernel::space::Plane;
use codec::entities::Spline;
use codec::types::Vector3;
use codec::Handle;
use kernel::geom2d::{NurbsCurve, Parameterization};
use kernel::space::Plane;
/// The drawing plane a spline sits on.
///
@@ -30,7 +30,7 @@ fn elevation(spl: &Spline) -> f64 {
// ── Conversion ─────────────────────────────────────────────────────────────
/// Convert an acadrust `Spline` to a kernel `NurbsCurve`, weights included.
/// Convert an opencadcodec `Spline` to a kernel `NurbsCurve`, weights included.
///
/// A SPLINE is stored one of two ways and both turn up: a control polygon the
/// curve approaches, or fit points it must pass through with optional end
@@ -114,7 +114,7 @@ fn spline_to_nurbs_with(
)
}
/// Rebuild an acadrust `Spline` from a kernel curve.
/// Rebuild an opencadcodec `Spline` from a kernel curve.
///
/// The `template` supplies entity common data and the Z elevation; splines
/// here are planar, so the kernel's 2D points are lifted back onto it.
+34 -34
View File
@@ -9,8 +9,8 @@
//
// Control-point dragging is already supported via the grip editing system.
use acadrust::types::Vector3;
use acadrust::EntityType;
use codec::types::Vector3;
use codec::EntityType;
use glam::DVec3;
@@ -53,16 +53,16 @@ enum Step {
pub struct SplineditCommand {
step: Step,
handle: acadrust::Handle,
spline: Option<acadrust::entities::Spline>,
pending: Option<acadrust::entities::Spline>,
history: Vec<(acadrust::Handle, acadrust::entities::Spline)>,
handle: codec::Handle,
spline: Option<codec::entities::Spline>,
pending: Option<codec::entities::Spline>,
history: Vec<(codec::Handle, codec::entities::Spline)>,
pick_context: Option<crate::command::PointPickContext>,
join_candidates: Vec<crate::command::SelectionEntity>,
delete_source: bool,
}
fn clear_fit_method(spline: &mut acadrust::entities::Spline) {
fn clear_fit_method(spline: &mut codec::entities::Spline) {
spline.fit_points.clear();
spline.begin_tangent = Vector3::ZERO;
spline.end_tangent = Vector3::ZERO;
@@ -80,7 +80,7 @@ fn weights_are_rational(weights: &[f64]) -> bool {
impl SplineditCommand {
pub fn new() -> Self {
Self { step: Step::SelectSpline, handle: acadrust::Handle::NULL, spline: None, pending: None, history: Vec::new(), pick_context: None, join_candidates: Vec::new(), delete_source: true }
Self { step: Step::SelectSpline, handle: codec::Handle::NULL, spline: None, pending: None, history: Vec::new(), pick_context: None, join_candidates: Vec::new(), delete_source: true }
}
pub fn with_delete_source(mut self, delete: bool) -> Self { self.delete_source = delete; self }
@@ -97,19 +97,19 @@ impl SplineditCommand {
if points.len() < 2 { return None; }
let entity = if let Some(planar) = crate::entities::curve::entity_curve(&EntityType::Spline(source.clone())) {
let normal = planar.plane.normal()?;
let elevation = cadkernel::space::Vec3::from(points[0]).dot(cadkernel::space::Vec3::from(normal));
let elevation = kernel::space::Vec3::from(points[0]).dot(kernel::space::Vec3::from(normal));
let normal = Vector3::new(normal[0], normal[1], normal[2]);
let plane = crate::entities::curve::ocs_plane(normal.clone(), elevation);
let mut polyline = acadrust::LwPolyline::new();
let mut polyline = codec::LwPolyline::new();
polyline.common = source.common.clone();
polyline.elevation = elevation; polyline.normal = normal; polyline.is_closed = self.closed();
polyline.vertices = points.iter().map(|point| {
let uv = plane.project(*point)?;
Some(acadrust::entities::LwVertex::new(acadrust::types::Vector2::new(uv[0], uv[1])))
Some(codec::entities::LwVertex::new(codec::types::Vector2::new(uv[0], uv[1])))
}).collect::<Option<Vec<_>>>()?;
EntityType::LwPolyline(polyline)
} else {
let mut polyline = acadrust::entities::Polyline3D::from_points(points.iter().map(|p| Vector3::new(p[0],p[1],p[2])).collect());
let mut polyline = codec::entities::Polyline3D::from_points(points.iter().map(|p| Vector3::new(p[0],p[1],p[2])).collect());
polyline.common = source.common.clone(); polyline.flags.closed = self.closed();
EntityType::Polyline3D(polyline)
};
@@ -117,7 +117,7 @@ impl SplineditCommand {
});
let Some(mut entity) = result else { return CmdResult::ReportError(crate::t!("Spline cannot be converted within the requested precision.").into_owned()); };
if self.delete_source { CmdResult::ReplaceMany(vec![(self.handle,vec![entity])], Vec::new()) }
else { entity.common_mut().handle = acadrust::Handle::NULL; CmdResult::ReplaceMany(Vec::new(), vec![entity]) }
else { entity.common_mut().handle = codec::Handle::NULL; CmdResult::ReplaceMany(Vec::new(), vec![entity]) }
}
fn picked_vertex(&self, point: DVec3) -> Option<usize> { self.picked_from_points(point, &self.spline.as_ref()?.control_points) }
@@ -132,7 +132,7 @@ impl SplineditCommand {
self.rebuild_fit(result)
}
fn rebuild_fit(&mut self, mut result: acadrust::entities::Spline) -> CmdResult {
fn rebuild_fit(&mut self, mut result: codec::entities::Spline) -> CmdResult {
let Some(source) = self.spline.as_ref() else { return CmdResult::NeedPoint; };
if source.fit_tolerance != 0.0 || source.weights.windows(2).any(|w| w[0] != w[1]) {
return CmdResult::ReportError(crate::t!("Editing weighted or tolerance-fitted interpolation data is not supported.").into_owned());
@@ -201,7 +201,7 @@ impl SplineditCommand {
self.spline.as_ref().is_some_and(|spline| spline.flags.closed || spline.flags.periodic)
}
fn replace(&mut self, spline: acadrust::entities::Spline) -> CmdResult {
fn replace(&mut self, spline: codec::entities::Spline) -> CmdResult {
self.pending = Some(spline.clone());
CmdResult::ReplaceManyContinue(vec![(self.handle, vec![EntityType::Spline(spline)])])
}
@@ -224,7 +224,7 @@ impl SplineditCommand {
CmdResult::ReplaceManyContinue(replacements)
}
fn refined(&self, point: Option<DVec3>, degree: Option<usize>) -> Option<acadrust::entities::Spline> {
fn refined(&self, point: Option<DVec3>, degree: Option<usize>) -> Option<codec::entities::Spline> {
let source = self.spline.as_ref()?;
if let Some(degree) = degree {
let current = usize::try_from(source.degree).ok()?;
@@ -244,10 +244,10 @@ impl SplineditCommand {
return Some(result);
}
let planar = crate::entities::curve::entity_curve(&EntityType::Spline(source.clone()))?;
let cadkernel::geom2d::Curve::Nurbs(mut curve) = planar.curve else { return None; };
let kernel::geom2d::Curve::Nurbs(mut curve) = planar.curve else { return None; };
if let Some(point) = point {
let projected = planar.plane.project([point.x, point.y, point.z])?;
let nearest = cadkernel::geom2d::closest_point(&cadkernel::geom2d::Curve::Nurbs(curve.clone()), projected);
let nearest = kernel::geom2d::closest_point(&kernel::geom2d::Curve::Nurbs(curve.clone()), projected);
let (start, end) = curve.domain();
let parameter = start + nearest.t * (end - start);
if parameter <= start || parameter >= end { return None; }
@@ -321,7 +321,7 @@ impl CadCommand for SplineditCommand {
fn inject_selection_entities(&mut self, entities: Vec<crate::command::SelectionEntity>) {
if matches!(self.step, Step::Join) { self.join_candidates = entities; }
}
fn on_selection_complete(&mut self, handles: Vec<acadrust::Handle>) -> CmdResult {
fn on_selection_complete(&mut self, handles: Vec<codec::Handle>) -> CmdResult {
self.join_candidates.retain(|item| handles.contains(&item.handle) && item.handle != self.handle);
CmdResult::NeedPoint
}
@@ -330,13 +330,13 @@ impl CadCommand for SplineditCommand {
fn inject_picked_entity(&mut self, entity: EntityType) {
self.spline = match entity { EntityType::Spline(spline) => Some(spline), _ => None };
}
fn on_entity_pick(&mut self, handle: acadrust::Handle, _pt: DVec3) -> CmdResult {
fn on_entity_pick(&mut self, handle: codec::Handle, _pt: DVec3) -> CmdResult {
if handle.is_null() || self.spline.is_none() { return CmdResult::NeedPoint; }
self.handle = handle;
self.step = Step::Options;
CmdResult::NeedPoint
}
fn on_entity_replaced(&mut self, old: acadrust::Handle, new: &[acadrust::Handle]) {
fn on_entity_replaced(&mut self, old: codec::Handle, new: &[codec::Handle]) {
if old == self.handle {
if let (Some(&handle), Some(replacement)) = (new.first(), self.pending.take()) {
if let Some(previous) = self.spline.replace(replacement) { self.history.push((old, previous)); }
@@ -514,7 +514,7 @@ impl CadCommand for SplineditCommand {
let controls = source.control_points.iter().map(|point| [point.x, point.y, point.z]).collect();
let weights = if source.weights.is_empty() { vec![1.0; source.control_points.len()] }
else { source.weights.clone() };
let curve = cadkernel::space::NurbsCurve3::new_strict(source.degree as usize, controls,
let curve = kernel::space::NurbsCurve3::new_strict(source.degree as usize, controls,
source.knots.clone(), weights).map(|curve| curve.with_periodicity(source.flags.periodic || source.flags.closed));
let Some(curve) = curve.and_then(|curve| curve.without_control_vertex(index)) else { return CmdResult::NeedPoint; };
let mut spline = source.clone();
@@ -576,14 +576,14 @@ impl CadCommand for SplineditCommand {
}
/// Apply a spline operation (CLOSE/OPEN/REVERSE) to a spline entity.
/// Called from `cmd_result.rs` when the ReplaceEntity sentinel is detected.
pub fn apply_spline_op(doc: &mut acadrust::CadDocument, handle: acadrust::Handle, op: &str) {
pub fn apply_spline_op(doc: &mut codec::CadDocument, handle: codec::Handle, op: &str) {
let Some(EntityType::Spline(spline)) = doc.get_entity_mut(handle) else {
return;
};
apply_to_spline(spline, op);
}
fn apply_to_spline(spline: &mut acadrust::entities::Spline, op: &str) {
fn apply_to_spline(spline: &mut codec::entities::Spline, op: &str) {
let result = match op {
"__SPLINEDIT_CLOSE__" => change_closure(spline, true),
"__SPLINEDIT_OPEN__" => change_closure(spline, false),
@@ -593,8 +593,8 @@ fn apply_to_spline(spline: &mut acadrust::entities::Spline, op: &str) {
if let Some(result) = result { *spline = result; }
}
fn change_closure(source: &acadrust::entities::Spline, closed: bool) -> Option<acadrust::entities::Spline> {
use cadkernel::space::{NurbsCurve3, Parameterization};
fn change_closure(source: &codec::entities::Spline, closed: bool) -> Option<codec::entities::Spline> {
use kernel::space::{NurbsCurve3, Parameterization};
if (source.flags.closed || source.flags.periodic) == closed { return None; }
let fit_method = !source.fit_points.is_empty() || source.dwg_flags1 & 1 != 0 || source.dxf_flags & 32 != 0;
let parameterization = match source.knot_parameterization {
@@ -654,8 +654,8 @@ fn change_closure(source: &acadrust::entities::Spline, closed: bool) -> Option<a
#[cfg(test)]
mod tests {
use super::*;
use acadrust::entities::Spline;
use acadrust::Handle;
use codec::entities::Spline;
use codec::Handle;
use iced::Rectangle;
fn control_spline(points: &[[f64; 3]], degree: usize) -> Spline {
@@ -665,7 +665,7 @@ mod tests {
.iter()
.map(|point| Vector3::new(point[0], point[1], point[2]))
.collect();
spline.knots = cadkernel::space::clamped_uniform_knots(degree, points.len());
spline.knots = kernel::space::clamped_uniform_knots(degree, points.len());
spline
}
@@ -785,7 +785,7 @@ mod tests {
#[test]
fn a_closed_weighted_control_curve_can_be_opened() {
let curve = cadkernel::space::NurbsCurve3::from_weighted_control_polygon(
let curve = kernel::space::NurbsCurve3::from_weighted_control_polygon(
2,
&[
[0.0, 0.0, 0.0],
@@ -806,7 +806,7 @@ mod tests {
let opened = change_closure(&spline, false).expect("closed control curve opens");
assert!(!opened.flags.closed);
assert!(!opened.flags.periodic);
assert!(cadkernel::space::NurbsCurve3::new_strict(
assert!(kernel::space::NurbsCurve3::new_strict(
opened.degree as usize,
opened
.control_points
@@ -888,7 +888,7 @@ mod tests {
#[test]
fn closed_conversion_closes_once_and_invalid_precision_retries() {
let curve = cadkernel::space::NurbsCurve3::from_control_polygon(
let curve = kernel::space::NurbsCurve3::from_control_polygon(
2,
&[
[0.0, 0.0, 0.0],
@@ -964,13 +964,13 @@ mod tests {
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["SPLINEDIT"] }); // SplineditCommand
fn spatial_spline(source: &acadrust::entities::Spline) -> Option<cadkernel::space::NurbsCurve3> {
fn spatial_spline(source: &codec::entities::Spline) -> Option<kernel::space::NurbsCurve3> {
let curve = if crate::entities::spline::uses_fit_method(source) {
crate::entities::spline::fit_nurbs3(source)?
} else {
let current = usize::try_from(source.degree).ok()?;
let weights = if source.weights.is_empty() { vec![1.0; source.control_points.len()] } else { source.weights.clone() };
cadkernel::space::NurbsCurve3::new_strict(current,
kernel::space::NurbsCurve3::new_strict(current,
source.control_points.iter().map(|point| [point.x, point.y, point.z]).collect(),
source.knots.clone(), weights)?
};
+1 -1
View File
@@ -15,7 +15,7 @@
// Insert : move the whole entity if its insertion point is inside.
// All others : move the whole entity if any point is inside.
use acadrust::Handle;
use codec::Handle;
use glam::DVec3;
use crate::t;
+2 -2
View File
@@ -5,13 +5,13 @@
// Step 1: Wait for an angle (numerical input), Enter (Most Readable), or pick first point.
// Step 2: If first point picked, pick second point to define angle vector.
use acadrust::Handle;
use codec::Handle;
use glam::DVec3;
use crate::t;
use crate::command::{CadCommand, CmdResult, DynField};
use crate::scene::model::wire_model::WireModel;
use acadrust::EntityType;
use codec::EntityType;
// ── Command implementation ─────────────────────────────────────────────────
+1 -1
View File
@@ -6,7 +6,7 @@
// Step 2: pick destination → translates all selected entities by (dest - base);
// Enter uses the base point as the displacement (commercial solutions).
use acadrust::Handle;
use codec::Handle;
use glam::DVec3;
use crate::command::{CadCommand, CmdResult, EntityTransform};
+14 -14
View File
@@ -10,7 +10,7 @@
use std::f64::consts::TAU;
// The plane geometry these commands run on lives in cadkernel; `geom` adapts
// The plane geometry these commands run on lives in opencadkernel; `geom` adapts
// its call shapes to the loose scalars and f32 render vertices used here.
use super::geom;
use super::geom::{
@@ -20,15 +20,15 @@ use super::geom::{
use crate::modules::draw::fence::{crossing_box_preview, FencePick};
use acadrust::entities::{
use codec::entities::{
Arc as ArcEnt, Circle as CircleEnt, Ellipse as EllipseEnt, Line as LineEnt, LwPolyline,
LwVertex, Ray as RayEnt, Spline as SplineEnt, XLine as XLineEnt,
};
use acadrust::types::Vector3;
use acadrust::{EntityType, Handle};
use codec::types::Vector3;
use codec::{EntityType, Handle};
use glam::DVec3;
use cadkernel::geom2d::nurbs::clamped_uniform_knots;
use cadkernel::geom2d::{
use kernel::geom2d::nurbs::clamped_uniform_knots;
use kernel::geom2d::{
intersect as kernel_intersect, trim_spans as kernel_trim_spans, Arc as KernelArc,
BulgeArc, Circle as KernelCircle, Curve, Extent as KernelExtent,
Ellipse as KernelEllipse, EllipseArc as KernelEllipseArc, Line as KernelLine,
@@ -77,7 +77,7 @@ const TRIM_EXTENT: f64 = 1_000_000.0;
/// Sampling density for the plan-view point lists the fence and preview
/// passes walk. The renderer's own figure, so a preview cut lands where the
/// drawn geometry is rather than a chord away from it.
const SAMPLE_SEGMENTS_PER_RADIAN: f64 = cadkernel::geom2d::DEFAULT_SEGMENTS_PER_RADIAN;
const SAMPLE_SEGMENTS_PER_RADIAN: f64 = kernel::geom2d::DEFAULT_SEGMENTS_PER_RADIAN;
/// If a trim interval endpoint is beyond this threshold it is treated as "infinite".
#[derive(Clone)]
@@ -694,11 +694,11 @@ fn extend_spline(spl: &SplineEnt, t_click: f64, geos: &[Geo]) -> Option<EntityTy
if extend_end {
new_spl
.control_points
.push(acadrust::types::Vector3::new(hit_x, hit_y, z));
.push(codec::types::Vector3::new(hit_x, hit_y, z));
} else {
new_spl
.control_points
.insert(0, acadrust::types::Vector3::new(hit_x, hit_y, z));
.insert(0, codec::types::Vector3::new(hit_x, hit_y, z));
}
// Rebuild knots (uniform) for the extended control polygon.
let degree = new_spl.degree as usize;
@@ -2548,7 +2548,7 @@ impl CadCommand for TrimCommand {
}
}
fn on_entity_replaced(&mut self, _old: Handle, new_handles: &[acadrust::Handle]) {
fn on_entity_replaced(&mut self, _old: Handle, new_handles: &[codec::Handle]) {
// Batch gestures stage several NULL-handle replacement groups before
// the document assigns real handles. The host applies them in the same
// order, so fill the first remaining placeholders on each callback.
@@ -3227,7 +3227,7 @@ impl CadCommand for ExtendCommand {
}
}
fn on_entity_replaced(&mut self, _old: Handle, new_handles: &[acadrust::Handle]) {
fn on_entity_replaced(&mut self, _old: Handle, new_handles: &[codec::Handle]) {
// The last new_handles.len() entries are the pieces appended with NULL
// handles in on_entity_pick — assign their real document handles.
let start = self.all_entities.len().saturating_sub(new_handles.len());
@@ -3816,7 +3816,7 @@ mod tests {
/// two empty ends as equal and sliced the empty sample from index 1.
#[test]
fn sampling_skips_segments_with_no_plan_shape() {
use acadrust::entities::{Polyline3D, Vertex3DPolyline};
use codec::entities::{Polyline3D, Vertex3DPolyline};
let mut pl = Polyline3D::new();
pl.vertices = vec![
@@ -3842,7 +3842,7 @@ mod tests {
/// must keep dropping the duplicated seam vertex between two of them.
#[test]
fn sampling_still_joins_segments_at_their_seam() {
use acadrust::entities::{Polyline3D, Vertex3DPolyline};
use codec::entities::{Polyline3D, Vertex3DPolyline};
let mut pl = Polyline3D::new();
pl.vertices = vec![
@@ -3862,7 +3862,7 @@ mod tests {
#[test]
fn preview_sampling_skips_edge_on_polyline2d_arc_segments() {
use acadrust::entities::{Polyline2D, Vertex2D};
use codec::entities::{Polyline2D, Vertex2D};
let mut pl = Polyline2D::new();
let mut start = Vertex2D::new(Vector3::new(0.0, 0.0, 0.0));