ricosjp / truck Goto Github PK
View Code? Open in Web Editor NEWTruck is a Rust CAD Kernel.
License: Apache License 2.0
Truck is a Rust CAD Kernel.
License: Apache License 2.0
Hi! love your work so far!!
Consider an airfoil:
I can use tsweep
to create a fully-symmetrical airfoil, but no such method exists in the builder
module to make a non-symmetrical airfoil like shown above (where one end tapers, like a commerical jet).
Can support for such an extrusion be added?
This is what I am using right now, but I don't think its fully correct:
fn extrude_then_transform<T: Sweep<Point3, Curve, Surface>>(
elem: &T,
extrude: Vector3,
transform: Matrix4,
) -> T::Swept {
let trsl_ex = Matrix4::from_translation(extrude);
elem.sweep(
&move |pt| transform.transform_point(trsl_ex.transform_point(*pt)),
&move |curve| curve.transformed(trsl_ex).transformed(transform),
&move |surface| surface.transformed(trsl_ex).transformed(transform),
&move |pt0, pt1| Curve::Line(Line(*pt0, *pt1)),
&move |curve0, curve1| match (curve0, curve1) {
(Curve::Line(line), Curve::Line(_)) => Surface::Plane(Plane::new(
line.0,
line.1,
transform.transform_point(line.1) + extrude,
)),
_ => unreachable!(),
},
)
}
I like very much the direction truck is going and as very early adaptor I am trying to integrate it with egui. Truck has a nice wgpu integration and for egui there is also a wgpu integration example. So I would like to use egui for all the app initialization and event handling and truck just for rendering a truck scene.
Now I took a look at truck-js to get an idea for the most bare bone approach, turning a Polygon mesh into a buffer suitable for webGL. However I would like to reuse as much as possible from truck wpgu render implementation. I think the examples and also what is in the tests folder is important for my use case. However I feel a bit lost right now ...
So my questions are:
CadQuery is an intuitive, easy-to-use Python module for building parametric 3D CAD models. At present, the underlying CAD kernel for CadQuery is OpenCascade. Multiple CAD kernels supporting was considered in the initial design of CadQuery. However, the open source CAD kernel is rare. To experiment with adaptation to other CAD kernels, We've created a minimal CadQuery. IMO, another kernel may benefit the development of CadQuery.
To make the adaption, we firstly need to create a Python binding for truck. I wonder whether somebody has done the Python binding thing?
On top of the above mentioned Python binding, truck should support the following CAD types:
Vertex
Edge
Wire
Face
Shell
Solid
CompSolid
Compound
A STEP exporting function should be there for output.
I'd like to see that the CadQuery could work with truck, and I'd like to make this happen, even if only a simplest prototype at the first beginning.
Are there plans to support t-splines as well on truck?
Also seeing how t-splines are a superset for NURBS and subdivision surfaces (hope my understanding is correct :p ) would it make sense to make all modelling centre around T-splines?
E.g.:
let bottle: Solid = bottle(1.4, 1.0, 0.6);
let bottle_mesh: PolygonMesh = PolygonMesh::from(bottle);
Add a Rendered
struct for rendering wire frame shading.
truck-polymesh
is still WIP. Now I plan the following changes.
PolygonMesh
should be private.MeshHandler
should be removed and all mesh handling algorithms should be implemented to PolygonMesh
as traits.Moreover, we should prepare the tests for mesh algorithms.
For reasons of manpower, we have not yet inherited some of the methods of BSplineSurface
to NURBSSurface
.
It should be fixed as soon as possible.
When using rsweep with large number for vertex coord, for example, 500 for x. It will panic:
panicked at 'failed to create instance', truck-rendimpl\src\shaperend.rs:86:14
fn create_test_solid() -> Solid(){
let vertex = builder::vertex(Point3::new(500.0, 0.0, 100.0 as f64));
// sweep the vertex along a circle
let circle: Wire = builder::rsweep(
// the reference to the vertex
&vertex,
// a point on the axis
// Point3::new(d.offset as f64 + d.radius as f64, 0.0, 0.0),
Point3::new(500.0 as f64, 0.0, 0.0),
// the direction of the axis
Vector3::unit_y(),
// If the absolute value is no less than 2π radian, a closed shape will be generated.
Rad(7.0),
);
let disk = builder::try_attach_plane(&vec![circle]).unwrap();
// sweep the circle along a circle
let mut solid = builder::rsweep(
// the reference to the wire
&disk,
// a point on the axis
Point3::origin(),
// the direction of the axis
Vector3::unit_z(),
// If the absolute value is no less than 2π radian, a closed shape will be generated.
Rad(d.angle as f64),
);
solid
}
I am watching this project. It's very good that this's implemented in rust. And I wonder what's the major difference between this and opencascade, is there any better design inside this project?
at my experiments with truck I ran into an issue where builder::try_attach_plane
sometimes worked as expected and sometimes just failed with WireNotInOnePlane
. I extracted from my app some actual numerical values where it fails
FAIL:
let start = Point3::new(333.3333333333333, 0.0, 0.0);
let axis = Vector3::new(333.3333333333333, 0.0, 0.0);
let pt = Point3::new(333.3333333333333, 0.0, 25.0);
let vertex = builder::vertex(pt);
let circle = builder::rsweep(&vertex, start, axis, Rad(7.0));
I "rounded" the numbers a bit and then it worked:
let start = Point3::new(333.3333333333333 as f32 as f64, 0.0, 0.0);
let axis = Vector3::new(333.3333333333333 as f32 as f64, 0.0, 0.0);
let pt = Point3::new(333.3333333333333 as f32 as f64, 0.0, 25.0);
let vertex = builder::vertex(pt);
let circle = builder::rsweep(&vertex, start, axis, Rad(7.0));
But like this I lose precision and I a have no clue how reliable it is
So how can I make builder::try_attach_plane
work in a reliable way ?
As mentioned in issue #2 I'd like to start implementing STEP loading functionality for truck. I've spent the last week getting reacquainted with ruststep and truck and I've built a set of assumptions that I hope you can confirm for me.
Simple { id: 22245, record: Record { name: "CARTESIAN_POINT", parameter: List([String(""), List([Real(66.1), Real(-23.2), Real(0.0)])]) } }
instead of like this: CartesianPoint { point: "", coordinates: vec![66.1, -23.2, 0.0]}
. This would have to be implemented before actually starting STEP importing in to truck.Are these assumptions correct and sufficient to begin implementation?
Best regards and thank you so much for your amazing work!
Any hint as to when we can expect Boolean operations to be made available in the package. I would like to know if it is possible to generate a mesh from a only a set of 3D points.
I need some clarification on best approach for creating a scene with repeating objects which share the same base shape (e.g. a cylinder). Let's say I have about hundred of these cylinders and their form, position and orientation is defined by their model matrix of their PolygonInstance or WireframeInstance. So if I create just one PolygonInstance and WireframeInstance and then clone it hundred times, and of course adapt their model matrix, does that mean they still share all the same Mesh Data ? Obviously, for performance and memory saving reasons that is what I want. But I am not sure, if I understood this properly.
Here's the rsweep issuse case, this cylinder can't be tessellationed, because the pivot line is aligned with the rotation axis, and the center point is coincide,so the connect_wires
method has wrong results.
let v = builder::vertex(Point3::new(0.0, 0.0, 0.0));
let e = builder::tsweep(&v, Vector3::unit_x());
let f = builder::tsweep(&e, Vector3::unit_y());
let cylinder = builder::rsweep(&f, Point3::origin(), Vector3::unit_y(), Rad(7.0));
let json = serde_json::to_vec_pretty(&cylinder).unwrap();
std::fs::write("cylinder.json", json).unwrap();
like this, one side face is not shown.
The reason is the following: I tried to use this 3d gizmo, it works perfectly (beside of some flickering caused by my egui integration), but that gizmo needs the projection matrix. I could calculate that matrix from Camera.projection()
and Camera.matrix
or even mess around with egui-gizmo sources, but the easiest is just to have access to the raw projection matrix.
truck-polymesh/examples/splitting-samples.rs
outputs about 3000 files with only one triangle registered. It is not correct.
Hello,
I'm very excited about this project! Do you plan to implement your own 2D sketching and constraint solving? This is on its own quite a difficult thing to get right, judging by the review papers I've seen.
One possibility could be to use Solvespace as a library, but it's written in C not Rust.
I'd be interested in collaborating with you on this, if you decide to write your own!
Are you planning to create WASM builds and javascript bindings for truck? I think it would be very useful as a way of showcasing what the library is capable of. I'd be very interested in using such bindings if they existed to build an open source web-based CAD program!
Hello,
I'd love to see support for Chamfer and Fillet commands within Truck. Is that on the roadmap? What do you think it would take to implement?
Cheers
We are not yet prepared for rotational sweeps with a fixed point. For example, we cannot create even a sphere by simple rotational sweeps.
The biggest difficulty in implementation is that when the surface is created in a naive way, the NURBS surface degenerates around the fixed point and the normal vector at the fixed point cannot be calculated.
In the example torus
, the result is wrong if one make the angle of secondary rotation more than π and less than 2π.
I think this occurs by the convergence of Newton method, however, perhaps there is a bug in the primitive modeling algorithm.
Consider comment format. cf. knot_vec.rs by virtualritz
Rationale:
parking_lot::Mutex
is the unwritten actual standard for various reasons (see Differences from the standard library Mutex).
It also makes the code not being littered with unwrap()
s wherever a Mutex
is locked. I.e. you will find that almost all crates in the wild that need Mutex
or RwLock
will use the ones from parking_lot
and not the ones from std
.
Using this is kind of a one way street as adding a feature flag means a lot of code duplication and lots of clutter in the code (i.e. worse than the aforementioned unwrap()
s).
rclite::Arc
is purportedly faster than the std
variant and it also uses a lot less memory (4 bytes on 64 bit systems vs 16 bytes for the std
variant).
This could be hidden behind a feature flag as the access syntax is the same.
For Raycasting (which is needed when any rendered object on the screen should become mouse selectable) the inverse of the Camera::projection()
is needed, but because wgsl has no matrix inverse()
and truck Camera::projection
is private, only by adapting the truck source code, the inverse of the view * projection matrix can be obtained. So my feature request is to add something like Camera::inverse_projection()
and its shader binding, so one can access in the shader of a custom implementation of Rendered
also a inverse_projection
field of the Camera
struct.
What is the best way to draw with truck-platform simple 3d lines (or other simple objects) for things like visualizing the coordinate system (e.g. lines with arrows along the coordinate axes and a grid at the xz-plane). And as requirement for lines I would like to set their color and thickness if possible. truck does not seem to expose an API for specific for these tasks but with truck-modeling::builder
I think I can construct anything. Or is there any other more appropriate approach ?
Discuss improvement of README in the top.
The draft will be used here, and I will list the points I was concerned about.
This is a design problem. For resolving, we have to separate InstanceDescriptor
into ShapeInstanceDescriptor
and PolygonInstanceDescriptor
.
in the first sentence in lib.rs it should be "Mesh algorithms" not "Mesh algorighms"
I recently came about this (https://github.com/gcherchi/InteractiveAndRobustMeshBooleans) implementation of boolean operations that claims to be really fast albeith in c++ and only on "manifold, watertight, self-intersections free, and well-oriented" meshes.
Would it be possible to implement this in the shapeops crate?
I've been able to generate Solids and Shells in Rust, but I'd like to create a triangle mesh that I can send to three.js for rendering. Clearly truck is capable of generating that mesh, since we can view the bottle with webGPU, but I can't figure out how to do it! Could you offer some pointers?
Hello,
Would it be possible to implement something like solid::volume()
and solid::center_of_mass()
? These are very useful for CAD applications.
Cheers
As long as we claim this crates a CAD Kernel, STEP I/O is unavoidable.
We're working on a plan for this, which we'll announce here soon.
Hello,
I am having a issue when running the rotate-objects example on the DX12 backend.
cargo run --example rotate-objects
thread 'main' panicked at 'failed to create graphics pipeline: Failed to create pipeline: Unsupported usage: Implementation specific error occurred', C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\wgpu-core-0.6.5\src\device\mod.rs:2898:30
Here is the backtrace:
stack backtrace:
0: std::panicking::begin_panic_handler
at /rustc/cb75ad5db02783e8b0222fee363c5f63f7e2cf5b\/library\std\src\panicking.rs:493
1: std::panicking::begin_panic_fmt
at /rustc/cb75ad5db02783e8b0222fee363c5f63f7e2cf5b\/library\std\src\panicking.rs:435
2: wgpu_core::device::{{impl}}::device_create_render_pipeline::{{closure}}<wgpu_core::hub::IdentityManagerFactory,gfx_backend_dx12::Backend>
at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\wgpu-core-0.6.5\src\device\mod.rs:2898
3: core::result::Result<gfx_backend_dx12::resource::GraphicsPipeline, gfx_hal::pso::CreationError>::map_err<gfx_backend_dx12::resource::GraphicsPipeline,gfx_hal::pso::CreationError,wgpu_core::device::DeviceError,closure-10>
at C:\Users\user\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\result.rs:595
4: wgpu_core::hub::Global<wgpu_core::hub::IdentityManagerFactory>::device_create_render_pipeline<wgpu_core::hub::IdentityManagerFactory,gfx_backend_dx12::Backend>
at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\wgpu-core-0.6.5\src\device\mod.rs:2893
5: wgpu::backend::direct::{{impl}}::device_create_render_pipeline
at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\wgpu-0.6.2\src\backend\direct.rs:758
6: wgpu::Device::create_render_pipeline
at C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\wgpu-0.6.2\src\lib.rs:1475
7: truck_rendimpl::InstanceState::pipeline_with_shader
at .\truck-rendimpl\src\instdesc.rs:186
8: truck_rendimpl::PolygonInstance::pipeline_with_shader
at .\truck-rendimpl\src\polyrend.rs:142
9: truck_rendimpl::polyrend::{{impl}}::pipeline
at .\truck-rendimpl\src\polyrend.rs:189
10: truck_platform::Rendered::render_object<truck_rendimpl::PolygonInstance>
at .\truck-platform\src\lib.rs:345
11: truck_platform::Scene::add_object
at .\truck-platform\src\scene.rs:357
12: rotate_objects::MyRender::load_obj<slice<u8>>
at .\truck-rendimpl\examples\rotate-objects.rs:63
13: rotate_objects::{{impl}}::init
at .\truck-rendimpl\examples\rotate-objects.rs:136
14: rotate_objects::app::App::run<rotate_objects::MyRender>
at .\truck-rendimpl\examples\app.rs:93
15: rotate_objects::main
at .\truck-rendimpl\examples\rotate-objects.rs:282
16: core::ops::function::FnOnce::call_once<fn(),tuple<>>
at C:\Users\user\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\ops\function.rs:227
I found a similar issue reported gfx-rs/wgpu-rs#600.
Thanks.
I test the cylinder code following:
pub fn create_cylinder(height: f64, radius: f64) -> Solid {
// make a solid cylinder
let vertex = builder::vertex(Point3::new(0.0, 0.0, radius));
let circle = builder::rsweep(&vertex, Point3::origin(), Vector3::unit_y(), Rad(7.0));
let disk = builder::try_attach_plane(&vec![circle]).unwrap();
let solid = builder::tsweep(&disk, Vector3::new(0.0, height, 0.0));
// Return the solid as a boundary shell for easier processing later.
// solid.into_boundaries().pop().unwrap()
solid
}
With default viewer, this comes out this. And some corner shows shadow lines with some camera angle.
See Rust API naming guidelines.
In
UpperCamelCase
, acronyms and contractions of compound words count as one word: useUuid
rather thanUUID
,Usize
rather thanUSize
orStdin
rather thanStdIn
.
For example in stl.rs
:
STLFace
-> StlFace
STLReader
-> StlReader
STLType
-> StlType
STLType::ASCII
-> StlType::Ascii
etc.
Would you consider a PR with such changes if I opened one?
According to the code, there are two ways to create a sphere:
let uhcircle = NURBSCurve::new(BSplineCurve::new(knot_vec, control_points));
let sphere = RevolutedCurve::by_revolution(
uhcircle, Point3::origin(), Vector3::unit_x(),
);
let sphere = Sphere::new(center, radius);
but they all don't impl the ModelShape trait, how to export sphere to step file ?
Thanks for all of the work you've put into this library --- it's awesome to have such a well-written codebase to use and learn from.
Are there any books, papers, or other resources you would recommend for better understanding Truck's algorithms and the overall approach you want the library to take?
I've read Nagy and Stroud's Solid Modelling and CAD Systems: How to Survive a CAD System which is a great overview of topology terminology and various operations, but it doesn't provide detailed algorithms.
Is there anything you recommend potential contributors read and understand so they can productively contribute to this library?
Ayam is a mature OSS NURBS modeler under a BSD license (written in C).
It has been around/developed since over 20 years. I would think it implements a lot of the operations truck would want to support.
Consider a 2d triangle/polygon with a smaller 2d triangle cut out of it:
When I extrude this using tsweep
and then generate a STL / OBJ file with it and import it into PrusaSlicer, the slicer reports errors:
I've tried compressing the shape, running put_together_same_attrs
on it, and a number of variations for how to create the wires/faces, but all of them seem to create files that report errors.
Am I doing something wrong here? Is it possible there is a bug in truck?
I guessed the co-ordinates, but this is the general gist
let points = [
// outer triangle
builder::vertex(Point3::new(0.0, 0.0, 0.0)),
builder::vertex(Point3::new(5.0, -5.0, 0.0)),
builder::vertex(Point3::new(10.0, 0.0, 0.0)),
// inner triangle
builder::vertex(Point3::new(1.0, 1.0, 0.0)),
builder::vertex(Point3::new(5.0, -2.0, 0.0)),
builder::vertex(Point3::new(9.0, 1.0, 0.0)),
];
let outer_wire: Wire = vec![
builder::line(&points[0], &points[1]),
builder::line(&points[1], &points[2]),
builder::line(&points[2], &points[0]),
].into();
let inner_wire: Wire = vec![
builder::line(&points[3], &points[4]),
builder::line(&points[4], &points[5]),
builder::line(&points[5], &points[3]),
].into();
let face = builder::try_attach_plane(&vec![outer_wire, inner_wire]).unwrap(); // I've also tried face.add_boundary(inner_wire)
let solid = builder::tsweep(&face, height * Vector3::unit_z());
use truck_meshalgo::tessellation::MeshableShape;
use truck_meshalgo::tessellation::MeshedShape;
let mesh = s.triangulation(0.05).to_polygon();
// use truck_meshalgo::filters::OptimizingFilter;
// mesh.put_together_same_attrs();
let mut out = Vec::with_capacity(1024);
truck_polymesh::stl::write(&mesh, &mut out, truck_polymesh::stl::STLType::Binary).unwrap();
Here is a minimal example:
use truck_modeling::builder::{tsweep, vertex};
use truck_modeling::{Point3, Vector3};
use truck_shapeops::or;
fn main() {
let point_a = vertex(Point3::new(0.0, 0.0, 0.0));
let line_a = tsweep(&point_a, Vector3::unit_x());
let square_a = tsweep(&line_a, Vector3::unit_y());
let cube_a = tsweep(&square_a, Vector3::unit_z());
let z_offset = 1.0;
let x_offset = 0.1;
let y_offset = 0.1;
let point_b = vertex(Point3::new(x_offset, y_offset, z_offset));
let line_b = tsweep(&point_b, Vector3::unit_x());
let square_b = tsweep(&line_b, Vector3::unit_y());
let cube_b = tsweep(&square_b, Vector3::unit_z());
let combined_cube = or(&cube_a, &cube_b, 0.01);
match combined_cube {
Some(c) => {
println!(
"combined_cube has {:?} shell boundaries",
c.boundaries().len()
);
}
None => {
println!("combined_cube: None");
}
}
}
When I run this, the output I see is:
combined_cube: None
I expected the output to be:
combined_cube has 1 shell boundaries
If I change the value to: let z_offset = 0.9
then I get the correct behavior:
combined_cube has 1 shell boundaries
If I change the value to: let z_offset = 1.1
then I get different behavior which is also correct:
combined_cube has 2 shell boundaries
It is only when I choose let z_offset = 1.0
when the None value is returned. Is this the intended behavior? How can I merge two solids which are touching but not overlapping?
Consider for README.md
in each crate cf. README.md by virtualritz
Currently the MeshableShape
implementation in Truck is quite slow if you set tol
low, but the whole time, it's only using one CPU core. Given the propenderence of iterators and maps in the function, and in the tesselation
one it calls, would something like rayon be a good solution to parallelizing the operation?
Will this be implemented or not?
I already find the Surface defination:
pub enum Surface {
/// Plane
Plane(Plane),
/// 3-dimensional B-spline surface
BSplineSurface(BSplineSurface<Vector3>),
/// 3-dimensional NURBS Surface
NURBSSurface(NURBSSurface<Vector4>),
/// revoluted curve
RevolutedCurve(Processor<RevolutedCurve<Curve>, Matrix4>),
}
In algorithms such as mesh partitioning, a reference tolerance is specified as an argument, but currently, even if a negative value is entered, for example, the process does not stop and an infinite loop may occur. The lower and upper limits of the tolerance should be clearly stated in the documentation, and an error should be returned if the value is outside the range.
I saw an example of cube-in-cube. If I want to dig out a small-cube from one side of the big-cube, how should I implement it? I haven't found the relevant method. Can you help me?
Hi!
I'm working on a 2d constraint-solving CAD, and would like to implement the ability to export a linear/lathe extrusion of the drawing as an STL or STEP file. Truck seems like the best library for the job as far as I can tell.
Can you help me work out how to do this?
I think I need to create a Wire
to represent the boundary geometry, creates two faces from this wire for the top and bottom faces, and then glue them together somehow, perhaps glue_at_boundaries
? And then presumably i need to use boolean operations to cut out interior geometry?
Lathe extrusion might be easier, if I can create a Wire
maybe I can use rsweep
?
If there's some circle curve, the UnitCircle with Processor can't be tessellated. May be the polyline is just unit circle polyline, without being transformed by processor's transform.
Here is the test file:
example_step.zip
for updating the mesh at a PolygonInstance
I do:
my_polygon_instance.swap_vertex(&mut another_polygon_instance);
for updating the vertices and indexes of a WireFrameInstance
there is no such equivalent. I tried to do it myself with something like:
my_wire_instance.vertices = another_wire_instance.vertices.clone();
my_wire_instance.strips = another_wire_instance.strips.clone();
but that is not possible, because the vertices
and strips
are private.
Am I missing something ?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.