Topological data scripting

Introduction
We will here explain you how to control the Part Module directly from the FreeCAD python interpreter, or from any external script. Be sure to browse the Scripting section and the FreeCAD Scripting Basics pages if you need more information about how python scripting works in FreeCAD.

First to use the Part module functionality you have to load the Part module into the interpreter:

Class Diagram
This is a UML overview about the most important classes of the Part module:

Geometry
The geomtric objects are the building block of all topological objects:
 * GEOM Base class of the geometric objects
 * LINE A straight line in 3D, defined by starting point and and point
 * CIRCLE Circle or circle segment defined by a center point and start and end point
 * ...... And soon some more ;-)

Topology
The following topological data types are available:
 * COMPOUND A group of any type of topological object.
 * COMPSOLID A composite solid is a set of solids connected by their faces. It expands the notions of WIRE and SHELL to solids.
 * SOLID A part of space limited by shells. It is three dimensional.
 * SHELL A set of faces connected by their edges. A shell can be open or closed.
 * FACE In 2D it is part of a plane; in 3D it is part of a surface. Its geometry is constrained (trimmed) by contours. It is two dimensional.
 * WIRE A set of edges connected by their vertices. It can be an open or closed contour depending on whether the edges are linked or not.
 * EDGE A topological element corresponding to a restrained curve. An edge is generally limited by vertices. It has one dimension.
 * VERTEX A topological element corresponding to a point. It has zero dimension.
 * SHAPE A generic term covering all of the above.

Creating basic types
You can easily create basic topological objects with the "make..." methods from the Part Module: b = Part.makeBox(0,0,0,100,100,100) Part.show(b)

A couple of other make... methods available:
 * makeBox(x,y,z,l,w,h) -- Make a box located in (x,y,z) with the dimensions (l,w,h)
 * makeCircle(radius,[angle1,angle2]) -- Make a circle with a given radius. By default angle1=0 and angle2=2*PI
 * makeCompound(list) -- Create a compound out of a list of geometries
 * makeCylinder(radius,height,[angle]) -- Make a cylinder with a given radius and height. By default angle=2*PI
 * makeLine((x1,y1,z1),(x2,y2,z2)) -- Make a line of two points
 * makePlane(lenght,width) -- Make a plane
 * makePolygon(list) -- Make a polygon of a list of points
 * makeSphere(radius,[angle1,angle2,angle3]) -- Make a sphere with a given radius. By default angle1=0, angle2=0.5*PI and angle3=2*PI

Exploring shapes
You can easily explore the topological data structure: b = Part.makeBox(0,0,0,100,100,100) b.Wires w = b.Wires[0] w w.Wires w.Vertexes Part.show(w) w.Edges e = w.Edges[0] e.Vertexes v = e.Vertexes[0] v.Point

By typing the line above in the python interpreter, you will gain a good understanding of the structure of Part objects. Here, our makeBox command created a solid shape. This solid, like all Part solids, contains faces. Faces always contain wires, which are lists of edges that border the face. Each face has exactly one closed wire. In the wire, we can look at each edge separately, and inside each edge, we can see the vertexes. Straight edges have only two vertexes, obviously. Part Vertexes are OCC shapes, but they have a Point attribute which returns a nice FreeCAD Vector.

Example 1: Creating simple topology
We will now create a topology by constructing it out of simpler geometry. As a case study we use a part as seen in the picture which consists of four vertexes, two circles and two lines.

Creating Geometry
First we have to create the distinct geometric parts of this wire. And we have to take care that the vertexes of the geometric parts are at the same position. Otherwise later on we might not be able to connect the geometric parts to a topology!

So we create first the points: from FreeCAD import Base V1 = Base.Vector(0,10,0) V2 = Base.Vector(30,10,0) V3 = Base.Vector(30,-10,0) V4 = Base.Vector(0,-10,0)

Arc
To create an arc of circle we make a helper point and create the arc of circle through three points: VC1 = Base.Vector(-10,0,0) C1 = Part.Arc(V1,VC1,V4) VC2 = Base.Vector(40,0,0) C2 = Part.Arc(V2,VC2,V3)
 * 1) and the second one

Line
The line can be created very simple out of the points: L1 = Part.Line(V1,V2) L2 = Part.Line(V4,V3)
 * 1) and the second one

Putting all together
The last step is to put the geometric base elements together and bake a topological shape: S1 = Part.Shape([C1,C2,L1,L2])

Make a prism
Now extrude the wire in a direction and make an actual 3D shape: W = Part.Wire(S1.Edges) P = W.extrude(Base.Vector(0,0,10))

Example 2: The OCC bottle
A typical example found on the OpenCasCade Getting Started Page is how to build a bottle. This is a good exercise for FreeCAD too. In fact, you can follow our example below and the OCC page simultaneously, you will understand well how OCC structures are implemented in FreeCAD.

The complete script below is also included in FreeCAD installation (inside the Mod/Part folder) and can be called from the python interpreter by typing: import Part import MakeBottle bottle = MakeBottle.makeBottle Part.show(bottle)

The complete script
Here is the complete MakeBottle script:

import Part, FreeCAD from FreeCAD import Base def makeBottle(myWidth=50.0, myHeight=70.0, myThickness=30.0): aPnt1=Base.Vector(-myWidth/2.,0,0) aPnt2=Base.Vector(-myWidth/2.,-myThickness/4.,0) aPnt3=Base.Vector(0,-myThickness/2.,0) aPnt4=Base.Vector(myWidth/2.,-myThickness/4.,0) aPnt5=Base.Vector(myWidth/2.,0,0) aArcOfCircle = Part.Arc(aPnt2,aPnt3,aPnt4) aSegment1=Part.Line(aPnt1,aPnt2) aSegment2=Part.Line(aPnt4,aPnt5) aEdge1=aSegment1.toShape aEdge2=aArcOfCircle.toShape aEdge3=aSegment2.toShape aWire=Part.Wire([aEdge1,aEdge2,aEdge3]) aTrsf=Base.Matrix aTrsf.A11=-1.0 # mirror on the x axis aMirroredWire=aWire.transform(aTrsf) myWireProfile=Part.Wire([aWire,aMirroredWire]) myFaceProfile=Part.Face(myWireProfile) aPrismVec=Base.Vector(0,0,myHeight) myBody=myFaceProfile.extrude(aPrismVec) myBody=myBody.makeFillet(myThickness/12.0,myBody.Edges) neckLocation=Base.Vector(0,0,myHeight) neckNormal=Base.Vector(0,0,1) myNeckRadius = myThickness / 4. myNeckHeight = myHeight / 10 myNeck = Part.makeCylinder(neckLocation,neckNormal,myNeckRadius,myNeckHeight) myBody = myBody.fuse(myNeck) return myBody

Detailed explanation
import Part, FreeCAD from FreeCAD import Base We will need,of course, the Part module, but also the FreeCAD.Base module, which contains basic FreeCAD structures like vectors and matrixes. def makeBottle(myWidth=50.0, myHeight=70.0, myThickness=30.0): aPnt1=Base.Vector(-myWidth/2.,0,0) aPnt2=Base.Vector(-myWidth/2.,-myThickness/4.,0) aPnt3=Base.Vector(0,-myThickness/2.,0) aPnt4=Base.Vector(myWidth/2.,-myThickness/4.,0) aPnt5=Base.Vector(myWidth/2.,0,0) Here we define our makeBottle function. This function can be called without arguments, like we did above, in which case default values for width, height, and thickness will be used. Then, we define a couple of points that will be used for building our base profile. aArcOfCircle = Part.Arc(aPnt2,aPnt3,aPnt4) aSegment1=Part.Line(aPnt1,aPnt2) aSegment2=Part.Line(aPnt4,aPnt5) Here we actually define the geometry: an arc, made of 3 points, and two line segments, made of 2 points. aEdge1=aSegment1.toShape aEdge2=aArcOfCircle.toShape aEdge3=aSegment2.toShape aWire=Part.Wire([aEdge1,aEdge2,aEdge3]) Remember the difference between geometry and shapes? Here we build shapes out of our construction geometry. 3 edges (edges can be straight or curved), then a wire made of those three edges. aTrsf=Base.Matrix aTrsf.A11=-1.0 # mirror on the x axis aMirroredWire=aWire.transform(aTrsf) myWireProfile=Part.Wire([aWire,aMirroredWire]) Until now we built only a half profile. Easier than building the whole profile the same way, we can just mirror what we did, and glue both halfs together. So we first create a matrix. A matrix is a very common way to apply transformations to objects in the 3D world, since it can contain in one structure all basic transformations that 3D objects can suffer (move, rotate and scale). Here, after we create the matrix, we mirror it, and we create a copy of our wire with that transformation matrix applied to it. We now have two wires, and we can make a third wire out of them, since wires are actually lists of edges. myFaceProfile=Part.Face(myWireProfile) aPrismVec=Base.Vector(0,0,myHeight) myBody=myFaceProfile.extrude(aPrismVec) myBody=myBody.makeFillet(myThickness/12.0,myBody.Edges) Now that we have a closed wire, it can be turned into a face. Once we have a face, we can extrude it. Doing so, we actually made a solid. Then we apply a nice little fillet to our object because we care about good design, don't we? neckLocation=Base.Vector(0,0,myHeight) neckNormal=Base.Vector(0,0,1) myNeckRadius = myThickness / 4. myNeckHeight = myHeight / 10 myNeck = Part.makeCylinder(neckLocation,neckNormal,myNeckRadius,myNeckHeight) Then, the body of our bottle is made, we still need to create a neck. So we make a new solid, with a cylinder. myBody = myBody.fuse(myNeck) The fuse operation, which in other apps is sometimes called union, is very powerful. It will take care of gluing what needs to be glued and remove parts that need to be removed. return myBody Then, we return our Part solid as the result of our function. That Part solid, like any other Part shape, can be attributed to an object in a FreeCAD document, with: myObject = FreeCAD.ActiveDocument.addObject("Part::Feature","myObject") myObject.Shape = bottle or, more simple: Part.show(bottle)

Load and Save
There are several ways to save your work in the Part module. You can of course save your FreeCAD document, but you can also save Part objects directly to common CAD formats, such as BREP, IGS, STEP and STL.

Saving a shape to a file is easy. There are exportBrep, exportIges, exportStl and exportStep methods availables for all shape objects. So, doing: import Part s = Part.makeBox(0,0,0,10,10,10) s.exportStep("test.stp") this will save our box into a STEP file. To load a BREP, IGES or STEP file, simply do the contrary: import Part s = Part.Shape s.read("test.stp") Note that importing or opening BREP, IGES or STEP files can also be done directly from the File -> Open or File -> Import menu. At the moment exporting is still not possible that way, but should be there soon.