app
Fusion - the Fusion 360 Bridge
Public Made by Adomby adom
Drive Autodesk Fusion 360 from the cloud via Adom Bridge: component libraries, IPC package generation, board layout, exports (STEP/Gerbers/BOM/CPL), fast APS cloud search, and parametric modeling.
master
Drew Owens
Share frozen native Rust mechanical development checkpoint and qualification scope
b2d7cdb
27d ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618
mod line_state;
use super::*;
use crate::sketches::{Backend, CreateAttempt, RootState, SketchState};
#[repr(C)]
#[derive(Default)]
struct RawRoot {
active: u32,
parametric: u32,
hybrid: u32,
configured: u32,
sketch_count: u64,
timeline_count: u64,
timeline_position: u64,
}
#[repr(C)]
#[derive(Default)]
struct RawSketch {
transform: [f64; 16],
curves: u64,
points: u64,
profiles: u64,
dimensions: u64,
constraints: u64,
plane: u32,
health: u32,
deferred: u32,
}
extern "C" {
fn af_sketch_root(
c: *mut c_void,
d: *mut c_void,
out: *mut RawRoot,
sink: Sink,
user: *mut c_void,
) -> i32;
fn af_sketch_create(
c: *mut c_void,
d: *mut c_void,
plane: u32,
out: *mut *mut c_void,
invoked: *mut u32,
) -> i32;
fn af_sketch_read(
c: *mut c_void,
d: *mut c_void,
sketch: *mut c_void,
out: *mut RawSketch,
sink: Sink,
user: *mut c_void,
) -> i32;
fn af_sketch_release(c: *mut c_void, sketch: *mut c_void) -> i32;
}
// No Drop: consuming release is explicit before parent document/context release.
pub(super) struct Handle(NonNull<c_void>);
pub(super) struct Adapter<'a> {
pub context: &'a Context,
pub document: &'a DocumentReference,
}
impl Backend for Adapter<'_> {
type Handle = Handle;
fn root(&self) -> Result<RootState, String> {
self.context.close.ensure_open()?;
self.context.child_leases.ensure_clear()?;
let mut raw = RawRoot::default();
let mut fields = Fields::default();
unsafe {
checked(af_sketch_root(
self.context.ptr.as_ptr(),
self.document.ptr.as_ptr(),
&mut raw,
collect,
(&mut fields as *mut Fields).cast(),
))?;
}
Ok(RootState {
active: flag(raw.active)?,
parametric: flag(raw.parametric)?,
hybrid: flag(raw.hybrid)?,
configured: flag(raw.configured)?,
root_revision: field(&mut fields, 0)?,
sketch_count: raw.sketch_count,
timeline_count: raw.timeline_count,
timeline_position: raw.timeline_position,
})
}
fn command(&self) -> Result<String, String> {
self.context.active_command()
}
fn create(&self, plane: u32) -> CreateAttempt<Handle> {
let mut ptr = std::ptr::null_mut();
let mut invoked = 0;
let status = unsafe {
af_sketch_create(
self.context.ptr.as_ptr(),
self.document.ptr.as_ptr(),
plane,
&mut ptr,
&mut invoked,
)
};
CreateAttempt {
handle: NonNull::new(ptr).map(Handle),
invoked: invoked != 0,
acknowledged: status == 0 && invoked == 1,
}
}
fn read(&self, handle: &Handle) -> Result<SketchState, String> {
self.context.close.ensure_open()?;
self.context.child_leases.ensure_clear()?;
let mut raw = RawSketch::default();
let mut fields = Fields::default();
unsafe {
checked(af_sketch_read(
self.context.ptr.as_ptr(),
self.document.ptr.as_ptr(),
handle.0.as_ptr(),
&mut raw,
collect,
(&mut fields as *mut Fields).cast(),
))?;
}
Ok(SketchState {
name: field(&mut fields, 0)?,
sdk_revision: field(&mut fields, 1)?,
plane: raw.plane,
transform: raw.transform,
curves: raw.curves,
points: raw.points,
profiles: raw.profiles,
dimensions: raw.dimensions,
constraints: raw.constraints,
health: raw.health,
deferred: flag(raw.deferred)?,
})
}
}
fn flag(value: u32) -> Result<bool, String> {
match value {
0 => Ok(false),
1 => Ok(true),
_ => Err("invalid SDK boolean".into()),
}
}
pub(super) fn release(context: &Context, handle: Handle) -> Result<(), String> {
unsafe { checked(af_sketch_release(context.ptr.as_ptr(), handle.0.as_ptr())) }
}
use crate::sketches::geometry::{GeometryBackend, InsertAttempt, ObservedEntity};
use crate::sketches::geometry_preparation::{Entity, Geometry};
#[repr(C)]
struct RawGeometry {
kind: u32,
construction: u32,
closed: u32,
point_count: u32,
reference: u32,
scalar: f64,
normal: [f64; 3],
points: [f64; 384],
}
const _: () = assert!(std::mem::size_of::<RawGeometry>() == 3128);
const _: () = assert!(std::mem::offset_of!(RawGeometry, points) == 56);
impl Default for RawGeometry {
fn default() -> Self {
Self {
kind: 0,
construction: 0,
closed: 0,
point_count: 0,
reference: 0,
scalar: 0.,
normal: [0.; 3],
points: [0.; 384],
}
}
}
extern "C" {
fn af_geometry_equalities(
c: *mut c_void,
d: *mut c_void,
s: *mut c_void,
leases: *const u64,
n: u32,
out: *mut u8,
) -> i32;
fn af_geometry_empty(c: *mut c_void, d: *mut c_void, s: *mut c_void, out: *mut u32) -> i32;
fn af_geometry_insert(
c: *mut c_void,
d: *mut c_void,
s: *mut c_void,
input: *const RawGeometry,
lease: *mut u64,
invoked: *mut u32,
stage: *mut u32,
) -> i32;
fn af_geometry_read(
c: *mut c_void,
d: *mut c_void,
s: *mut c_void,
lease: u64,
out: *mut RawGeometry,
) -> i32;
}
impl RawGeometry {
fn input(entity: &Entity) -> Self {
let mut raw = Self {
construction: match entity.construction {
None => 0,
Some(false) => 1,
Some(true) => 2,
},
..Self::default()
};
let points = match &entity.geometry {
Geometry::Point { position_cm } => {
raw.kind = 1;
vec![*position_cm]
}
Geometry::Line { start_cm, end_cm } => {
raw.kind = 2;
vec![*start_cm, *end_cm]
}
Geometry::Circle {
center_cm,
radius_cm,
} => {
raw.kind = 3;
raw.scalar = *radius_cm;
vec![*center_cm]
}
Geometry::Arc {
center_cm,
start_cm,
sweep_radians,
..
} => {
raw.kind = 4;
raw.scalar = *sweep_radians;
vec![*center_cm, *start_cm]
}
Geometry::FittedSpline {
fit_points_cm,
closed,
} => {
raw.kind = 5;
raw.closed = u32::from(*closed);
fit_points_cm.clone()
}
};
raw.point_count = points.len() as u32;
for (i, p) in points.iter().enumerate() {
raw.points[i * 3..i * 3 + 3].copy_from_slice(p);
}
raw
}
fn observed(&self) -> Result<ObservedEntity, String> {
let n = self.point_count as usize;
if n > 128
|| !self.scalar.is_finite()
|| !self.points[..n * 3]
.iter()
.all(|p| p.is_finite() && p.abs() <= 10000.)
{
return Err("native_geometry_invalid_sdk_geometry".into());
}
let p = |i: usize| {
[
self.points[i * 3],
self.points[i * 3 + 1],
self.points[i * 3 + 2],
]
};
if matches!(self.kind, 3 | 4)
&& self
.normal
.iter()
.zip([0., 0., 1.])
.any(|(a, b)| !a.is_finite() || (*a - b).abs() > 1e-9)
{
return Err("native_geometry_invalid_sdk_plane".into());
}
let geometry = match self.kind {
1 if n == 1 => Geometry::Point { position_cm: p(0) },
2 if n == 2 => Geometry::Line {
start_cm: p(0),
end_cm: p(1),
},
3 if n == 1 => Geometry::Circle {
center_cm: p(0),
radius_cm: self.scalar,
},
4 if n == 3 => Geometry::Arc {
center_cm: p(0),
start_cm: p(1),
end_cm: p(2),
sweep_radians: self.scalar,
},
5 if n >= 2 => Geometry::FittedSpline {
fit_points_cm: (0..n).map(p).collect(),
closed: flag(self.closed)?,
},
_ => return Err("native_geometry_invalid_sdk_kind".into()),
};
Ok(ObservedEntity {
geometry,
construction: match self.construction {
0 => None,
1 => Some(false),
2 => Some(true),
_ => return Err("native_geometry_invalid_sdk_flag".into()),
},
reference: flag(self.reference)?,
})
}
}
impl GeometryBackend for Adapter<'_> {
fn entity_equalities(&self, handle: &Handle, leases: &[u64]) -> Result<Vec<Vec<bool>>, String> {
self.context.close.ensure_open()?;
self.context.child_leases.ensure_clear()?;
if leases.is_empty() || leases.len() > 64 {
return Err("native_entities_bound".into());
}
let mut matrix = [0u8; 4096];
unsafe {
checked(af_geometry_equalities(
self.context.ptr.as_ptr(),
self.document.ptr.as_ptr(),
handle.0.as_ptr(),
leases.as_ptr(),
leases.len() as u32,
matrix.as_mut_ptr(),
))?;
}
matrix[..leases.len() * leases.len()]
.chunks_exact(leases.len())
.map(|row| row.iter().map(|v| flag(*v as u32)).collect())
.collect()
}
fn empty_origin_only(&self, handle: &Handle) -> Result<bool, String> {
self.context.close.ensure_open()?;
self.context.child_leases.ensure_clear()?;
let mut empty = 0;
unsafe {
checked(af_geometry_empty(
self.context.ptr.as_ptr(),
self.document.ptr.as_ptr(),
handle.0.as_ptr(),
&mut empty,
))?;
}
flag(empty)
}
fn insert(&self, handle: &Handle, entity: &Entity) -> InsertAttempt {
let raw = RawGeometry::input(entity);
let mut lease = u64::MAX;
let mut invoked = 0;
let mut stage = 0;
let status = unsafe {
af_geometry_insert(
self.context.ptr.as_ptr(),
self.document.ptr.as_ptr(),
handle.0.as_ptr(),
&raw,
&mut lease,
&mut invoked,
&mut stage,
)
};
InsertAttempt {
lease: (lease != u64::MAX).then_some(lease),
invoked: invoked != 0,
acknowledged: status == 0 && invoked == 1 && stage == 5,
stage: match stage {
1 => "prepare",
2 => "add",
3 => "construction",
4 => "periodic_closure",
5 => "complete",
_ => "unconfirmed",
},
}
}
fn entity(&self, handle: &Handle, lease: u64) -> Result<ObservedEntity, String> {
self.context.close.ensure_open()?;
self.context.child_leases.ensure_clear()?;
let mut raw = RawGeometry::default();
let status = unsafe {
af_geometry_read(
self.context.ptr.as_ptr(),
self.document.ptr.as_ptr(),
handle.0.as_ptr(),
lease,
&mut raw,
)
};
if status != 0 {
return Err(format!("native_geometry_read_status_{status}"));
}
raw.observed()
}
}
use crate::sketches::extrusion::{ExtrudeAttempt, ExtrusionBackend, ProfileState, SolidState};
#[repr(C)]
#[derive(Default)]
struct RawProfile {
area: f64,
perimeter: f64,
centroid: [f64; 3],
loops: u64,
curves: u64,
root_bodies: u64,
root_extrudes: u64,
on_plane: u32,
outer_only: u32,
high_accuracy: u32,
}
#[repr(C)]
#[derive(Default)]
struct RawSolid {
volume: f64,
centroid_sketch: [f64; 3],
distance: f64,
taper: f64,
health: u32,
body_solid: u32,
feature_solid: u32,
new_body: u32,
one_side: u32,
profile_plane_start: u32,
high_accuracy: u32,
}
#[repr(C)]
struct RawExtrudeDiagnostic {
sdk_error: i32,
stage: u32,
length: u32,
truncated: u32,
text: [u8; 4096],
}
impl Default for RawExtrudeDiagnostic {
fn default() -> Self {
Self {
sdk_error: -1,
stage: 0,
length: 0,
truncated: 0,
text: [0; 4096],
}
}
}
const _: () = assert!(std::mem::size_of::<RawProfile>() == 88);
const _: () = assert!(std::mem::size_of::<RawSolid>() == 80);
const _: () = assert!(std::mem::size_of::<RawExtrudeDiagnostic>() == 4112);
extern "C" {
fn af_profile_read(
c: *mut c_void,
d: *mut c_void,
s: *mut c_void,
out: *mut RawProfile,
diagnostic: *mut RawExtrudeDiagnostic,
) -> i32;
fn af_extrude_create(
c: *mut c_void,
d: *mut c_void,
s: *mut c_void,
distance: f64,
negative: u32,
invoked: *mut u32,
diagnostic: *mut RawExtrudeDiagnostic,
) -> i32;
fn af_extrude_verify_profile(
c: *mut c_void,
d: *mut c_void,
s: *mut c_void,
diagnostic: *mut RawExtrudeDiagnostic,
) -> i32;
fn af_extrude_read(
c: *mut c_void,
d: *mut c_void,
s: *mut c_void,
out: *mut RawSolid,
diagnostic: *mut RawExtrudeDiagnostic,
) -> i32;
}
impl Adapter<'_> {
fn extrusion_checked(
&self,
status: i32,
diagnostic: &RawExtrudeDiagnostic,
) -> Result<(), String> {
if status == 0 {
return Ok(());
}
let value = crate::sketches::extrusion::sdk_diagnostic(
status,
diagnostic.stage,
diagnostic.sdk_error,
&diagnostic.text,
diagnostic.length as usize,
diagnostic.truncated != 0,
);
self.context.extrusion_diagnostic.replace(value);
Err(format!(
"native_extrude_sdk_status_{status}_stage_{}_error_{}",
diagnostic.stage, diagnostic.sdk_error
))
}
}
impl ExtrusionBackend for Adapter<'_> {
fn clear_diagnostic(&self) {
self.context.extrusion_diagnostic.replace(Value::Null);
}
fn diagnostic(&self) -> Value {
self.context.extrusion_diagnostic.borrow().clone()
}
fn profile(&self, handle: &Handle) -> Result<ProfileState, String> {
self.context.close.ensure_open()?;
self.context.child_leases.ensure_clear()?;
let mut raw = RawProfile::default();
let mut diag = RawExtrudeDiagnostic::default();
let status = unsafe {
af_profile_read(
self.context.ptr.as_ptr(),
self.document.ptr.as_ptr(),
handle.0.as_ptr(),
&mut raw,
&mut diag,
)
};
self.extrusion_checked(status, &diag)?;
Ok(ProfileState {
area_cm2: raw.area,
perimeter_cm: raw.perimeter,
centroid_cm: raw.centroid,
loops: raw.loops,
curves: raw.curves,
root_bodies: raw.root_bodies,
root_extrudes: raw.root_extrudes,
on_plane: flag(raw.on_plane)?,
outer_only: flag(raw.outer_only)?,
high_accuracy: flag(raw.high_accuracy)?,
})
}
fn extrude(&self, handle: &Handle, distance_cm: f64, negative: bool) -> ExtrudeAttempt {
let mut invoked = 0;
let mut diag = RawExtrudeDiagnostic::default();
let status = unsafe {
af_extrude_create(
self.context.ptr.as_ptr(),
self.document.ptr.as_ptr(),
handle.0.as_ptr(),
distance_cm,
u32::from(negative),
&mut invoked,
&mut diag,
)
};
let _ = self.extrusion_checked(status, &diag);
ExtrudeAttempt {
invoked: invoked != 0,
acknowledged: status == 0 && invoked == 1,
stage: match diag.stage {
200..=208 | 213..=214 => "feature_input",
209 => "feature_add",
210..=212 => "returned_body_retention",
_ => "profile_or_shared_guard",
},
}
}
fn verify_extrude_profile(&self, handle: &Handle) -> Result<(), String> {
self.context.close.ensure_open()?;
self.context.child_leases.ensure_clear()?;
let mut diag = RawExtrudeDiagnostic::default();
let status = unsafe {
af_extrude_verify_profile(
self.context.ptr.as_ptr(),
self.document.ptr.as_ptr(),
handle.0.as_ptr(),
&mut diag,
)
};
self.extrusion_checked(status, &diag)
}
fn solid(&self, handle: &Handle) -> Result<SolidState, String> {
self.context.close.ensure_open()?;
self.context.child_leases.ensure_clear()?;
let mut raw = RawSolid::default();
let mut diag = RawExtrudeDiagnostic::default();
let status = unsafe {
af_extrude_read(
self.context.ptr.as_ptr(),
self.document.ptr.as_ptr(),
handle.0.as_ptr(),
&mut raw,
&mut diag,
)
};
self.extrusion_checked(status, &diag)?;
Ok(SolidState {
volume_cm3: raw.volume,
centroid_sketch_cm: raw.centroid_sketch,
distance_cm: raw.distance,
taper_radians: raw.taper,
feature_health: raw.health,
body_solid: flag(raw.body_solid)?,
feature_solid: flag(raw.feature_solid)?,
new_body: flag(raw.new_body)?,
one_side: flag(raw.one_side)?,
profile_plane_start: flag(raw.profile_plane_start)?,
high_accuracy: flag(raw.high_accuracy)?,
})
}
}
mod relations;
mod dimensions;