Generative Design with FreeCAD

Have some feature requests, feedback, cool stuff to share, or want to know where FreeCAD is going? This is the place.
Forum rules
Be nice to others! Read the FreeCAD code of conduct!
paullee
Veteran
Posts: 5097
Joined: Wed May 04, 2016 3:58 pm

Re: Generative Design with FreeCAD

Post by paullee »

This looks very interesting !

Maybe should cross-post in the Arch / BIM forum for more exposure :D
paullee
Veteran
Posts: 5097
Joined: Wed May 04, 2016 3:58 pm

Re: Generative Design with FreeCAD

Post by paullee »

Trying to understand how it works, can you elaborate slightly on the usage of the attributes ?

And reading and hopefully could understand the codes :)
llll
Posts: 173
Joined: Fri Nov 12, 2021 1:56 am

Re: Generative Design with FreeCAD

Post by llll »

I'm going to drop this here, in case there are clever people reading. Just in case there is some overlap in these ideas! I'm not sure though, as I'm not one of the clever ones ;)
https://www.youtube.com/watch?v=gcRDdYrgOhg
Description: "This course is a comprehensive introduction to intrinsic triangulations, a powerful recent technique for surface mesh algorithms. These triangulations can be used to make existing algorithms robust to low-quality data, as well as enabling entirely new geometric routines. They offer a computational connection to the rich mathematical theory of intrinsic geometry."
chrome_Hx6Og4Yk6p.png
chrome_Hx6Og4Yk6p.png (466.73 KiB) Viewed 2253 times
paullee
Veteran
Posts: 5097
Joined: Wed May 04, 2016 3:58 pm

Re: Generative Design with FreeCAD

Post by paullee »

j8sr0230 wrote: Wed May 25, 2022 7:49 pm These function calls generate examples that you can use to take a closer look at the objects. Together with the documented code in the Python module, you should be able to understand how my scripted objects work. I hope for a good exchange and a lively discussion. If you have any questions, please do not hesitate to contact me.
Noted App::LinkPython is used in your implementation. With limited python knowledge, I use the stock App:Link in Voxelisation - 2nd Prototype. Another FeaturePython object is responsible to calculate the placement and element count required. The 'output' of the latter FeaturePython are used to feed to the stock App:Link and can be used for other usecases.

Any comments on either approaches? Thanks :)

Example model file -
Links to codes in github -
https://github.com/paullee0/FreeCAD_Ske ... t.py#L1051
https://github.com/paullee0/FreeCAD_Ske ... ct.py#L558
https://github.com/paullee0/FreeCAD_Ske ... ct.py#L439
User avatar
bitacovir
Veteran
Posts: 1570
Joined: Sat Apr 19, 2014 6:23 am
Contact:

Re: Generative Design with FreeCAD

Post by bitacovir »

j8sr0230 wrote: Thu May 26, 2022 12:20 pm
There is a great follow along tutorial on programming a node editor in Python using PyQT5 on BlenderFreak https://blenderfreak.com/tutorials/node ... al-series/. I am thinking of doing the tutorial and programming everything into a FreeCAD QtGui.QDockWidget instead of a stand-alone QMainWindow. You can see a draft design in the image below.

That should be a manageable task. After that, the community could work with me to transfer the necessary OCCT functions of the part module into nodes. In contrast to microelly2's PyFlow approach, this would not be an extra program but a FreeCAD native node editor as a dock widget. This in turn would be a suitable platform for making generative algorithms available to the community as nodes, possibly better than independent macros. What is your opinion on this and why was the solid PyFlow approach not pursued further?
It would be a great addition for FreeCAD. I think it would allow to many artists to take interest in the software (currently there are almost only engineers as users). Also there are some addons that need a nodes platform to operate on the software like Topologic or Ladybug. You could help to fill the gap of these new tools and FreeCAD.
::bitacovir::
==================
One must be absolutely modern.
Arthur Rimbaud (A Season in Hell -1873)

Canal Youtube Grupo Telegram de FreeCAD Español

My personal web site
My GitHub repository
Mini Airflow Tunnel Project
User avatar
M4x
Veteran
Posts: 1472
Joined: Sat Mar 11, 2017 9:23 am
Location: Germany

Re: Generative Design with FreeCAD

Post by M4x »

Could this be used to generate a Voronoi pattern?


voronoi.png
voronoi.png (153.72 KiB) Viewed 2073 times
Source: https://openclipart.org/image/2400px/sv ... offset.png
User avatar
hammax
Veteran
Posts: 1985
Joined: Thu Jan 19, 2017 5:03 pm
Location: Ammersee DE

Re: Generative Design with FreeCAD

Post by hammax »

User avatar
j8sr0230
Posts: 140
Joined: Thu Apr 07, 2022 8:59 am
Location: Chemnitz
Contact:

Re: Generative Design with FreeCAD

Post by j8sr0230 »

paullee wrote: Sat May 28, 2022 1:26 am Trying to understand how it works, can you elaborate slightly on the usage of the attributes ?

And reading and hopefully could understand the codes :)
Of course, let me start with the DistributeOnFaceLink class.

Code: Select all

def __init__(self, obj, instance_link, face_link):
As you can see in the constructor (see __init__ declaration), besides the FreeCAD document object, you need a link to an instance (instance_link) that you want to randomly distribute on a target face (face_link). The class has the properties Face, Seed, Density, Align, UAlpha, UBeta, VAlpha and VBeta. The following code snippet shows the property declaration of the __init__ contructor function.

Code: Select all

obj.addProperty("App::PropertyLinkSub", "Face", "Particles", "Target face", 0).Face = face_link
obj.addProperty("App::PropertyInteger", "Seed", "Particles", "Seed of random distribution", 0).Seed = 0
obj.addProperty("App::PropertyFloat", "Density", "Particles", "Instance density on target surface", 0).Density = 1
obj.addProperty("App::PropertyBool", "Align", "Particles", "Aligned to target surface (true) or default alignment (false)", 0).Align = True
obj.addProperty("App::PropertyFloat", "UAlpha", "Particles", "First distribution shape parameters in u direction", 0).UAlpha = 1
obj.addProperty("App::PropertyFloat", "UBeta", "Particles", "Second distribution shape parameters in u direction", 0).UBeta = 1
obj.addProperty("App::PropertyFloat", "VAlpha", "Particles", "First distribution shape parameters in v direction", 0).VAlpha = 1
obj.addProperty("App::PropertyFloat", "VBeta", "Particles", "Second distribution shape parameters in v direction", 0).VBeta = 1
obj.LinkedObject = instance_link # Instance to distribute
obj.ShowElement = False # Default setup
Face stores the target face as a link on which the instances are to be generated. The corresponding face_link e.g. (Sphere, "Face1") must be passed as a tuple when instantiating the class. The Seed is used to initialise the random number generator. Changing the seed number generates a different random distribution of object instances on the target surface. Density controls the density of the objects on the target face in 1/m^2. By changing the Align property, the orientation of the object instances on the target surface can be set (True: object normal = surface normal at the corresponding point, False: object normal = global z axis). For the random distribution of positions on the surface domain, I generated random numbers in the definition range of the U or V coordinates. The distribution along these coordinates can be influenced by the properties UAlpha, UBeta, VAlpha and VBeta. I use a so-called beta distribution for this purpose https://en.wikipedia.org/wiki/Beta_distribution. The influence of these properties on the distribution can be seen here: https://en.wikipedia.org/wiki/File:Beta ... on_pdf.svg.
Since my scripted FreeCAD object is of type App::LinkPython, it already has the properties LinkedObject and ShowElement. In the penultimate constructor line, I pass the instance_link to the LinkedObject property, as it is precisely this instance that is to be duplicated. Lastly, I initiate ShowElement with False to avoid displaying all instances of instance_link in the FreeCAD tree.

The calculation of the positions of the object instances is done in the following function, which is called automatically by FreeCAD, e.g. after manipulation of the properties:

Code: Select all

def execute(self, obj):
First, the target face must be determined based on the reference in target_face and the boundaries of the U and V coordinates must be saved. In addition, the number of object instances to be generated is calculated using the density property. The rest of the function is only executed if the number of elements to be generated is greater than 0.

Code: Select all

# Get uv range of target face
target_face = obj.Face[0].getSubObject(obj.Face[1])[0]		   	
uRange = np.array(target_face.ParameterRange)[:2]
vRange = np.array(target_face.ParameterRange)[2:]
		 
# Genrate random uv list with beta distribution
number = int((target_face.Area/1000) * obj.Density)
Now random numbers in the previously determined U- and V-range can be determined and combined to real (u, v) coordinates.

Code: Select all

random_gen = np.random.default_rng(seed=obj.Seed)
u_list = np.interp(random_gen.beta(obj.UAlpha, obj.UBeta, number), [0, 1], uRange)
v_list = np.interp(random_gen.beta(obj.VAlpha, obj.VBeta, number), [0, 1], vRange)
uv_list = np.column_stack((u_list, v_list))
However, we need cartesian coordinates on the target surface and not coordinates in UV space. Position and normal vector at a given UV point can be determined using the valueAt(u, v) and normalAt(u, v) functions. In addition, I calculate a mask to get only the positions that are actually on the BREP surface. Finally, I get the positions, rotations and normal vectors on the target surface as FreeCAD.Vector objects.

Code: Select all

# Calculate vectors and normals on target face
vectors =[target_face.valueAt(uv[0], uv[1]) for uv in uv_list] # Random positions on uv
normals = [target_face.normalAt(uv[0], uv[1]) for uv in uv_list] # Random normals on uv
mask = np.array([target_face.isInside(vector, 0.1, True) for vector in vectors]) # True, if vector is inside face
vectors = np.array(vectors)[mask] # Random positions inside face
normals = np.array(normals)[mask] # Random normals inside face
rotations = [App.Rotation(App.Vector(0, 0, 1), App.Vector(normal)) for normal in normals] # Random rotations inside face
The last few lines of the execute function create a Placement object for each vector (for each object instance) based on the determined positions, rotations and normals and update the ElementCount property.

Code: Select all

# Generate placement list
placement_list = []
for idx, vector in enumerate(vectors):
	placement = App.Placement()
	placement.Base = vector
	if obj.Align:
		placement.Rotation = rotations[idx]
		placement_list.append(placement)	
obj.ElementCount = len(placement_list)
obj.PlacementList = placement_list
As soon as I have a few minutes of free time again, I'll explain the DualMesh class in more detail.
Before I forget, thanks for creating the cross-post. Are all my replies now automatically mirrored in this cross post, or does this just serve as a reference to this thread?
Last edited by j8sr0230 on Sun May 29, 2022 11:02 am, edited 1 time in total.
Codelink on GitHub: https://github.com/j8sr0230/codelink
Codelink on PiPy: https://pypi.org/project/codelink/
FreeCAD Nodes Workbench on GitHub: https://github.com/j8sr0230/Nodes
User avatar
j8sr0230
Posts: 140
Joined: Thu Apr 07, 2022 8:59 am
Location: Chemnitz
Contact:

Re: Generative Design with FreeCAD

Post by j8sr0230 »

paullee wrote: Sat May 28, 2022 10:35 am
j8sr0230 wrote: Wed May 25, 2022 7:49 pm These function calls generate examples that you can use to take a closer look at the objects. Together with the documented code in the Python module, you should be able to understand how my scripted objects work. I hope for a good exchange and a lively discussion. If you have any questions, please do not hesitate to contact me.
Noted App::LinkPython is used in your implementation. With limited python knowledge, I use the stock App:Link in Voxelisation - 2nd Prototype. Another FeaturePython object is responsible to calculate the placement and element count required. The 'output' of the latter FeaturePython are used to feed to the stock App:Link and can be used for other usecases.

Any comments on either approaches? Thanks :)

Example model file -
Test_ Voxel_ 7_ Link w ElementCount_ Test_Class Voxel_ r.FCStd


Links to codes in github -
https://github.com/paullee0/FreeCAD_Ske ... t.py#L1051
https://github.com/paullee0/FreeCAD_Ske ... ct.py#L558
https://github.com/paullee0/FreeCAD_Ske ... ct.py#L439
I like your implementation and to be honest I initially took exactly the same approach with two classes to position link array objects in space. However, I wanted my LinkArray to behave and look like a LinkArray from the Draft WB. The App::LinkPython object gives you the oportunity to create your very own AppLink with all the logic you need, implemented in the execute function (in just one class). By the way, it even has an appLinkExecute function, that is evaluated for every item in the LinkArray and can be used to get an even better access to all your linked elements in the array.
Codelink on GitHub: https://github.com/j8sr0230/codelink
Codelink on PiPy: https://pypi.org/project/codelink/
FreeCAD Nodes Workbench on GitHub: https://github.com/j8sr0230/Nodes
paullee
Veteran
Posts: 5097
Joined: Wed May 04, 2016 3:58 pm

Re: Generative Design with FreeCAD

Post by paullee »

j8sr0230 wrote: Sun May 29, 2022 9:34 am Of course, let me start with the DistributeOnFaceLink class.
Wow! That's a lecture ! Thanks! :D

Would make some response to your next comment before I could read through and understand all your implementation.
Post Reply