Pages

Saturday, November 19, 2011

Scripting example: Wankel block

Wankel engine shape is good to show some FreeCAD scripting capabilities. I will show Wankel block creation in two ways:
  • simple but uneditable,
  • slightly more complicated but fully real-time editable by values in the Property view fields.
Wankel shape is based on the epitrochoid. What we have to do:
  1. Create lines with start- and endpoint positions based on the epitrochoid equations.
  2. Connect lines in a one closed polyline.
  3. Upgrade polyline (wire) to face.
  4. Cut epitrochoid face from rectangle face (for simplicity rectangle is the external shape of our block).
  5. Extrude result face in a solid.

Non-parametric way:

Open a new document. Put below script in the Python interpreter line or create a macro. (Download macro)

from __future__ import division # allows floating point division from integers
import FreeCAD, FreeCADGui, Part
from FreeCAD import Base
import math
steps=360 #number of polyline segments
dang=math.radians(360/steps) #step angle
e=10 #eccentricity
r=60 #radius
ang=0 #start anle
z=0 #line z coordinate
halfw=r+e+20
halfh=r+e+20

for i in range(steps): #make many lines and connect they in wire (polyline, epitrochoid)
    if i==0: #for first line
        x1=e*math.cos(3*ang)+r*math.cos(ang) #coords for line startpoint
        y1=e*math.sin(3*ang)+r*math.sin(ang)
        ang=dang
        x2=e*math.cos(3*ang)+r*math.cos(ang) #coords for line endpoint
        y2=e*math.sin(3*ang)+r*math.sin(ang)
        seg=Part.makeLine((x1,y1,z),(x2,y2,z))
        wire2=Part.Wire([seg])
        x1=x2
        y1=y2
    else:
        x2=e*math.cos(3*ang)+r*math.cos(ang)
        y2=e*math.sin(3*ang)+r*math.sin(ang)
        seg=Part.makeLine((x1,y1,z),(x2,y2,z))
        wire2=Part.Wire([wire2,seg])
        x1=x2
        y1=y2
    ang=ang+dang #increment angle

edge1 = Part.makeLine((-halfw,halfh,0), (halfw,halfh,0)) #lines needed to create rectangle
edge2 = Part.makeLine((halfw,halfh,0), (halfw,-halfh,0))
edge3 = Part.makeLine((halfw,-halfh,0), (-halfw,-halfh,0))
edge4 = Part.makeLine((-halfw,-halfh,0), (-halfw,halfh,0))
wire1 = Part.Wire([edge1,edge2,edge3,edge4]) #rectangle
face1 = Part.Face(wire1) #face from rectangle
face2=Part.Face(wire2) #face from epitrochoid http://en.wikipedia.org/wiki/Epitrochoid
diff = face1.cut(face2) #boolean cut epitrochoid from rectangle
ext=diff.extrude(Base.Vector(0,0,30)) #extrude the cut (face)
Part.show(ext) #show extrude in FreeCAD window


You should see Shape object in the Tree view and Wankel block model in the view area. You can click Fits the whole content of the screen button to tune view area. Unfortunately the Shape is not editable:(


Parametric way:

Open a new document and create a macro from below code (download macro):

from __future__ import division # allows floating point division from integers
import FreeCAD, Part, math
from FreeCAD import Base

class WankelBlock:
    def __init__(self, obj):
        ''' Add the properties: Radius, Eccentricity, Height, Segments (see Property View) '''
        obj.addProperty("App::PropertyLength","Radius","Wankel","Base radius").Radius=60.0
        obj.addProperty("App::PropertyLength","Eccentricity","Wankel","Rotor eccentricity").Eccentricity=12.0
        obj.addProperty("App::PropertyLength","Height","Wankel","Height of the block").Height=30.0
        obj.addProperty("App::PropertyLength","Segments","Wankel","Number of the line segments").Segments=72
        obj.Proxy = self

    def onChanged(self, fp, prop):
        if prop == "Radius" or prop == "Eccentricity" or prop == "Height" or prop == "Segments": #if one of these is changed
            self.execute(fp)

    def execute(self, fp): #main part of script
        steps=int(fp.Segments) #get value from property
        dang=math.radians(360/steps)
        e=fp.Eccentricity
        r=fp.Radius
        h=fp.Height
        ang=0
        z=0
        halfw=r+e+20
        halfh=r+e+20

        for i in range(steps):
            if i==0:
                x1=e*math.cos(3*ang)+r*math.cos(ang) #coords for line startpoint
                y1=e*math.sin(3*ang)+r*math.sin(ang)
                ang=dang
                x2=e*math.cos(3*ang)+r*math.cos(ang) #coords for line endpoint
                y2=e*math.sin(3*ang)+r*math.sin(ang)
                seg=Part.makeLine((x1,y1,z),(x2,y2,z))
                wire2=Part.Wire([seg])
                x1=x2
                y1=y2
            else:
                x2=e*math.cos(3*ang)+r*math.cos(ang)
                y2=e*math.sin(3*ang)+r*math.sin(ang)
                seg=Part.makeLine((x1,y1,z),(x2,y2,z))
                wire2=Part.Wire([wire2,seg])
                x1=x2
                y1=y2
            ang=ang+dang #increment angle
        edge1 = Part.makeLine((-halfw,halfh,0), (halfw,halfh,0)) #lines needed to create rectangle
        edge2 = Part.makeLine((halfw,halfh,0), (halfw,-halfh,0))
        edge3 = Part.makeLine((halfw,-halfh,0), (-halfw,-halfh,0))
        edge4 = Part.makeLine((-halfw,-halfh,0), (-halfw,halfh,0))
        wire1 = Part.Wire([edge1,edge2,edge3,edge4]) #rectangle
        face1 = Part.Face(wire1) #face from rectangle
        face2=Part.Face(wire2) #face from epitrochoid http://en.wikipedia.org/wiki/Epitrochoid
        diff = face1.cut(face2) #boolean cut epitrochoid from rectangle
        ext=diff.extrude(Base.Vector(0,0,h)) #extrude the cut (face)
        fp.Shape = ext #result shape

def makeWankelBlock():
    doc = FreeCAD.activeDocument()
    if doc == None:
        doc = FreeCAD.newDocument()
    wankelblock=doc.addObject("Part::FeaturePython","Wankel_Block") #add object to document
    wankelblock.Label = "Wankel Block"
    WankelBlock(wankelblock)
    wankelblock.ViewObject.Proxy=0

if __name__ == "__main__": #feature will be generated after macro execution
    makeWankelBlock()


After macro execution effect should be similar to first one. But take look in the Property View-Data. If you change one of the properties (radius, eccentricity, etc.) model will be automatically refreshed.


Properties can be also changed from the command line. As example, type:
App.activeDocument().getObject("Wankel_Block").Radius = 80

Comments are in the scripts code (after "#").
I have used four spaces for indentation.


Download RAW Video.


FreeCAD 0.12 5150 SVN

5 comments:

  1. Thanks for this tuto again. Great!

    ReplyDelete
  2. Diederik van LieropJanuary 24, 2013 at 9:58 PM

    Thanks a lot, this was really helpful!

    ReplyDelete
  3. Thanks a lot, great introduction

    ReplyDelete
  4. I can't get the parametric version to work in FreeCAD 0.15. When I run the macro nothing shows up in the Application window. If change a length in the "data" tab I get a "Make sure to use matching types" error. If I save it out and then read it back in I get "'module' object has no attribute 'WankelBlock'". It appears that none of the examples of Scripted objects work any more.

    ReplyDelete
  5. http://forum.freecadweb.org/viewtopic.php?f=22&t=10036#p81015

    ReplyDelete