Repository navigation
Conversation
|
@phkahler great to see you attempting this. Over the years I've been considering this - most recently at the time Dune3D started. In my head it works similarly to what you started outlining with one more level of abstraction: A chamfer/fillet is:
In the future the patch class can be used to implement generic patch functionality - e.g. close a "random" hole outlined by a contour of any edges (not necessarily straight) in 3d. A general purpose patch tool may/will have to generate new edges on it's own (imagine a corner where a few edges meet and they are all filleted) and I am not sure how this will be handled by this approach. Intersections of the newly created NURBS surfaces will be these new edges. |
Woops you're right. Curves are not duplicated, but there are two TrimBy, one for each surface that meets along a curve. These reference the same underlying curve but one is "backward". So I'll still need to generate 2 new curves, and move both Trims rather than creating a new one. Good news - the PWL points are not in the trims! Assuming all fillets meeting at a point are the same radius, the surface we want there is spherical. It's really hard to find anything (with google) on spherical NURBS patches, never mind triangular ones. But that's for later. Chamfers first and Fillets with 1 or 2 per vertex. I'm both excited and dreading this... |
Indeed :-) |
|
I very welcome the attempt to add this. :) |
|
Getting back to this soon. Here is a sketch to visualize how fillets meet at a corner. Drag the green lines around. For equal radius fillets it forms a spherical triangle. |
|
So, is anyone feeling brave enough to pick this up during the days off and actually push the development forward? |
|
Haha @dodox1 brave is right. Anyone is welcome to attempt this, and I'm happy to answer questions on how the internals work. Such a person would become highly knowledgeable in the solvespace NURBS code. Unfortunately my attempt last year didn't pan out and I wasn't even able to make partial shell modifications without problems. It really takes a deep dive into it to make progress. Next time I try I might limit myself to flat surfaces only just to get the basics down. |
93215ea to
bc2bca8
Compare
|
Sorry about the delay. This is now turning into a monster project. With all the time I had to think about it the implementation is going well. The code is in a testing state. Every shell potentially gets modified. There is a function to determine which edges get modified and it is over 200 lines long in order to specifically modify certain edges of specific test models. If it returns a positive number, that is a radius to apply to an edge. If it is a negative number that will produce a chamfer that cuts as deep as the fillet with the absolute value of that number. If you want to play, just have it return +3 and do an extrusion, or even a skewed extrusion. A vertex with 3 chamfers will get a little triangle patch. A vertex with 3 fillets will get a little spherical triangle patch - my scheme for that works well and in all cases. There is a lot not possible yet: I'd like to wrap up the shell modifier code this weekend, and then get to work on linking sketch entities to shell edges so we can implement the UI for selecting edges and modifying them. |
…ting chamfers and fillets. The function is hooked in Group::Generate but does not modify the shell at this point so it is benign.
…le at the origin and extruded.
… parameter range. Works on rational curves, so arcs stay exact.
…r faces. Edges are picked by the GetEdgeModifier() test hook, which selects one top edge of a +/-10 cube.
…linder end. Test hook now also picks the concave edge of L_shape and the top circle of test_cylinder.
…er). EdgeMod now indexes faces by side so either face can be the shared one. Test hook picks all four outer edges of the z = 10 face.
…illet ends at a cross-section; the corner patch is left out for now, so the hole is open (naked edges). Test hook adds the cube's x = 10, y = 10 edge.
… (sphere) are still left open.
…lpha/2), cross-section weight sin(alpha/2). Chamfer corners may be non-square. Test hook picks the house_test eaves (135 degrees).
…eighbors. Test hook covers arc_test.
… toward the ball, find its center (spine), contacts and the middle row where the contact tangents cross. Same results as before for plane-plane edges; also handles a cylinder whose axis runs along the edge.
… except the bottom: seams between plane and cylinder, and corners of an arc, a line and a seam.
….cpp): three bands, each a side arc swept about the axis through the ball's center along its chord, trimmed by arcs to the triangle's incenter. No collapsed edges.
…section points along the edge (a line, or around the arc's axis) to the first crossing of the partner fillet or face, found from signed distances. Replaces the straight-only ray tracing for miters. Test hook: arc_test variants 2 and 3.
…faces) with SSurface::MarchIntersection(), the Boolean march, instead of tracing them by sampled crossings. The crossing search stays only to find where a miter switches to a face cut. Removes the ray tracer.
…raight line running away from the edge with the same normal along it, such as a cone on a revolved part. Test hook: revolve_test.
…ofiles give: offset in the edge's plane by the Tiller and Hanson construction. Circular arcs now need the right middle weight. Snap near-tangent joins together. Curved edges meeting an end face at an angle are marched. Test hook: bezier_test, text_test.
|
One option for the edge selection UI, from what worked for me in a fork: |
Thanks @dodox1 I like the idea of face selection. Where did you store the face selection and radius info? What I was planning to do is tweak the shell generation to tag edges with the handle of the sketch entity they were created from, or correspond to. There are two things to overcome first. One is that revolve groups don't create arc entites at all. #1631 is an attempt to add them. The bigger problem is that most 2D sketches generate redundant entities when you extrude, revolve, or lathe #1642, so we need to pick only one of the two points to create a line or curve from and apply its handle to the generated curve(s). Beyond that, not all shell edges have entities over them - where surfaces intersect in booleans is an obvious one. Having the ability to select a face and fillet all its edges would be a nice feature and could cover some of these edges too! A less appealing option might be to have a group parameter "fillet radius" specifically for intersections during booleans. |
|
Per group: a list of faces, each a point on the face plus its normal, and the For the booleans: faces should cover intersection edges too, since such an Here is how the face selection works in my fork: https://youtu.be/YbZ7HQgTWbs |
|
An idea I arrived at for naming edges, just as a suggestion: store the entity |
|
X Off topic and at the same time very much on topic :-) CLICK MEAs I mentioned a while ago somewhere - once the the vibe coding starts "working" there will be many forks :-) Here is another where fillets and chamfers are "implemented": https://github.com/joernfeuerabend/solvespace
This is better that the original vibe coded attempt by g19fanatic (#577 (comment)) but the "handle" in screen shot above is a bug (just like in the original vibe coded attempt) @joernfeuerabend. @phkahler One interesting change in that fork joernfeuerabend/solvespace@2b096c7 may be here : The involute gear in that fork is also rather interesting/amusing:
Who wants to draw all that stuff by hand anyway https://www.printables.com/model/84042-involute-gear-designed-in-solvespace. On the other hand this is a rather "high level" primitive for the SolveSpace "minimalist ideology" :-) Since Claude Fable in May 2026 the LLMs have become truly interesting :-) My only "worry" is: "Who is ever going to read all that code?" :-) Even more off topicI like the @g19fanatic and @joernfeuerabend forks much more than the NeoSolve / @dodox1 because they add the feature to the SolveSpace NURBS/geometric kernel instead of integrating Open Cascade Technology (OCCT). I do not like Open Cascade - the code is "awful", huge and old (like most commercial CAD kernels). |
|
@ruevs I know Open Cascade is getting very popular - some slicers even use it now to read STEP files. Almost every other open source project that handles STEP files uses it (gcad3d being an exception). I had no idea anyone vibe coded fillets for solvespace. BTW A gear tool has also crossed my mind. I'll have to look at that one. There are a few other things on forks that we might want to bring upstream. |
It's the only "fully" functional option - until we end up with:
You spend too much time vibe coding and not enough time snooping around, and I do the opposite ;-) :-D SolveSpace Forks:
Other vibe coded CAD:
As I said - who is ever going to (carefully) read all that code :-) I started contributing to SolveSpace because of the beautiful, clean, hand crafted, coherent, compact and "opinionated" code, that @jwesthues had written back in 2008. And the "control freak" in me wants to read (and undersyand, and agree with) every line of AI generetad code - to make sute it maintains the "vibe" (pun intended). But I don't have the bandwidth - I'm slow - my brain runs on only ~200W and the cache/RAM is too small (the HDD is OK) :-D |
…f text_test. Find where two flat curves (quadratic, cubic or arc) cross, searching well past their ends. Allow miters at reflex corners with curved faces when both cross-sections are equal: the miter runs straight from the corner to the third edge.
… MITER end, and the unused EdgeMod::orig. Remove the never-used miter branch of MakeEndCurve() and PathAt(), a wrapper around FollowEdge(). Flip fillet surfaces and sphere bands with SSurface::Reverse(). No change in behavior.
…ball cross-section, the vertex cases as they are handled now, and the steps of ModifyEdges(). Fix a comment warning from a trailing backslash. Comments only.
… out of chamfer.cpp into fillet_test.cpp, since it will be thrown away once edges can be picked. No change in behavior.
…s Boolean bookkeeping, and a later Boolean skips curves tagged that way when it looks for an exact trim curve where a surface is tangent to a plane (surfinter.cpp, issue solvespace#1291), which is what a fillet's offset curves are.
|
@ruevs When this is done I'm planning to squash it all into one or two commits. If you want to see the current state of it that way I have a separate squashed branch here The file fillet_test.cpp will go away completely. That is just how claude is finding specific edges in our test files to apply fillets to. Some of the changes are bug fixes or enhancements similar to what you put in a PR for proper handling of planar surfaces, and may even overlap. I'm with you on the vibe code review problem. One of my claude logs without the tool calls is 135K, this is not just handing the task over to AI and forgetting about it, it's hours of interaction. That said, the code readability seems lower that human written. This feature will be mostly located in a couple new files, so I think it'll be worth it. It reminds of when I wrote the IDF file linking code - I wasn't happy with it, but it's all contained in one file. Also might be a good idea to push this before it's fully functional. Maybe some of the other vibe coders want to experiment with the UI for applying fillets and chamfers! This just modifies an existing shell, so if we don't call it, it won't do anything ;-) |
…t each piece on its own. The Tiller and Hanson offset is exact only at a curve's ends, so the shorter pieces follow the true offset much more closely: bezier_test is within about 0.4% of the exact fillet volume, from 8-9% too much. The pieces meet at a new JOIN end, handled like a tangent end. NeighborMod() picks the piece that ends at the vertex, and the first piece takes over the face trims while the others add theirs.
…ead of doing none. ModifyEdges() is split into CollectEdges(), SetUpEdges() and BuildEdges(); the last two report every edge that fails at a step, those edges are dropped (one per vertex per pass, so a corner's other edge gets another try) and the rest set up again. Refuse a reflex corner on a curved end face, where the neighbors would be extended past the end of the face's surface.
…s indexed by curve handle, instead of rescanning every trim for each replaced curve; the commit step on 650 edge pieces of text drops from 15 to 3-5 ms. Test hook: arc_end_test, a 120 degree circular segment closed by a chord, whose top arc fillet ends on the chord's wall at an angle (marched; within 0.03% of the exact volume). Update STATUS: that case works, and sharp text notches are left out by decision.
…t steps and testing notes; they stay on the fillet-dev branch. SShell::GetEdgeModifier() is a stub that picks no edges, and the call to ModifyEdges() in Group::GenerateShellAndMesh() is commented out, until edges can be picked in the user interface. The design notes in chamfer.cpp end with a short list of limits.
|
This PR is still here for anyone wanting to see the step by step development via commit messages. But the squashed commits are now in PR #1815 |
|
I'm going to close this. Probably next week I'll move the other PR to a branch. Next steps will be figuring out topological naming for shell edges which will be all internal stuff, hopefully not very complicated. |




Just an outline at this point (in the code). Hoping to attempt this over the hollidays. Thought people might want to follow along or comment if this goes anywhere.