FullControlXYZ / fullcontrol

Python version of FullControl for toolpath design (and more) - the readme below is best source of information
GNU General Public License v3.0
672 stars 78 forks source link

[feature] Mesh Export #20

Open ES-Alexander opened 1 year ago

ES-Alexander commented 1 year ago

It was raised in #18 that it could be desirable to export the generated mesh of the printed extrusion, potentially as an STL file or similar.

Conceptually that should mostly involve a small amount of data wrangling to create data in an appropriate format, but I think it's important to establish the context and desired features first, so we can determine which file format(s) are most valuable to support exporting as.

A few potential considerations as a starting point for discussion:

I think we should make a list of features we care about, then turn that into a comparison table for viable file type options, and use that to determine which file types make the most sense to implement. Here's a quick first pass:

File Type Colours? Normals? Lines? Multi-part? Physical Units? Animation? File Size? Expected
Implementation
Difficulty?
STL Non-standard Required No ? Non-standard No Ascii: big
Binary: small
Easy-ish
(need normals)
OBJ Yes
(external)
Optional Yes ? Non-standard No big-ish Easy-ish
PLY Optional Optional Optional Optional Non-standard No Ascii: big
Binary: small-ish
?
AMF Optional Implicit No Yes ? No XML: big
zipped: small
?
3MF Optional ? ? Yes Yes ? XML: big-ish? ?
glTF Yes Optional ? Yes ? Yes ASCII/JSON: small-ish?
binary: small
Likely complicated
aapolipponen commented 1 year ago

I like the 3mf format. Is it something that could be used?

ES-Alexander commented 1 year ago

Sure, it seems like a 3D printing focused variant of AMF, so is at least worth considering as an option. I’ve added it to the table.

I’m not certain what the intended use case is here, so hard to judge at this stage which features are most important - it could be perfect or could be not great depending on what’s being done with the mesh.

More generally we likely want to support 3MF in at least some capacity as an export format, since if PrusaSlicer can store gcode + metadata in it then we should be able to as well :-)

AndyGlx commented 1 year ago

Thanks for raising all these interesting points. I think there will be a lot of applications in research that only require the basic geometry:

For all of the above points, the predictive quality of the structural geometry is really important. Ultimately, this might mean it becomes useful to control the structure in ways like setting extruded filaments to have a round-ended rectangle geometry or similar (I do a lot of research characterising the actual cross sectional shape of the extrudates). But, initial export functionality doesn't need to be perfect obviously! I know you mention other things related to this in another issue (drooping filaments between bridge points, etc.). The choice of file type may not affect things much if these applications only care about geometry and all file types are good enough. A closed mesh is important. It would be useful to be able to record different properties for each bit of the geometry - e.g. record extrusion width or speed at each location in the model, since these may affect the local material properties used in FEA. But this doesn't need to feature in the initial release and likely involves some thought on the difference between surface triangles and internal volumes.

FYI, related to this feature, I'm also likely to soon introduce the potential to simulate microscale interactions between filaments and the resulting geometry, as researched here and in follow-on studies. This has stl export capabilities already, but we'll likely want to like that work and this work (STL from plot) together under a single 3D-model-from-'design' umbrella.

Some other applications that come to mind are:

I'm certainly not a fan of STL files, but it's useful that they're instantly known to everyone and widely supported

ES-Alexander commented 1 year ago

As mentioned, creating a mesh export option with the current geometry should be reasonably straightforward. That said, generating meshes that are suitable for structural analysis may require some additional development on the corner handling side of things.

For reference, at the moment we've got TubeMesh (with its sharp corner diameter and 90 degree twisting limitations), and CylindersMesh (with independent, overlapping cylinders)

image

We may want to consider developing one or more of the following corner separation / rounding approaches:

image

... It would be useful to be able to record different properties for each bit of the geometry ...

Storage of reasonably arbitrary metadata on a per-point / per-segment basis should be doable with at least some of the listed formats (probably from PLY down in the current table), although depending on the coupling and relative importance of geometry vs metadata it might also be worth considering a more general file format like HDF5 (although if we go down that route we'll definitely need at least one more plain geometry format, for compatibility with other visualisation programs).

ES-Alexander commented 1 year ago

I’ve also just had the thought that a mesh intended for simulation may benefit from being subdivided along the path length.

The current approaches have triangles stretched the full distance of each tube segment (which helps minimise the number of triangles that need to be stored and rendered) whereas FEA may work better with path segments that are split up into smaller chunks, using triangles with more equal side lengths.

In a similar vein, we should consider whether the current side points generation approach is sufficient (which generates angle-uniformly distributed points on an ellipse with the specified width and height), or whether we want to focus on matching the tube cross-sectional area, and/or doing a side-length-based uniform distribution (which may be valuable if height and width are quite different).

Another possibility is detecting and merging the mesh at points where the extrusion self-intersects, although that would likely be quite an involved process, and is perhaps best handled externally in MeshLab or similar (or using an optional library dependency).

For visualisation purposes it seems fine for the existing tube classes to gain some more export methods, but if we want a different kind of generation process for simulation oriented meshes then we may wish to make an additional subclass (or multiple) for that.

AndyGlx commented 1 year ago

The corner approaches will definitely be useful. What's the difference between 'cut' and 'widen'?

I guess the storage of data alongside the geometry is a little tricky because each 'point' in the toolpath is kinda deleted as soon as the 3D geometry relevant to it is created. Each new point (for each triangle) could be assigned the supplementary data associated with the toolpath point that was used to create that triangle, although this may lead to a lot of redundant data. I guess this is the challenge of using surface triangles to store data related to volume. I'm sure there are neat ways to eliminate this redundancy though, although it's not in my area of expertise at all.

For the splitting up of the mesh, I suspect there may be well polished filters in pymeshlab or similar programs that can do this. And the advantage of allowing them to do it is that end users can apply filters to refine the mesh to their specific requirements. This will be useful if each new user may have different things they want to encourage/avoid in their mesh.

I agree that as this gets more complex, a separate class may be logical. With potentially one operation creating the raw geometry data for the toolpath which is the used by plotting and 3D model classes that transform it as appropriate. The plot may call the 3D model operation to get 3D tube data or may just directly use the toolpath data for line plots.

The current method for tube geometry creation, using an ellipse will need to change yep. For triangles, but more importantly to get a realising geometry rather than one that serves only for a nice visualization. A simple approach for the initial version of this would be to simply generate a rectangle cross section for the tube based on width and height. This will give a nice contact between neighbouring filaments (in both XY and Z directions)

ES-Alexander commented 1 year ago

What's the difference between 'cut' and 'widen'?

In terms of practical differences,

My original design intent was for the tube segment endpoints to be either pushed back along the path segment vectors (for 'cut') or pushed out along the corner tangent vector (for 'widen'):

image

'widen' is already implemented in CylindersMesh, but currently only takes a numerical separation value (or an array of values for each internal corner), whereas the idea here would be to have a special "minimal" specifier (or similar), where it calculates the minimal separation automatically (to avoid a self-intersection), based on the tube geometry.


Fair enough to your comments.

ES-Alexander commented 1 year ago

More generally we likely want to support 3MF in at least some capacity as an export format, since if PrusaSlicer can store gcode + metadata in it then we should be able to as well :-)

Following up on this, it seems I was mistaken that gcode can be included in a 3MF - the resource descriptions I had seen were actually just mentioning that both gcode and 3MF files are (independently) exportable from PrusaSlicer.

It is possible to include metadata in a 3MF file (including things like printer settings, filament type, and thumbnail images), but the metadata is tied to models rather than specific parts of the path.

AndyGlx commented 1 year ago

What's the difference between 'cut' and 'widen'?

In terms of practical differences,

  • with 'cut' corners

    • the tube segments stay true to the path segments, but
    • in very sharp corners the tube won't reach / go through the path point (because the corner is cut off)
  • with 'widen'ed corners

    • the path corner is passed ... ... ...

Thanks for the clarification. My feeling is that corners are always going to be messy and difficult to truly predict, so would avoid the 'widen' option if possible. Keeping an accurate medial axis of the lines segments seems more important since this bit are quite likely to be representative of real life results without too much error.

'Cut' seem like it could be quite become complex when the tubes are different widths/heights since the point where they contact will not be on the angle bisector. I guess that's not too complicated to figure out. A more unavoidable issue is the obvious limitation that this will always under-predict the real extent of material deposition. For paths like infill between perimeter walls in extrusion 3D printing, this could be quite a critical issues .(for visualization and for geometric-model fidelity.

'Tilt' does seem to be a good compromise, but I understand that is naturally more complex. And with that, comes risk of particular type of toolpath not being envisaged by us.

Adding extra points seems like it could be justified based on my thoughts at the moment. It seems like it encorperates the best attributes from all options. And potentially really simplifies or enhances things when it comes to varying tube geometry... i.e. for a path with two tubes, one wide and one narrow, the almost full-length of those two tubes can be given the correct geometry, and the all the deviation can occur between the 'end' of each line and the new point, which has average width and height of the two lines. There is obviously a computational hit for adding those extra points. I don't mind the over-extension of corners with flow_tube. They're an understandable error, so the designer can factor them in naturally. So, the designer can do 90% of the work with flow_tube and then switch to a more accurate but time-consuming corner representation when it's necessary (maybe only one, when they're finished all their visual checks).

You mentioned '90 degree twisting limitations' in an earlier post in relation to tube_mesh, what is that limitation?

ES-Alexander commented 1 year ago

… would avoid the ‘widen’ option if possible.

Agreed - it’s not a very logical approach for realistic corners, but it was super easy to implement for a fixed offset and can be useful for staggering width changes in collinear path segments, which I was playing with a bit in initial testing. I imagine it will go unused in fullcontrol, so I might remove it once a more realistic corner type has been implemented.

‘cut’ with an extra point on the corner is likely fine for most real-world use-cases, but we can consider more points or the ‘tilt’ approach if there’s genuine value in extremely sharp corners that can’t just be specified with an extra input point (personally I don’t expect that to be relevant for anything other than a 2D plotter, since extruding into the same volume that was just filled is generally ill-advised for a practical machine).

You mentioned '90 degree twisting limitations' in an earlier post in relation to tube_mesh, what is that limitation?

I’ll likely need to set aside some time to investigate that to even be able to explain it in an intuitive way (and figure out whether it’s resolvable, and whether it’s reasonable to do so).

As a rough initial attempt, my current understanding is that particular successive rotations can cause the tube to get twisted, which can be (and is) compensated for if it’s detectable, but at 90 degree turns there’s an effect akin to gimbal lock whereby it becomes ambiguous which orientation the tube is in at the corner, so detecting a twist would need to consider multiple points before and after the corner (instead of just the immediate incoming and outgoing vectors, which is the current approach). Mathematically that ambiguity comes about because orthogonal vectors have a cross product of 0, which has no sign/direction.

Unfortunately with only one or two points per corner it’s possible to have successive 90 degree turns, in which case there may not be an upper bound on how many points need to be considered to determine whether the tube is twisted. That problem may solve itself with 3 or more points per <= 90 degree corner - I haven’t thought about it or tested things enough to find out.

nodtem66 commented 1 year ago

@ES-Alexander Have you ever seen this: https://pymesh.readthedocs.io/en/latest/wire_mesh.html It might have some codes that you're looking for.

ES-Alexander commented 1 year ago

@nodtem66 that's not something I'd come across before, and the paper made for an interesting read - thanks for sharing! :-)

From my reading of the algorithm used there it doesn't seem concerned with maintaining tube orientation (which is a nice feature of our current approach), and is designed to be structured around a network of multi-connected nodes rather than a single tubular mesh around an extrusion path. The network approach may create smoother results for lattice structures and the like (if path intersections are pre-computed), but doing so inherently loses some of the disjointedness that's maintained by following a single extrusion path (which may be important for realistic simulations of layer adhesion being weaker than the base material strength, for example). I'm not sure which type of result would be preferable for practical use, but my current approach has been to strictly follow the gcode rather than trying to create a more general mesh around the final shape.

From an implementation perspective, the convex hull approach for the corners is neat but not necessarily realistic for a plastic extrusion (since the mesh touching the corner is not the same as the extrusion passing through it), and I can't see an obvious way it could be implemented with vectorised operations using just Python+NumPy, so for performance reasons we would likely need to add PyMesh as a project dependency if we want to use that mesh generation approach (since it implements the performance-critical components in C++), which may cause compatibility issues or installation difficulties across different systems.

At this stage I'm personally more interested in implementing a reasonable/more realistic corner approach within the existing tube mesh generation structure, so that's likely what I'll work on in the time I'm able to make, unless we decide through discussion that doing so is not worthwhile compared to just integrating and making use of PyMesh instead. That said, the PyMesh approach is definitely worth consideration, and some discussion of what we actually want from a mesh (which is the point of this Issue). At minimum it's a fallback option, and it may turn out to be the best approach going forwards - thanks again for the suggestion :-)

ES-Alexander commented 8 months ago

25 has been merged, but there are some outstanding features that are yet to be implemented, which I'm copying here so we don't forget about them:

ES-Alexander commented 2 months ago

Dropping a note so I don't forget about it:

OpenUSD is another export format that may (or may not) be worth pursuing, and could be relevant to #69. I came across it in this video.