-
Notifications
You must be signed in to change notification settings - Fork 53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature Add to resolve #16 #21
Comments
If you're trying to slice a CSG into a planar polygon, how are you going to account for the inherent loss of geometry detail when projecting a 3D object onto a 2D plane? Simply taking a thin slice and performing a projection transformation on that isn't going to carry over detail in the thickness of that slice. If the slice was infinitesimally thick, then you'd end up with an infinite number of slices, but no loss in detail (and for obvious reasons, this isn't a practical solution). |
Here is my first thoughts, this won't be the most efficient, but will be a good starting place. Take your part and transform it by the inverse of the cut plane (this should be changed to Transform from Plane maybe?). Then make a intersection of a cube whose dimensions are the x and y min/max values and the thickness in the layer height. Then you can go through the polygons of the remaining shape and prune out any polygons that have a point where z is not 0. This will give you back the polygons of exactly the shape at the cut plane. |
As for the detail loss, this is inherent in machining and manufacturing of all kinds, what you make is never actually what the computer defines. The computer can compute shapes to any arbitrary precision. The slicer, based on the 3d printer or milling settings, will step through the piece at whatever compromise between precision and machining time the user prefers. Bis trade off will be done at the layer above this function, and should not effect the data that this code is given or generates. |
Hopefully after years of experience with these things @sjkelly might have some insight on this as well. |
As you can see in my latest commit, I changed the ISlice interface to take a slice plane input as a double. Is a Transform really necessary? If you just want to specify the z coordinate of the slice plane, this seems to do the job just fine. However, if you want to slice on some weird axis, I can see a Transform being necessary. |
I'm ok with that, but i can imagine a few cases where I would use this to slice some weird plane, like the plane where the servo connects to the body? https://github.com/madhephaestus/BancroftMakerspaceProject2016/blob/master/bodyGenerator.groovy#L111 is how to move a CSG using just the transform object |
For reference, I added the Transform method. I'm scaling it by the inverse of the current scale so that the scale doesn't mess up the matrix inversion. Is this the right thing to do, or can I ignore scale in the matrix inversion? |
It just seemed unnecessary; however, if you think a general Transform would be useful, I can keep the latest commit. |
Instead of making a giant cube (which may not be giant enough), why not use the introspection of its own size: http://neuronrobotics.com/JavaCAD/Measurment/ that way the cube if set to the bounds of object it will always be exactly the right size. |
I figured having a |
Since the values of z are floating point numbers you can not check ==0. You should do a bound check (or make a bound check function) to check if a value is +- some value simply int casting at this point can cause some surprising results, so always do the bound check. also when doing long stream calls, flatten them to new lines each time you do a method call. Or let the IDE auto-format the section. |
I think that I can check at == 0 because I'm the CSG used as the slice plane has a face at z == 0, is this still not correct? I tested this algorithm the other day and it worked for my test object. If I use two bounds instead, then what would those be? |
+- 0.001 mm is good, it just catches datatype clipping. Just because you computed something that should be a float at 0 doesn't mean it is... |
You should add unit tests for a shape with a hole in it. You should include some convex shapes as well (like the letter C). How might you verify that the shapes that your algorithm gives back are "correct"? Can you make a test that checks the points and fails if they are wrong? A cube with a smaller cube cut from its center would make a good cross section, and would be easy to calculate what those 8 points should be. Maybe making an export to DXF and loading in the DXF might be a good way to test? |
Is there an easy way I can view the CSG's I'm getting from these tests? I tried outputting to an OBJ or an STL string, putting that into a Gist, and then loading that Gist with Bowler Studio, but both formats have errors. |
I put the OBJ into Blender, and it's being imported correctly. Perhaps this is a Bowler Studio error? |
the STL should have worked, except its a binary file format, so you have to use the code to write the file. You can also open stls in Cura to quickly look a them. InkScape is a good way to look at DXFs Once you get a look at whats coming out, you should start with unit tests of what the correct data should be. This is called Test Driven Development. You then adjust your algorithm to pass the tests. It means you are always building testing into your code as you go which is a good habit to have. |
I'm not getting any faces, though. I'm getting all the points needed to describe the slice (plus some extra in one test, but this is out of my control), but no faces are being generated. I think this is also out of my control, as |
Hmm, we need to change the return type to note there are helper functions to detect winding order: https://github.com/Octogonapus/JCSG/blob/master/src/main/java/eu/mihosoft/vrl/v3d/Extrude.java#L184 |
this is looking good! |
I like the list of polygons as a return better, since the test for winding order is that data type. |
Can you explain to me this inside/outside feature business? It's not making much sense to me since we're talking about a 2-D plane. |
Polygons are defined by having a encapsulated space that is "inside" the polygon. To denote that this, a polygon defines the outer boundary using counterclockwise winding order. Now, some polygons are meant to represent "holes" in other polygons. "Holes" are polygons with clockwise winding order. This also means that any "Hole" bust be entirely inside a non-"hole" polygon. A place this is used is when defining tool paths, and most importantly when doing insets, this definition is critical. A CNC tool must ride outside of the part, not along its "line" because the tool has some thickness to it, and it needs to know what part is the outside, so it knows where the tool can go. For insets, the inside is the direction that the surface of all the lines move (in or out). In fact, the best way to calculate the inset is to simulate a circle moving tangent to the line (step by step in tiny increments, those increments being something like the minimum step size for your printer), and calculate its center as it moves tangent to the lines, then switching to a new line when the circle intersects another line. Like this object: https://gist.github.com/07f489d46d23ceac8a1e8b3779719e9b |
here is a nice graphic: http://flylib.com/books/2/124/1/html/2/files/figure12-6.jpeg |
So any polygon which has a hole needs to be wound clockwise? I have a way to detect holes Euler's polyhedron formula, but I need to get an edge count for it to work. Know of any good way to get edges? |
so in this object: https://gist.github.com/07f489d46d23ceac8a1e8b3779719e9b Will produce 2 polygons. The large polygon (40 mm on a side) will be counter-clockwise, and the smaller one, (20mm on a side) will be clockwise. |
Hows it going? |
I haven't made much progress on the whole winding thing. I picked up a job since I last worked on this and have been busy since. I really don't know how to detect winding-dependent features out of an array of vertices. If I did, then this wouldn't be a difficult implementation. |
I'm not sure there is anything wrong in the implementation, it just needs a bunch of unit tests to verify that what it's giving back is what you expect it would. It may be generating triangular decomposition of the shapes, which might be whats throwing you off. After you have the list of all polygons that are at z0, you then go through them to create the actual polygons you intend to return. You will use the brushfire search to search through the points and form the outer polygon first. Start at a point outside the polygon and calculate an ever increasing circle until the first point in your list is inside the circle. thats a point that is on the outside of the final shape. Start there and do the same increasing circle trick to find the next point on the outer surface. To figure out if its the next point, you check to see if a point just to the right of the line is outside, and just to the left is inside (be careful for edge cases, like straight, folded over and 90 degree angles) . In this process you make a new list for this polygon. The polygon is done when you get back to the starting point. If there are points left in the original list (assuming you pop out the points as you add them to the outer polygon), you repeat the process until all points are pulled out of the original list. Once you are done, and all the polygons are now the outer surfaces rather than the triangle decomposition, you check each polygon to see if all of its points are inside any other of the polygons, and if so, use the existing API to reverse the winding order for that polygon. To test if a point is inside a polygon, and there a bunch of tricks for that: http://www.blackpawn.com/texts/pointinpoly/ Heres another track you could explore. Convert the layer to an image (using some JavaFX API's) from the layer you are looking at. Maybe color it black and contrast it with a white background then make a png of the image of the cross section. Then hand that to OpenCV to process into the list of polygons (it has a function that takes images poops out the polygon data we want) This would need to be done in bowler-scripting-kernel or in BowlerStudio as a script since JCSG doesn't have Opencv (and its not trivial to just add in a way that deploys nicely) |
I found this library that may be very helpful in converting the flat versions into paths. https://github.com/madhephaestus/imagetracerjava I took a quick stab at using it: https://github.com/NeuronRobotics/BowlerStudio/blob/development/src/main/java/com/neuronrobotics/bowlerstudio/threed/SVGFactory.java but it needs lots of fine tuning to generate a sane input png. |
https://github.com/madhephaestus/imagetracerjava works however it is a very slow and not very precise (converting to image then generating vector from images) I'm losing at least +-.5mm just in the conversion and it takes many seconds to complete. BUT, if you read the algorithm this code is using and search through your vector lists instead of searching through the pixel image like he is, then your version could be SUPER fast and exact. |
@pseudoeuclidean THis issue might be a good place to start with a slicer function. |
@pseudoeuclidean I took a close look at the coincident edge detection and found an issue. There are situations where one edge will be totaly overlapping with a second edge, but that second edge is longer than the first one. If the long edge is erased then the there is a mesh gap. If the the small one is erased, then the long edge cuts through the polygon forming an invalid shape. What will need to happen is when an edge is partially overlapping, then the long edges need to be broken into short edge sections and the triangulation needs to be re-run to deal with the new edge structure. I think this is why the edge pruning is failing. |
In this feature a function should be added that takes in a CSG and a slice plane and returns a list of polygons of all the features at that plane. This would add the feature to resolve #16
The text was updated successfully, but these errors were encountered: