Suggestions for a Separate Structure Workbench

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!
User avatar
amrit3701
Posts: 343
Joined: Mon Jun 13, 2016 5:37 pm

Re: Suggestions for a Separate Structure Workbench

Post by amrit3701 »

yorik wrote:The App::FeaturePython is based on the simple generic App::Feature. It basically does nothing. You need to do everything yourself in the python code.
The Part::FeaturePython is based on Part::Feature which already does a lot of things: It already comes with several defined properties such as Placement and Shape, and if you give it a shape, it will compute a coin3d representation of it automatically. In Draft.py, for example, both are used. Most Draft objects, that are based on shapes, use Part::Feature. But some others, the dimension for example, doesn't have a shape, and is therefore an App::FeaturePython.
Greetings,

Yeah :), I got your point. Which FeaturePython object I have to use for structure objects (like a column, beam, and slab ) in Arch workbench if I want predefined properties like a coin3d representation etc?

Also, can you give the link/source from where I can see all FeaturePython objects present in FreeCAD and it's respective source code?

Thanking you,

--
Amritpal Singh
http://www.amritpals.com
https://github.com/amrit3701
Amritpal Singh
Github, Like my work, sponsor me!
User avatar
yorik
Founder
Posts: 13665
Joined: Tue Feb 17, 2009 9:16 pm
Location: Brussels
Contact:

Re: Suggestions for a Separate Structure Workbench

Post by yorik »

They are spread all over freecad, and all coded in C++. This in the python console gives you the available types:

Code: Select all

FreeCAD.ActiveDocument.supportedTypes()
If your object will have a "real" solid shape, then definitely you should go with Part::Feature. The App one you should consider using only for shapeless objects.
User avatar
amrit3701
Posts: 343
Joined: Mon Jun 13, 2016 5:37 pm

Re: Suggestions for a Separate Structure Workbench

Post by amrit3701 »

Hi Yorik,

I have seen many examples of scripting object and in all the examples there is only one box (Part.makeBox()) which sets the shape of the scripting object (fp.Shape = Part.makeBox(fp.L_Length,fp.L_Width,fp.L_Height). But my need is to make two boxes having some relation with each other and then set that two boxes as the shape of the object (fp.Shape).

I just taking an example of the table to understand my problem in a simpler manner. For making a table, we need five boxes, in which one box is an upper portion of the table (slab) and the four boxes are the legs of the table. Then placing all the five boxes with some relation and set all the five boxes are the shape of a scripting object (fp.Shape).

Code: Select all

class PartFeature:
	def __init__(self, obj):
		obj.Proxy = self

class Box(PartFeature):
	def __init__(self, obj):
		PartFeature.__init__(self, obj)
		''' Add some custom properties to our box feature '''
		obj.addProperty("App::PropertyLength","L_Length","Large box","Length of the box").L_Length=2.0
		obj.addProperty("App::PropertyFloat","L_Width","Large box","Width of the box").L_Width=2.0
		obj.addProperty("App::PropertyLength","L_Height","Large box", "Height of the box").L_Height=1.0
		obj.addProperty("App::PropertyLength","S_Length","Small box","Length of the box").S_Length=1.0
		obj.addProperty("App::PropertyLength","S_Width","Small box","Width of the box").S_Width=1.0
		obj.addProperty("App::PropertyLength","S_Height","Small box", "Height of the box").S_Height=2.0		
	

	def onChanged(self, fp, prop):
		''' Print the name of the property that has changed '''
		FreeCAD.Console.PrintMessage("Change property: " + str(prop) + "\n")
		if prop == "L_Length" or prop == "L_Width" or prop == "L_Height" or prop == "S_Length" or prop == "S_Width" or prop == "S_Height":
			box1 = Part.makeBox(fp.L_Length,fp.L_Width,fp.L_Height)
			box2 = Part.makeBox(fp.S_Length,fp.S_Width,fp.S_Height)			
			fp.Shape = # How I can set box1 and box2 is the shape of fp.


	def execute(self, fp):
		''' Print a short message when doing a recomputation, this method is mandatory '''
		FreeCAD.Console.PrintMessage("Recompute Python Box feature\n")
		box1 = Part.makeBox(fp.L_Length,fp.L_Width,fp.L_Height)
		box2 = Part.makeBox(fp.S_Length,fp.S_Width,fp.S_Height)			
		fp.Shape = # How I can set box1 and box2 is the shape of fp.
Can you tell the solution of my problem? It will be better if you can give an example or code related to my problem.

Thanking you, :)

--
Amritpal Singh
http://www.amritpals.com
https://github.com/amrit3701
Amritpal Singh
Github, Like my work, sponsor me!
User avatar
yorik
Founder
Posts: 13665
Joined: Tue Feb 17, 2009 9:16 pm
Location: Brussels
Contact:

Re: Suggestions for a Separate Structure Workbench

Post by yorik »

there you have two solutions: fuse (=union, in OCC slang) or compound. Fusing is done like this:

Code: Select all

finalshape = box1.fuse(box2)
This is a boolean union. If your two boxes are interescting, they will become one single object.
The compound is just an agglomeration of other Part Shapes under one shape, with no other operation being done. You do it like this:

Code: Select all

finalshape = Part.makeCompound([box1,box2])
User avatar
amrit3701
Posts: 343
Joined: Mon Jun 13, 2016 5:37 pm

Re: Suggestions for a Separate Structure Workbench

Post by amrit3701 »

yorik wrote:there you have two solutions: fuse (=union, in OCC slang) or compound. Fusing is done like this:

Code: Select all

finalshape = box1.fuse(box2)
This is a boolean union. If your two boxes are interescting, they will become one single object.
The compound is just an agglomeration of other Part Shapes under one shape, with no other operation being done. You do it like this:

Code: Select all

finalshape = Part.makeCompound([box1,box2])
Yes, both of them works fine for me. Now, I also changed my scripted object in a workbench called as Structural workbench.
https://github.com/amrit3701/Structural

When I use "Arch.makeStructure()" function in a scripted object, why does it create a new object "Structure"? As when I change any value of a parameter of a slab (scripted object), it creates a new object. If I cannot use "Arch.makeStructure()" function in a scripted object, then can I use "Part.makeBox()" function? If yes, then how can I give all structural properties to the Part object?

Code: Select all

import FreeCAD, Part, math, Arch
from FreeCAD import Base
from pivy import coin


class Slab():
	def __init__(self, obj):
		''' Add some custom properties to our box feature '''				
		obj.addProperty("App::PropertyLength","Length","Slab","Length of the slab").Length=10.0
		obj.addProperty("App::PropertyLength","Width","Slab","Width of the slab").Width=10.0
		obj.addProperty("App::PropertyLength","Height","Slab", "Height of the slab").Height=1.0		
		obj.Proxy = self


	def onChanged(self, fp, prop):
		''' Print the name of the property that has changed '''
		FreeCAD.Console.PrintMessage("Change property: " + str(prop) + "\n")
		slab = Arch.makeStructure(length=fp.Length,width=fp.Width,height=fp.Height)
		if prop == "Length" or prop == "Width" or prop == "Height":
			slab.Length = fp.Length
			slab.Width = fp.Width
			slab.Height = fp.Height
				

	def execute(self, fp):
		# Print a short message when doing a recomputation, this method is mandatory
		FreeCAD.Console.PrintMessage("Recompute Python Box feature\n")
		#slab = Arch.makeStructure(length=fp.Length,width=fp.Width,height=fp.Height)
				

class ViewProviderBox:
	def __init__(self, obj):
		''' Set this object to the proxy object of the actual view provider '''
		#obj.addProperty("App::PropertyColor","Color","Box","Color of the box").Color=(1.0,0.0,0.0)
		obj.Proxy = self

	def attach(self, obj):
		''' Setup the scene sub-graph of the view provider, this method is mandatory '''
		return

	def updateData(self, fp, prop):
		''' If a property of the handled feature has changed we have the chance to handle this here '''
		return

	def getDisplayModes(self,obj):
		''' Return a list of display modes. '''
		modes=[]
		return modes

	def getDefaultDisplayMode(self):
		''' Return the name of the default display mode. It must be defined in getDisplayModes. '''
		return "FlatLines"

	def setDisplayMode(self,mode):
		''' Map the display mode defined in attach with those defined in getDisplayModes.
		Since they have the same names nothing needs to be done. This method is optinal.
		'''
		return mode

	def onChanged(self, vp, prop):
		''' Print the name of the property that has changed '''
		FreeCAD.Console.PrintMessage("Change property: " + str(prop) + "\n")

	def getIcon(self):
		''' Return the icon in XMP format which will appear in the tree view. This method is optional
		and if not defined a default icon is shown.
		'''
		return """
                    /* XPM */
                    static const char * ViewProviderBox_xpm[] = {
                    "16 16 6 1",
                    "   c None",
                    ".  c #141010",
                    "+  c #615BD2",
                    "@  c #C39D55",
                    "#  c #000000",
                    "$  c #57C355",
                    "        ........",
                    "   ......++..+..",
                    "   .@@@@.++..++.",
                    "   .@@@@.++..++.",
                    "   .@@  .++++++.",
                    "  ..@@  .++..++.",
                    "###@@@@ .++..++.",
                    "##$.@@$#.++++++.",
                    "#$#$.$$$........",
                    "#$$#######      ",
                    "#$$#$$$$$#      ",
                    "#$$#$$$$$#      ",
                    "#$$#$$$$$#      ",
                    " #$#$$$$$#      ",
                    "  ##$$$$$#      ",
                    "   #######      "};
                    """

	def __getstate__(self):
		''' When saving the document this object gets stored using Python's cPickle module.
		Since we have some un-pickable here -- the Coin stuff -- we must define this method
		to return a tuple of all pickable objects or None.
		'''
		return None

	def __setstate__(self,state):
		''' When restoring the pickled object from document we have the chance to set some
		internals here. Since no data were pickled nothing needs to be done here.
		'''
		return None


def makeSlab():
	FreeCAD.newDocument()
	a=FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Slab")
	Slab(a)
	ViewProviderBox(a.ViewObject)
	FreeCAD.ActiveDocument.recompute() 


makeSlab()
Thanking you,

--
Amritpal Singh
http://www.amritpals.com
https://github.com/amrit3701
Amritpal Singh
Github, Like my work, sponsor me!
User avatar
yorik
Founder
Posts: 13665
Joined: Tue Feb 17, 2009 9:16 pm
Location: Brussels
Contact:

Re: Suggestions for a Separate Structure Workbench

Post by yorik »

amrit3701 wrote:When I use "Arch.makeStructure()" function in a scripted object, why does it create a new object "Structure"?
To me, this seems the obvious thing a "makeStructure" function should do :) What would you expect it to do instead?

Ah I see what you want to achieve in your code. This is not the proper way to do it. You should instead subclassing the Arch Structure object. In other words, you create a new class using it as a base like this:

Code: Select all

class Slab(Arch._Structure):
Then you can override any of its methods.
Be careful that in python, when subclassing, it is mandatory that inside your __init__() function, you call the __init__() function of the parent class.

Check the arch _Structure code itself, it does the same way with the generic Arch Component).
User avatar
chakkree
Posts: 327
Joined: Tue Jun 30, 2015 12:58 am
Location: Bangkok Thailand

Re: Suggestions for a Separate Structure Workbench

Post by chakkree »

Test to create Slab Object.
Slab.PNG
Slab.PNG (39.51 KiB) Viewed 2095 times

Code: Select all

"""
  Slabs.py
"""

import Part,Draft
import FreeCAD, ArchComponent
from FreeCAD import Base , Vector
from math import cos,sin,radians

class Slab(ArchComponent.Component):
    def __init__(self,obj):
        ArchComponent.Component.__init__(self,obj)
        self.Type = "Slab"

        obj.addProperty("App::PropertyLength", "Length", "Slab", "The length of this slab")
        obj.addProperty("App::PropertyLength", "Width", "Slab", "The Width of this slab")
        obj.addProperty("App::PropertyLength", "Thickness", "Slab", "The Thickness of this slab")
        obj.addProperty("App::PropertyFloatList", "OffSet", "Slab", "The List of offset of this slab")
    
    
    def execute(self,obj):
        tu = FreeCAD.Units.parseQuantity
        pl = obj.Placement
        S = min( obj.Length , obj.Width)
        L = max( obj.Length , obj.Width)
        t = obj.Thickness
        #offSet = [tu('100mm'),tu('100mm'),tu('100mm'),tu('100mm')]
        offSet = [tu( '%fmm'%obj.OffSet[0] ) , tu('%fmm'%obj.OffSet[1]),
                  tu('%fmm'%obj.OffSet[2]),tu('%fmm'%obj.OffSet[3]) ] 
        
        shapes =[]
        
        concrete = Part.makeBox(obj.Length-offSet[0]-offSet[2],obj.Width-offSet[1]-offSet[3],obj.Thickness,
                       FreeCAD.Vector(offSet[3] ,offSet[2] , -obj.Thickness) )
        shapes.append (concrete)
        
        shapes.append ( Part.Line( FreeCAD.Vector(offSet[3],offSet[2],0) , 
                                   FreeCAD.Vector(obj.Length-offSet[1] ,obj.Width-offSet[0],0) ).toShape() )
        shapes.append ( Part.Line( FreeCAD.Vector(offSet[3],obj.Width-offSet[0],0) , 
                                   FreeCAD.Vector(obj.Length-offSet[1] ,offSet[2]  ,0) ).toShape() )
        shapes.append ( Part.makeCylinder ( 200 , 4 , Vector(obj.Length/2 , obj.Width/2,3 )  ) )
        
        slabGeom = Part.Compound(shapes)
        pl = obj.Placement
        slabGeom.Placement = pl
        obj.Shape = slabGeom

    


def makeSlab(length=1000, width=1000 , thickness=80):
    doc = FreeCAD.activeDocument()
    if doc == None:
        doc = FreeCAD.newDocument()
    obj=doc.addObject("Part::FeaturePython","Slab") #add object to document
    Slab(obj)
    obj.Length = length
    obj.Width = width
    obj.Thickness = thickness
    obj.OffSet = [100,100,100,100]
    obj.Label = "Slab"
    obj.Role = "Slab"
    obj.ViewObject.Proxy=0
    return obj

if __name__ == "__main__":
    TestFlag = 2
    if TestFlag==1:
        slab01 = makeSlab(4000,4000,100)
    
    if TestFlag==2:
        slab01 = makeSlab(4000,4000,100)
        slab02 = makeSlab(4000,4000,100)
        slab02.Placement.Base.x+= 4000
        slab03 = makeSlab(4000,4000,100)
        slab03.Placement.Base.y+= 4000
        slab04 = makeSlab(2000,4000,100)
        slab04.Placement.Base.x+= 4000
        slab04.Placement.Base.y+= 4000
        slab05 = makeSlab(2000,1500,100)
        slab05.Placement.Base.x+= 4000 +2000
        slab05.Placement.Base.y+= 4000 +4000-1500
        slab06 = makeSlab(2000,4000-1500,100)
        slab06.Placement.Base.x+= 4000 +2000
        slab06.Placement.Base.y+= 4000 
    
    FreeCAD.ActiveDocument.recompute()
User avatar
bernd
Veteran
Posts: 12851
Joined: Sun Sep 08, 2013 8:07 pm
Location: Zürich, Switzerland
Contact:

Re: Suggestions for a Separate Structure Workbench

Post by bernd »

Are the top faces sloped to the center of the slabs?
User avatar
chakkree
Posts: 327
Joined: Tue Jun 30, 2015 12:58 am
Location: Bangkok Thailand

Re: Suggestions for a Separate Structure Workbench

Post by chakkree »

Not a slope, but the line and circle are drawing symbol of a cast in place slab.
It should be in ObjectViewProvider.

Now, No idea about slab object. :D
Slab2.PNG
Slab2.PNG (26.07 KiB) Viewed 2088 times
User avatar
amrit3701
Posts: 343
Joined: Mon Jun 13, 2016 5:37 pm

Re: Suggestions for a Separate Structure Workbench

Post by amrit3701 »

yorik wrote:Ah I see what you want to achieve in your code. This is not the proper way to do it. You should instead subclassing the Arch Structure object. In other words, you create a new class using it as a base like this:

Code: Select all

class Slab(Arch._Structure):
The class Arch._Structure is not directly imported in the FreeCAD because class begins with one underscore is a semi-private class which cannot directly be imported. And also for importing semi-private class in FreeCAD I have a below code in "ArchStructure.py" file. After this, it's working fine for me.:)

Code: Select all

__all__ = ["_Structure", "_ViewProviderStructure"]
Is above the right way for using the semi-private class?

Here is the code:

Code: Select all

import Arch                                                             
                                                                        
class Slab(Arch._Structure):                                            
    "The Structure object"                                              
    def __init__(self,obj):                                             
        Arch._Structure.__init__(self, obj)                             
        obj.Height = 1000                                               
        obj.Length = 1000                                               
        obj.Width = 1000                                                
                                                                        
    def onChanged(self,obj,prop):                                       
        FreeCAD.Console.PrintMessage("Change property: " + str(prop) + "\n")
                                                                        
class _ViewProviderSlab(Arch._ViewProviderStructure):                   
    "A View Provider for the Structure object"                          
                                                                        
    def __init__(self,vobj):                                            
        Arch._ViewProviderStructure.__init__(self,vobj)                 
                                                                        
                                                                                                                      
def makeSlab():                                                         
        FreeCAD.newDocument()                                           
        a=FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Slab")
        Slab(a)                                                         
        _ViewProviderSlab(a.ViewObject)                                 
        FreeCAD.ActiveDocument.recompute()                              
                                                                        
                                                                        
makeSlab()
When I created rebar in my scripted object (Slab) using FreeCAD GUI it does not combine with rebar means a new object is created "rebar". Why it behaves differently from when I created rebar on the normal structural object?
yorik wrote:Then you can override any of its methods.
Be careful that in python, when subclassing, it is mandatory that inside your __init__() function, you call the __init__() function of the parent class.
Check the arch _Structure code itself, it does the same way with the generic Arch Component).
Yes, I already trying to understand workflow and code of the Arch workbench.

--
Amritpal Singh
http://www.amritpals.com
https://github.com/amrit3701
Amritpal Singh
Github, Like my work, sponsor me!
Post Reply