Macros recipes

From FreeCAD Documentation
Revision as of 13:05, 8 July 2011 by Yorik (talk | contribs)

Rotate the view by 90°

This macro rotates the current view by 90° to the left. Only works if you are in Top view.

import math
from pivy import coin
cam = Gui.ActiveDocument.ActiveView.getCameraNode()
rot = coin.SbRotation()
rot.setValue(coin.SbVec3f(0,0,1),math.pi/2)
nrot = cam.orientation.getValue() * rot
cam.orientation = nrot

Array copy

This macro copies the selected object several times, on an array grid. You can define the number of rows and columns and the distance between them. You need pyqt installed.

import FreeCAD, FreeCADGui, Part
from PyQt4 import QtGui,QtCore

def proceed():
    try:
        u = (int(l1.text()),float(l2.text()))   
        v = (int(l3.text()),float(l4.text()))
    except:
        FreeCAD.Console.PrintError("Wrong input! Only numbers allowed...\n")
    sel = FreeCADGui.Selection.getSelection()
        if sel:
            sel = sel[0]
            name = sel.Name   
            shape = sel.Shape
            for column in range(u[0]):
                for row in range(v[0]):
                    if (column != 0) or (row != 0):
                        delta = FreeCAD.Vector(column*u[1],row*v[1],0)   
                        newshape = sel.Shape.copy()
                        newshape.translate(delta)
                        newobject = FreeCAD.ActiveDocument.addObject("Part::Feature",name)
                        newobject.Shape = newshape
        else:
            FreeCAD.Console.PrintError("Error: One object must be selected")
        hide()

def hide():
    dialog.hide()

dialog = QtGui.QDialog()
dialog.resize(200,300)
dialog.setWindowTitle("Array")
la = QtGui.QVBoxLayout(dialog)
t1 = QtGui.QLabel("number of columns")
la.addWidget(t1)
l1 = QtGui.QLineEdit()
la.addWidget(l1)
t2 = QtGui.QLabel("distance between columns")
la.addWidget(t2)
l2 = QtGui.QLineEdit()
la.addWidget(l2)
t3 = QtGui.QLabel("number of rows")
la.addWidget(t3)
l3 = QtGui.QLineEdit()
la.addWidget(l3)
t4 = QtGui.QLabel("distance between rows")   
la.addWidget(t4)
l4 = QtGui.QLineEdit()
la.addWidget(l4)
okbox = QtGui.QDialogButtonBox(dialog)
okbox.setOrientation(QtCore.Qt.Horizontal)
okbox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok)
la.addWidget(okbox)
QtCore.QObject.connect(okbox, QtCore.SIGNAL("accepted()"), proceed)
QtCore.QObject.connect(okbox, QtCore.SIGNAL("rejected()"), hide)
QtCore.QMetaObject.connectSlotsByName(dialog)
dialog.show()

Flatten wire

This macro flattens draft wires that are not plane to their median Z coordinate.

import FreeCAD
obj = FreeCAD.ActiveDocument.ActiveObject
z = 0
for p in obj.Points: z += p.z
z = z/len(obj.Points)
newpoints = []
for p in obj.Points: newppoints.append(FreeCAD.Vector(p.x,p.y,z))
obj.Points = newppoints

Convert Meshes to Parts

This macro converts selected meshes to parts. It has a broad tolerance, so use it only with objects that have no curves otherwise you'll get weird results.

import FreeCAD,FreeCADGui,Mesh,Part,MeshPart

for obj in FreeCADGui.Selection.getSelection():
	if "Mesh" in obj.PropertiesList:
		faces = []		
		mesh = obj.Mesh
		segments = mesh.getPlanes(0.01) # use rather strict tolerance here

		for i in segments:
		  if len(i) > 0:
		     # a segment can have inner holes
		     wires = MeshPart.wireFromSegment(mesh, i)
		     # we assume that the exterior boundary is that one with the biggest bounding box
		     if len(wires) > 0:
		        ext = None
		        max_length = 0
		        for i in wires:		
		           if i.BoundBox.DiagonalLength > max_length:
		              max_length = i.BoundBox.DiagonalLength
		              ext = i
	        	wires.remove(ext)
	        	# all interior wires mark a hole and must reverse their orientation, otherwise Part.Face fails
	        	for i in wires:
	        	   i.reverse()
	        	# make sure that the exterior wires comes as first in the lsit
	        	wires.insert(0, ext)
	        	faces.append(Part.Face(wires))

		shell=Part.Compound(faces)
		solid = Part.Solid(Part.Shell(faces))
		name = obj.Name
		FreeCAD.ActiveDocument.removeObject(name)
		FreeCAD.ActiveDocument.addObject("Part::Feature",name).Shape = solid

Joint wire

This macro allow to find and joint all non connected edge to the closest non connected one using a line. It take a shape matrix in entry ( [shape1,shape2,...]).

def findWires(edges):
   def verts(shape):
               return [shape.Vertexes[0].Point,shape.Vertexes[-1].Point]
       def group(shapes):
               shapesIn = shapes[:]
               pointTst = []
       pointOut =[]
       for s in shapesIn :
           pointTst=pointTst+[s.Vertexes[0].Point]
           pointTst=pointTst+[s.Vertexes[-1].Point]
       print pointTst               
       changed = False
               for s in shapesIn:
                       if len(s.Vertexes) < 2:
               print "one vertex, its a circle, just add"
                       else:                             
                               for v in verts(s):
                   twoDot=0
                                       for vv in pointTst:
                                               if v == vv:
                           twoDot=twoDot+1                           
                       if v==vv and twoDot==2 :                   
                           changed = True
                           print "found matching vert"
                           break
                                   if twoDot<2:
                       print "didn't find any matching vert..."
                       pointOut.append(v)
        print "Dots non connected", pointOut
               return(changed,pointOut)
   def joint(point):
       for p in range(len(point)/2) :
           print point
           deltI=Part.Vertex(100,100,100).Point
           pos=1
           for pp in range(len(point)-1) :
               print "position:",pp+1
               if len(point)-1>1:
                   deltN=(point[0]-point[pp+1])
                   if deltN.Length<deltI.Length:
                       deltI=deltN
                       pos=pp+1
                       print "changement",pos
               else:
                   pos=1   
           print "points a joindre",point[0],point[pos]
           if point[0]!=point[pos]:
               Part.show(Part.makePolygon([point[0],point[pos]]))
           else:
               print "WARNING les points ont la meme valeurs "
           point.pop(0)
           point.pop(pos-1)
       point=0 #to have a return normally void
       return(point)
   working = True
       edgeSet = edges
   result = group(edgeSet)
       working = result[0]
       edgeSet = result[1]
   joint(result[1])
       return result[1]

Automatic Drawing

This code allow the user to get the view of his object in a drawing with 4 different position(front,top,iso,right). Need some modification to be perfecly effective.

import FreeCAD, Part, Drawing
if len(Gui.Selection.getSelectionEx())>1:
   App.Console.PrintError("Warning: Only the first item is generate")
if len(Gui.Selection.getSelectionEx())==0:
   App.Console.PrintError("Warning: Need to select one item")
Piece=Gui.Selection.getSelectionEx()[0]
App.activeDocument().addObject('Drawing::FeaturePage','AutoDrawing')
App.activeDocument().AutoDrawing.Template = App.getResourceDir()+'Mod/Drawing/Templates/A3_Landscape.svg'
DH=20
DL=30
L=Piece.Object.Shape.BoundBox.XMax
H=Piece.Object.Shape.BoundBox.ZMax
P=Piece.Object.Shape.BoundBox.YMax
Sc=(400-3*DL)/(L+H)
Sc2=(250-3*DH)/(P+H)
if Sc>Sc2 : 
   Sc=Sc2
TopX=DL+Sc*L
FrontX=DL+Sc*L
RightX=2*DL+Sc*L
IsoX=2*DL+Sc*(L)
TopY=DH+Sc*P
RightY=DH+P*Sc
FrontY=2*DH+Sc*(P+H)
IsoY=2*DH+Sc*P

print TopX,RightX,TopY,FrontY

#Create topView
App.activeDocument().addObject('Drawing::FeatureViewPart','topView')
App.activeDocument().topView.Source =Piece.Object
App.activeDocument().topView.Direction = (0,0,1)
App.activeDocument().topView.Rotation=180
App.activeDocument().topView.X = TopX
App.activeDocument().topView.Y = TopY
App.activeDocument().topView.ShowHiddenLines=True
App.activeDocument().AutoDrawing.addObject(App.activeDocument().topView)
App.activeDocument().topView.Scale = Sc
#Create FrontView
App.activeDocument().addObject('Drawing::FeatureViewPart','FrontView')
App.activeDocument().FrontView.Source =Piece.Object
App.activeDocument().FrontView.Direction = (0,-1,0)
App.activeDocument().FrontView.Rotation=90
App.activeDocument().FrontView.Scale = Sc
App.activeDocument().FrontView.X = FrontX
App.activeDocument().FrontView.Y = FrontY
App.activeDocument().FrontView.ShowHiddenLines=True
App.activeDocument().AutoDrawing.addObject(App.activeDocument().FrontView)
#Create RightView
App.activeDocument().addObject('Drawing::FeatureViewPart','RightView')
App.activeDocument().RightView.Source =Piece.Object
App.activeDocument().RightView.Direction = (1,0,0)
App.activeDocument().RightView.Scale = Sc
App.activeDocument().RightView.X = RightX
App.activeDocument().RightView.Y = RightY
App.activeDocument().RightView.ShowHiddenLines=True
App.activeDocument().AutoDrawing.addObject(App.activeDocument().RightView)
#Create IsotView
App.activeDocument().addObject('Drawing::FeatureViewPart','IsoView')
App.activeDocument().IsoView.Source =Piece.Object
App.activeDocument().IsoView.Direction = (1,1,1)
App.activeDocument().IsoView.Rotation=60
App.activeDocument().IsoView.Scale = Sc*.6
App.activeDocument().IsoView.X = IsoX
App.activeDocument().IsoView.Y = IsoY
App.activeDocument().IsoView.ShowHiddenLines=True
App.activeDocument().AutoDrawing.addObject(App.activeDocument().IsoView)

Draw 2D function

Use it to draw a function described by a "equation" [z=F(x)] (Z-X plane) The example done here generate a parabol. Need to be defined : F=variable used in the fonction, X=initial value of x, Nb= Number of step, Z=function express with x ZZ=function express with xx

import FreeCAD, FreeCADGui, Part
import math
F=800
X=-500
Nb=10
Step=1000/Nb
Y=0
for I in range(Nb):
	XX=X+Step 
	Z=X*X/(4*F)
	ZZ=XX*XX/(4*F)
	if I==0:
		print "Le test est vrai !"
		nomme=Part.makeLine((X,Y,Z),(XX,Y,ZZ))
		WWire=Part.Wire([nomme])
	else :
		print "Le test est 2 !"
		nomme=Part.makeLine((X,Y,Z),(XX,Y,ZZ))		
		WWire=Part.Wire([WWire,nomme])
	X=XX 

Part.show(WWire)

Remove parametric history from a shape

This will remove all parametric associativity from an object, leaving it as a "dumb" shape.

newShape = originalObject.Shape.copy()
newName = FreeCAD.ActiveDocument.ActiveObject.Name
FreeCAD.ActiveDocument.removeObject(newName)
newObject = FreeCAD.ActiveDocument.addObject("Part::Feature",newName)
newObject.Shape = newShape

Creating and printing corner shapes

This macro is a complete application, it pops up a dialog asking for the dimensions of your corner piece, then creates the object in the document and creates a page view with top, front and lateral views of the piece.

   # -*- coding:utf-8 -*-

   #####################################
   # Importation de fonctions externes :

   #from os import *
   import FreeCAD, FreeCADGui, Part, Draft, math, MeshPart, Mesh, Drawing
   from PyQt4 import QtGui,QtCore
   from FreeCAD import Base
   App=FreeCAD
   Gui=FreeCADGui

   ##################################
   # Défnition Class :

   class Corniere:
      def __init__(self, obj):
         obj.addProperty("App::PropertyLength","L1","Corniere","Largeur 1").L1=20.0
         obj.addProperty("App::PropertyLength","L2","Corniere","Largeur 2").L2=20.0
         obj.addProperty("App::PropertyLength","e1","Corniere","Epaisseur 1").e1=2.0
         #obj.addProperty("App::PropertyLength","e2","Corniere","Epaisseur 2").e2=2.0
         obj.addProperty("App::PropertyLength","Longueur","Corniere","Longueur").Longueur=200.0
         obj.Proxy = self

      def execute(self, fp):
         P1=Base.Vector(fp.e1,fp.e1,0)
         S1=Part.makeBox(fp.L1,fp.L2,fp.Longueur)
         S2=Part.makeBox(fp.L1-fp.e1,fp.L2-fp.e1,fp.Longueur,P1)
         fp.Shape=S1.cut(S2)   

   ##################################
   # Défnition locale de fonctions :


   def proceed():
      QtGui.qApp.setOverrideCursor(QtCore.Qt.WaitCursor)

      if FreeCAD.ActiveDocument==None:
         FreeCAD.newDocument("Corniere")

      oldDocumentObjects=App.ActiveDocument.Objects

      try:
         QL1 = float(l1.text())   
         QL2 = float(l2.text())
         Qe = float(l3.text())
         QLongueur = float(l4.text())
      except:
         FreeCAD.Console.PrintError("Wrong input! Only numbers allowed...\n")

      Cor=FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Corniere")
      Corniere(Cor)
      Cor.ViewObject.Proxy=0
      Cor.L1=QL1
      Cor.L2=QL2
      Cor.e1=Qe
      Cor.Longueur=QLongueur

      App.ActiveDocument.recompute()
      Gui.SendMsgToActiveView("ViewFit")

      QtGui.qApp.restoreOverrideCursor()
      
      Plan(Cor)
      
      dialog.hide()

   def hide():

      dialog.hide()

   def Plan(obj):

      ObjetProjete=obj.Shape

      TailleX=ObjetProjete.BoundBox.XLength
      TailleY=ObjetProjete.BoundBox.YLength
      TailleZ=ObjetProjete.BoundBox.ZLength

      page = App.activeDocument().addObject('Drawing::FeaturePage','Page')
      page.Template = App.getResourceDir()+'Mod/Drawing/Templates/A3_Landscape.svg'   
      vueprofil = App.activeDocument().addObject('Drawing::FeatureViewPart','VueProfil')
      vueprofil.Source = obj
      vueprofil.Direction = (0.0,0.0,1.0)
      vueprofil.Scale = 1.0
      vueprofil.X = 50.0
      vueprofil.Y = 50.0
      page.addObject(vueprofil)

      vuegauche = App.activeDocument().addObject('Drawing::FeatureViewPart','Vuegauche')
      vuegauche.Source = obj
      vuegauche.Direction = (-1.0,0.0,0.0)
      vuegauche.ShowHiddenLines = True
      vuegauche.Scale = 1.0
      vuegauche.Rotation = 180.0
      vuegauche.X = 50.0+TailleX/2+TailleX
      vuegauche.Y = 50.0
      page.addObject(vuegauche)

      vuedessus = App.activeDocument().addObject('Drawing::FeatureViewPart','Vuedessus')
      vuedessus.Source = obj
      vuedessus.Direction = (0.0,-1.0,0.0)
      vuedessus.ShowHiddenLines = True
      vuedessus.Scale = 1.0
      vuedessus.Rotation = 180.0
      vuedessus.X = 50.0+TailleX/2+TailleX
      vuedessus.Y = 50.0+TailleX/2+TailleY+TailleX
      page.addObject(vuedessus)

      vueiso = App.activeDocument().addObject('Drawing::FeatureViewPart','VueIso')
      vueiso.Source = obj
      vueiso.Direction = (-1.0,-1.0,0.5)
      vueiso.Scale = 1.0
      vueiso.ShowSmoothLines = True
      vueiso.X = TailleZ+TailleX/2
      vueiso.Y = 7*TailleZ+3*TailleY
      page.addObject(vueiso)
      

      App.activeDocument().recompute()

      PageFile = open(page.PageResult,'r')
      OutFile = open('temp.svg','w')
      OutFile.write(PageFile.read())
      del OutFile,PageFile
      

   dialog = QtGui.QDialog()
   dialog.resize(200,200)
   dialog.setWindowTitle("Corniere")
   la = QtGui.QVBoxLayout(dialog)

   e1 = QtGui.QLabel("Dimensions de la corniere")
   commentFont=QtGui.QFont("Arial",10,True)
   e1.setFont(commentFont)
   la.addWidget(e1)

   t1 = QtGui.QLabel("L1")
   la.addWidget(t1)
   l1 = QtGui.QLineEdit()
   l1.setText("20")
   la.addWidget(l1)

   t2 = QtGui.QLabel("L2")
   la.addWidget(t2)
   l2 = QtGui.QLineEdit()
   l2.setText("20")
   la.addWidget(l2)

   t3 = QtGui.QLabel("e")
   la.addWidget(t3)
   l3 = QtGui.QLineEdit()
   l3.setText("2")
   la.addWidget(l3)

   t4 = QtGui.QLabel("Longueur")
   la.addWidget(t4)
   l4 = QtGui.QLineEdit()
   l4.setText("300")
   la.addWidget(l4)

   okbox = QtGui.QDialogButtonBox(dialog)
   okbox.setOrientation(QtCore.Qt.Horizontal)
   okbox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok)
   la.addWidget(okbox)
   QtCore.QObject.connect(okbox, QtCore.SIGNAL("accepted()"), proceed)
   QtCore.QObject.connect(okbox, QtCore.SIGNAL("rejected()"), hide)
   QtCore.QMetaObject.connectSlotsByName(dialog)
   dialog.show()