Here I show my modest attempt to create such functionality for FreeCAD, in a very early stage of development.
I've divided the algorithm in three stages:
Part 1: Explore the shape
Part 2: Get the particular geometry of every face
Part 3: Unfold
Once it works correctly, I will try to code a modelling tool to add walls, perforations, and standard elements. (UPDATED: See sheet metal idea part 2 for improved algorithms and further info )
Part 1
As said above, here I`ll try to filter an input shape that meets some conditions, let's go.
First: create the 3d object
The sheet object is created manually at the moment. In a future, a specific tool can be developed to make it easier. If you want to jump this step, get the final model here
In a new document, create a sketch on XZ:
What we are going to build there is the profile of the folded sheet, so thickness and the bending radius are set here.
We can start by drawing the folds:
They must be concentric and the inner one should measure the bending radius.
Now we draw the rest of it:
Applying the correspondent constrains you should obtain something like this:
Where we set the thickness of the sheet to 2 mm and the bending radius to 8 mm.
Now, extrude the sketch:
For example, 100 mm.
Now create a hole on top and cut the sheet to change its square form:
Pocket the sketch and this is the result:
The algorithm:
The algorithm works by exploring the shape with some variables in mind, like bending radius, thickness, 90º degree bending angles and that the part is based on XY plane.
Use it by selecting the shape on the tree-view and copy-paste the code.
Import the libraries:
Use it by selecting the shape on the tree-view and copy-paste the code.
Import the libraries:
import Part import math as mt
Basic definitions needed:
Thickness = 2.0 BendingRadius = 8 k = 0.33 Alpha = 90.0
Get user selection:
SObj = Gui.Selection.getSelection()[0] SObj_Shape = SObj.Shape
Create empty lists:
Faces = [] FlatFaces = [] CylFaces = []
Get all faces of the selected object and gather them in the list "Faces"
for i in SObj_Shape.Faces: Faces.append(i)
Classify the gathered faces by being flat or cylindrical:
for i in Faces: Surface = i.Surface if str(Surface) == "<Plane object>": FlatFaces.append(i) if str(Surface) == "<Cylinder object>": CylFaces.append(i)
At the moment we have all the faces of the shape classified by being cylindrical (bends) and flat.
The next step is to remove the faces marked on the picture, because we do not need them
To do it:
RemoveFaces = [] for i in FlatFaces: for n in i.Edges: Len = n.Length if Len > Thickness*0.99 and Len < Thickness*1.01: RemoveFaces.append(i) break
It searches for faces which have one of their edges equal to the sheet thickness (with a tolerance, to ride off floats inaccuracy) and appends them to the new list RemoveFaces.
for i in RemoveFaces: FlatFaces.remove(i)
With that sentence the non desired faces are removed from the main list "FlatFaces"
The next faces to remove are the parallel ones, we need only one of them:
This works this way:
-Get the center of mass of a face
-Get the center of mass of another face
-Are them separated by the sheet thickness?
-If they are, append one of them to RemoveFaces
RemoveFaces = [] for i in FlatFaces: C1 = i.CenterOfMass for n in FlatFaces: C2 = n.CenterOfMass V12 = C2 - C1 M12 = abs(V12.Length) if M12 > Thickness*0.99 and M12 < Thickness*1.01: FlatFaces.remove(n) break for i in RemoveFaces: FlatFaces.remove(i)
To finish this post (I've more coded, future posts about this will come ;) ), a test to see what is in the list "FlatFaces":
def TESTF(FlatFaces): for i in FlatFaces: center = i.CenterOfMass Origin = center Origin_Vertex = Part.Vertex(Origin) Origin = App.ActiveDocument.addObject("Part::Feature","Test_Point") Origin.Shape = Origin_Vertex Origin_User_Name = Origin.Label FreeCADGui.ActiveDocument.getObject(Origin_User_Name).PointColor = (0.33, 0.00, 1.00) FreeCADGui.ActiveDocument.getObject(Origin_User_Name).PointSize = 5.00
The function input is a list containing faces. It draws a point at the center of mass of every face of the list, and if we apply it to our "FlatFaces" list we obtain:
That means we had a success at filtering the input shape!
Next steps are gather what is inside face (hole, squares...) and unfold.
Part 1 complete code:
""" Javier Martinez Garcia, 2014 """ import Part import math as mt Thickness = 2.0 BendingRadius = 8 k = 0.33 Alpha = 90.0 SObj = Gui.Selection.getSelection()[0] SObj_Shape = SObj.Shape Faces = [] FlatFaces = [] CylFaces = [] for i in SObj_Shape.Faces: Faces.append(i) for i in Faces: Surface = i.Surface if str(Surface) == "<Plane object>": FlatFaces.append(i) if str(Surface) == "<Cylinder object>": CylFaces.append(i) RemoveFaces = [] for i in FlatFaces: for n in i.Edges: Len = n.Length if Len > Thickness*0.99 and Len < Thickness*1.01: RemoveFaces.append(i) break for i in RemoveFaces: FlatFaces.remove(i) RemoveFaces = [] for i in FlatFaces: C1 = i.CenterOfMass for n in FlatFaces: C2 = n.CenterOfMass V12 = C2 - C1 M12 = abs(V12.Length) if M12 > Thickness*0.99 and M12 < Thickness*1.01: FlatFaces.remove(n) break for i in RemoveFaces: FlatFaces.remove(i) def TESTF(FlatFaces): for i in FlatFaces: center = i.CenterOfMass Origin = center Origin_Vertex = Part.Vertex(Origin) Origin = App.ActiveDocument.addObject("Part::Feature","Test_Point") Origin.Shape = Origin_Vertex Origin_User_Name = Origin.Label FreeCADGui.ActiveDocument.getObject(Origin_User_Name).PointColor = (0.33, 0.00, 1.00) FreeCADGui.ActiveDocument.getObject(Origin_User_Name).PointSize = 5.00 TESTF(FlatFaces)
Feel free to criticize or point out anything you consider ;)
Bye!