Last active
December 17, 2015 17:49
-
-
Save 5263/5649053 to your computer and use it in GitHub Desktop.
Import ft designer (*.ftm) files into FreeCAD
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os | |
if open.__module__ == '__builtin__': | |
pythonopen = open # to distinguish python built-in open function from the one declared here | |
ftdpath='C:/Program Files (x86)/fischertechnik designer' | |
partnamelang='NAME' #german | |
#partnamelang='ENG' #english | |
ftmmotorstr = "MotorSpeed%d=0\nMOTORROTATION%d=0\nMOTORSTATUS%d=0\nMotor%d=\nMOTORSOUND%d=0" | |
ftmfiletrailer="""[COMMON] | |
Version=1 | |
Count=%(count)d | |
InfModell= | |
InfOriginal= | |
InfGebaut= | |
TubeCount=0 | |
KinematikCount=0 | |
CollisionCount=0 | |
HalbzeugCount=0 | |
%(motors) | |
KeyCount=0 | |
AniLength=125 | |
[CAMERA] | |
PositionX=0 | |
PositionY=1 | |
PositionZ=-10 | |
DirectionX=0 | |
DirectionY=0 | |
DirectionZ=-1 | |
UpX=0 | |
UpY=1 | |
UpZ=0 | |
TargetX=0 | |
TargetY=0 | |
TargetZ=0 | |
[LOGIKCOMMON] | |
BLOCKCOUNT=0 | |
CONNCOUNT=0 | |
""" | |
class connection(): | |
def __init__(self,secdict): | |
import FreeCAD | |
self.name=secdict[partnamelang].decode('latin1') #german or english | |
pos=FreeCAD.Vector(floatg(secdict['X']),floatg(secdict['Y']),floatg(secdict['Z'])) | |
frot=prttorot(floatg(secdict['PF']),floatg(secdict['RF']),floatg(secdict['TF'])) | |
#if 'PN' in secdict and 'RN' in secdict and 'TN' in secdict: | |
nrot=prttorot(floatg(secdict.get('PN','0')),floatg(secdict.get('RN','0')),floatg(secdict.get('TN','0')))#purpose is unclear | |
self.Placement = FreeCAD.Placement(pos,frot) | |
self.num= secdict.get('id') | |
if self.num is not None: | |
self.num=int(self.num[4:]) | |
def __repr__(self): | |
return "<%u-%s>"%(self.num,self.name) | |
def getdefdatabase(): | |
dict1={} | |
import os | |
path=os.path.join(ftdpath,'def') | |
filenames=os.listdir(os.path.join(ftdpath,'def')) | |
for filename in filenames: | |
defpath=os.path.join(path,filename) | |
partdef= def2list(defpath) | |
partdefdict = ftlist2ftdict(partdef) | |
key=partdefdict['COMMON']['ORGNUMBER'] | |
dict1[key]=partdefdict | |
#partname=os.path.splitext(os.path.basename(filename))[0] | |
#partfname=partdefdict['COMMON'][partnamelang].decode('latin1') #Part name | |
#partoffset=FreeCAD.Vector(floatg(partdefdict['OFFSET']['X']),floatg(partdefdict['OFFSET']['Y']),floatg(partdefdict['OFFSET']['Z'])) | |
#shortname = partname | |
#longname ='%s_%s' %(shortname,partfname) | |
#partmeshfilename=ftdpath+'/%s/%s.stl'%(partlib,partname) | |
#print filename | |
def formatfloat(f1): | |
f2=float(f1) | |
if f2.is_integer(): | |
return "%d"%f2 | |
elif abs(f2)<10 and abs(f2)>0.09: | |
return ("%f"%f2).replace('.',',') | |
else: | |
return ("%0.14E"%f2).replace('.',',') | |
def formatvector(vec,postfix): | |
vecindex="XYZ" | |
return "\n".join(["%s%s=%s"%(vecindex[index],postfix,formatfloat(velm)) for index,velm in enumerate(vec)]) | |
def formatpart(placement,color,Phase,Number,PartNum,RepCount): | |
formats="[%(id)s]\nNAME=%(NAME)s\n%(VECPOS)s\n%(VECUP)s\n%(VECDIR)s\nCOLOR=%(COLOR)s\nPHASE=%(PHASE)s\n" | |
partdict={} | |
matrixlst=placement.toMatrix().A | |
partdict['VECPOS']=formatvector(placement.Base.multiply(0.1),'POS') | |
partdict['VECUP']=formatvector(matrixlst[1:10:4],'UP') | |
partdict['VECDIR']=formatvector(matrixlst[2:11:4],'DIR') | |
partdict['COLOR']= formatfloat(round(color[0]*0xff+color[1]*0xff00+color[2]*0xff0000)) | |
partdict['NAME']='F%s_%d' % (PartNum,RepCount or 1) | |
partdict['PHASE']=int(Phase or 1) | |
partdict['id']='PART%d' % int(Number) | |
return formats % partdict | |
def floatg(fstr): | |
return float(fstr.replace(',','.').lstrip('#')) | |
def prttorot(p,r,t): | |
import FreeCAD | |
#'xyz-prt' | |
pr=FreeCAD.Rotation(FreeCAD.Vector(-1,0,0),p) | |
rr=FreeCAD.Rotation(FreeCAD.Vector(0,0,1),r) | |
tr=FreeCAD.Rotation(FreeCAD.Vector(0,1,0),t) | |
return pr.multiply(rr).multiply(tr) | |
def def2list(filename): | |
fh=pythonopen(filename) | |
ftobjects=[] | |
for line in fh: | |
if line: | |
lines=line.strip() | |
if lines.startswith('[') and lines.endswith(']'): | |
newobj={'id':lines[1:-1].upper()} | |
ftobjects.append(newobj); | |
elif '=' in lines: | |
key, value = lines.split('=',1) | |
newobj[key.upper()]=value | |
return ftobjects | |
def ftlist2ftdict(ftlist): | |
retdict={} | |
for item in ftlist: | |
id = item['id'] | |
retdict[id]=item.copy() | |
return retdict | |
def getoffset(partname): | |
import FreeCAD | |
#partname=ftpartobj['NAME'].split('_')[0][1:] #revove trailing F from Part Number | |
partpath=ftdpath+'/def/%s.def'%partname | |
partdef= def2list(partpath) | |
partdefdict = ftlist2ftdict(partdef) | |
#print partdefdict.keys() | |
#partfname=partdefdict['COMMON'][partnamelang].decode('latin1') #Part name | |
return FreeCAD.Vector(floatg(partdefdict['OFFSET']['X']),floatg(partdefdict['OFFSET']['Y']),floatg(partdefdict['OFFSET']['Z'])) | |
def drawconnections(partdefdict,pos,rot,shortname): #doesn't work | |
def drawcoordinatesystem(shortname,partplacement,pos,rot,drawy=False,length=10): | |
import FreeCAD | |
import Draft | |
dwirex=Draft.makeWire([partplacement.multVec(pos),partplacement.multVec(rot.multVec(FreeCAD.Vector(length,0,0)).add(pos))]) | |
dwirez=Draft.makeWire([partplacement.multVec(pos),partplacement.multVec(rot.multVec(FreeCAD.Vector(0,0,length)).add(pos))]) | |
dwirex.Label='%s-X'%(shortname) | |
dwirez.Label='%s-Z'%(shortname) | |
dwirex.ViewObject.LineColor= (0.5,0.0,0.0) | |
dwirez.ViewObject.LineColor= (0.0,0.0,0.5) | |
if drawy: | |
dwirey=Draft.makeWire([partplacement.multVec(pos),partplacement.multVec(rot.multVec(FreeCAD.Vector(0,length,0)).add(pos))]) | |
dwirey.Label='%s-Y'%(shortname) | |
dwirey.ViewObject.LineColor= (0.0,0.5,0.0) | |
import FreeCAD | |
for secname,secdict in partdefdict.iteritems(): | |
if secname.startswith('CONN') and secname != 'CONNECTIONS': | |
connname=secdict['NAME'].decode('latin1') | |
partplacement=FreeCAD.Placement(pos,rot) | |
connpos=FreeCAD.Vector(floatg(secdict['X']),floatg(secdict['Y']),floatg(secdict['Z'])) | |
pf,rf,tf=(floatg(secdict['PF']),floatg(secdict['RF']),floatg(secdict['TF'])) | |
#pn,rn,tn=(floatg(secdict['PN']),floatg(secdict['RN']),floatg(secdict['TN'])) | |
drawcoordinatesystem('%s-%s' % (shortname,connname),partplacement,connpos,prttorot(pf,rf,tf)) | |
def open(filename): | |
"called when freecad opens a file." | |
import FreeCAD | |
docname = os.path.splitext(os.path.basename(filename))[0] | |
doc = FreeCAD.newDocument(docname) | |
loadfiletodoc(doc,filename) | |
def insert(filename,docname): | |
"called when freecad imports a file" | |
import FreeCAD | |
try: | |
doc=FreeCAD.getDocument(docname) | |
except: | |
doc=FreeCAD.newDocument(docname) | |
loadfiletodoc(doc,filename) | |
def fclabeltoftpart(label,name): | |
import re | |
moimp=re.match("P(\d+)_N(\d+)_([^_]+)_.*",label) | |
if moimp: | |
Phase,Number,PartNum = moimp.groups() | |
RepCount = None | |
print moimp.groups() | |
else: | |
sname=name.lstrip('_') | |
if len(sname)==4 or len(sname)==7: | |
sname='0'+sname | |
if len(sname)==8: | |
RepCount = int(sname[5:8])+1 | |
else: | |
RepCount = 1 | |
PartNum=sname[0:5] | |
Phase,Number = None,None | |
return (Phase,Number,PartNum,RepCount) | |
def export(exportList,filename): | |
"called when freecad exports a file" | |
import FreeCAD | |
efile = pythonopen(filename,'w') | |
meshlist=[obj for obj in exportList if obj.isDerivedFrom('Mesh::Feature')] | |
for objindex,obj in enumerate(meshlist): | |
pospar=fclabeltoftpart(obj.Label,obj.Name) | |
Phase,Number,PartNum,RepCount = pospar | |
#todo sort and regenerate Part Number and ids | |
correctedplacement=FreeCAD.Placement(obj.Placement.Base.sub(obj.Placement.Rotation.multVec(getoffset(PartNum))),obj.Placement.Rotation) | |
efile.write(formatpart(correctedplacement,obj.ViewObject.ShapeColor, Phase,objindex+1,PartNum,RepCount)) | |
efile.write('\n') | |
efile.write(ftmfiletrailer%{'count':len(meshlist),'motors':"\n".join((ftmmotorstr % (5*(e,))) for e in range(1,9))}) | |
efile.close() | |
def importrandompart(doc=None,drawconns=True,drawchildren=True): | |
import FreeCAD | |
import random | |
partfilename="%s/def/%s" % (ftdpath,random.choice( os.listdir(ftdpath+'/def'))) | |
if doc is None: | |
doc=FreeCAD.newDocument() | |
print partfilename | |
ftpartformrepository(partfilename).addtofreecad(doc,drawconns,drawchildren) | |
def loadfiletodoc(doc,filename): | |
import FreeCAD | |
if filename.lower().endswith('.ftm'): | |
ftmodel(filename).addtofreecad(doc) | |
elif filename.lower().endswith('.part'): | |
ftpartfromfile(filename).addtofreecad(doc,drawconns=True,drawchildren=True) | |
elif filename.lower().endswith('.def'): | |
ftpartformrepository(filename).addtofreecad(doc,drawconns=True,drawchildren=True) | |
doc.recompute() | |
def shape_oese_negativ(): | |
import FreeCAD | |
import Part | |
zylauss=Part.makeCylinder(3.2,20,FreeCAD.Vector(0,0,-10)) | |
zylinn=Part.makeCylinder(2,2,FreeCAD.Vector(0,0,-1)) | |
boxpl=Part.makeBox(2,6,1.2,FreeCAD.Vector(1.5,-3,-0.6)) | |
boxmi=Part.makeBox(2,6,1.2,FreeCAD.Vector(-3.5,-3,-0.6)) | |
boxes=boxpl.fuse(boxmi) | |
return zylauss.cut(boxes).fuse(zylinn) | |
def shape_oese_winkeltraeger(laengezmm=30): | |
import FreeCAD | |
import Part | |
zylinn=Part.makeCylinder(2,laengezmm,FreeCAD.Vector(0,0,-laengezmm/2.0)) | |
box=Part.makeBox(3,7,laengezmm,FreeCAD.Vector(-1.5,-3.5,-laengezmm/2.0)) | |
return zylinn.fuse(box).removeSplitter() | |
def shape_strebe(laengemm=30,b1=2.8,r1=3.975): | |
import FreeCAD | |
import Part | |
cyl1=Part.makeCylinder(r1,b1,FreeCAD.Vector(0,-0.5,-b1/2)) | |
cyl2=Part.makeCylinder(r1,b1,FreeCAD.Vector(0,laengemm+0.5,-b1/2)) | |
box=Part.makeBox(r1*2,laengemm+1,b1,FreeCAD.Vector(-r1,-0.5,-b1/2)) | |
return box.fuse(cyl1).fuse(cyl2).removeSplitter() | |
def shape_i_strebe(laengemm=60,subtractbox=True): | |
import FreeCAD | |
import Part | |
posshape=shape_strebe(laengemm) | |
oen1=shape_oese_negativ() | |
oen2=oen1.copy() | |
oen2.Placement=FreeCAD.Placement(FreeCAD.Vector(0,laengemm,0),FreeCAD.Rotation()) | |
beforebox = posshape.cut(oen1).cut(oen2) | |
if not subtractbox: | |
return beforebox | |
else: | |
boxshape1=Part.makeBox(4,laengemm-9,1,FreeCAD.Vector(-2,4.5,0.8)) | |
boxshape2=Part.makeBox(4,laengemm-9,1,FreeCAD.Vector(-2,4.5,-1.8)) | |
return beforebox.cut(boxshape1).cut(boxshape2) | |
def shape_zapfen(hoehe=2.6): | |
import FreeCAD | |
import Part | |
cyl1=Part.makeCylinder(2,8) | |
cyl1.Placement=FreeCAD.Placement(FreeCAD.Vector(0,-2.1,-4),FreeCAD.Rotation(-90,0.0,0.0)) | |
nut1=cyl1.fuse(\ | |
Part.makeBox(3,2,8,FreeCAD.Vector(-1.5,-2,-4))).common(\ | |
Part.makeBox(8,2.8,8,FreeCAD.Vector(-4,- hoehe,-4))).removeSplitter() | |
nut2=nut1.copy() | |
nut2.Placement=FreeCAD.Placement(FreeCAD.Vector(),FreeCAD.Rotation(0,90,0)) | |
return nut1.common(nut2) | |
def shape_nut_flach(laengezmm=15,hoehe=2.8,breite=3.1): | |
import FreeCAD | |
import Part | |
cyl1=Part.makeCylinder(2,laengezmm) | |
cyl1.Placement=FreeCAD.Placement(FreeCAD.Vector(0,-2.1,0),FreeCAD.Rotation(90,0.0,0.0)) | |
return cyl1.fuse(\ | |
Part.makeBox(breite,2,laengezmm,FreeCAD.Vector(-breite/2.0,-2,0))).common(\ | |
Part.makeBox(8,hoehe,laengezmm,FreeCAD.Vector(-4,-hoehe,0))).removeSplitter() | |
def shape_nut(laengezmm=15,breite=3.0): | |
import FreeCAD | |
import Part | |
cyl1=Part.makeCylinder(2,laengezmm) | |
cyl1.Placement=FreeCAD.Placement(FreeCAD.Vector(0,-2.1,0),FreeCAD.Rotation(90,0,0)) | |
return cyl1.fuse( Part.makeBox(breite,2,laengezmm,FreeCAD.Vector(-breite/2.0,-2,0)))\ | |
.removeSplitter() | |
def shape_zapfen_bs(hoehe=2.4,withstandoff=False): | |
import FreeCAD | |
import Part | |
zapfen=shape_zapfen(hoehe) | |
cyl1=Part.makeCylinder(0.4,8) | |
box=Part.makeBox(0.8,2,8,FreeCAD.Vector(-0.4,0,0)) | |
slot=box.fuse(cyl1).removeSplitter() | |
slot.Placement=FreeCAD.Placement(FreeCAD.Vector(-2,-2.1,-2),\ | |
FreeCAD.Rotation(180,-45,0)) | |
cyl2=Part.makeCylinder(2.35,5) | |
cyl2.Placement=FreeCAD.Placement(FreeCAD.Vector(), FreeCAD.Rotation(0,0,90)) | |
box1=Part.makeBox(9,1,1.2,FreeCAD.Vector(-4.5,-0.1,-0.6)) | |
box2=box1.copy() | |
box2.Placement=FreeCAD.Placement(FreeCAD.Vector(), FreeCAD.Rotation(0,90,0)) | |
withoutstandoff=zapfen.cut(slot).common(cyl2) | |
if not withstandoff: | |
return withoutstandoff | |
else: | |
standoff=box1.fuse(box2).removeSplitter() | |
standoff.Placement=FreeCAD.Placement(FreeCAD.Vector(), FreeCAD.Rotation(0,45,0)) | |
return withoutstandoff.fuse(standoff) | |
def shape_baustein(laengemm=15,zapfenhinten=False,bohrungen=0): | |
import FreeCAD | |
import Part | |
box = Part.makeBox(15,laengemm,15,FreeCAD.Vector(-7.5,0,-7.5)) | |
if bohrungen > 0: | |
bb1=Part.makeBox(20,4.15,4.15,FreeCAD.Vector(-10,-2.075,-2.075)) | |
bb2=Part.makeBox(4.15,4.15,20,FreeCAD.Vector(-2.075,-2.075,-10)) | |
bohrung=bb1.fuse(bb2).removeSplitter() | |
for bi in xrange(bohrungen): | |
bohrung.Placement=FreeCAD.Placement(\ | |
FreeCAD.Vector(0,(bi+1)*(laengemm/(bohrungen+1.0)),0),\ | |
FreeCAD.Rotation()) | |
box=box.cut(bohrung) | |
# box.fuse(shape_zapfen(2.4)) | |
nut0=shape_nut(laengemm) | |
nut1=nut0.copy() | |
nuty=nut0.copy() | |
nut1.Placement=FreeCAD.Placement(FreeCAD.Vector(0,0,-7.5), FreeCAD.Rotation(0,0,-90)) | |
#nut2=nut0.copy() | |
nut0.Placement=FreeCAD.Placement(FreeCAD.Vector(0,0,7.5),FreeCAD.Rotation(180,0,90)) | |
nutz=nut1.fuse(nut0) | |
nutx=nutz.copy() | |
nutx.Placement=FreeCAD.Placement(FreeCAD.Vector(),FreeCAD.Rotation(0,90,0)) | |
nuty.Placement=FreeCAD.Placement(FreeCAD.Vector(-7.5,laengemm,0),\ | |
FreeCAD.Rotation(0,90,0)) | |
boxny0=Part.makeBox(4.4,4.4,4,FreeCAD.Vector(-7.5 ,laengemm-4.4,-2)) | |
boxny1=Part.makeBox(4.4,4.4,4,FreeCAD.Vector(7.5-4.4,laengemm-4.4,-2)) | |
zapfenv=shape_zapfen_bs(2.4) | |
nutxz=nutx.fuse(nutz) | |
if zapfenhinten: | |
zapfenh=zapfenv.copy() | |
zapfenh.Placement=FreeCAD.Placement(FreeCAD.Vector(0,laengemm,0),\ | |
FreeCAD.Rotation(0,0,180)) | |
return box.fuse(zapfenv).fuse(zapfenh).cut(nutxz) | |
else: | |
nut=nutxz.fuse(nuty.fuse(boxny0.fuse(boxny1))) | |
return box.cut(nut).removeSplitter().fuse(zapfenv) | |
def shape_winkeltraeger(laengemm=60): | |
import FreeCAD | |
import Part | |
boxo=Part.makeBox(15,laengemm,15,FreeCAD.Vector(-7.5,0,-7.5)) | |
boxi=Part.makeBox(13,laengemm-5.5,13,FreeCAD.Vector(-5.5,2,-5.5)) | |
traeger1=boxo.cut(boxi) | |
nut=shape_nut_flach() | |
nut.Placement=FreeCAD.Placement(FreeCAD.Vector(0,laengemm,-2.5),FreeCAD.Rotation()) | |
traeger1=boxo.cut(boxi).cut(nut).fuse(shape_zapfen()) | |
oese1=shape_oese_winkeltraeger() | |
oese2=oese1.copy() | |
oese1.Placement=FreeCAD.Placement(FreeCAD.Vector(0,0,-7.5),FreeCAD.Rotation()) | |
oese2.Placement=FreeCAD.Placement(FreeCAD.Vector(-7.5,0,0),FreeCAD.Rotation(0,90,0)) | |
oesep=oese1.fuse(oese2) | |
len1=laengemm // 15 | |
for i in range(len1): | |
oesep.Placement=FreeCAD.Placement(FreeCAD.Vector(0,7.5+i*15),FreeCAD.Rotation()) | |
traeger1=traeger1.cut(oesep) | |
return traeger1 | |
def shape_x_strebe(laengemm=60,subtractbox=True): | |
import FreeCAD | |
import Part | |
len1=laengemm // 15 | |
tmpshape=shape_strebe(laengemm) | |
oesetemplate=shape_oese_negativ() | |
boxshape=Part.makeBox(4,7,1) | |
for i in range(len1+1): | |
oesetemplate.Placement=FreeCAD.Placement(FreeCAD.Vector(0,15.0*i,0),FreeCAD.Rotation()) | |
tmpshape=tmpshape.cut(oesetemplate) | |
if subtractbox and i != len1: | |
boxshape.Placement=FreeCAD.Placement(FreeCAD.Vector(-2,4+15.0*i,0.8),FreeCAD.Rotation()) | |
tmpshape=tmpshape.cut(boxshape) | |
boxshape.Placement=FreeCAD.Placement(FreeCAD.Vector(-2,4+15.0*i,-1.8),FreeCAD.Rotation()) | |
tmpshape=tmpshape.cut(boxshape) | |
return tmpshape | |
def parsealldefs(): | |
partsdict={} | |
for relfilename in os.listdir(ftdpath+'/def'): | |
if relfilename.endswith('.def'): | |
partpath="%s/def/%s" % (ftdpath,relfilename) | |
partdef= def2list(partpath) | |
partdefdict = ftlist2ftdict(partdef) | |
if 'COMMON' in partdefdict and 'ORGNUMBER' in partdefdict['COMMON']: | |
key=partdefdict['COMMON']['ORGNUMBER'] | |
else: | |
key=relfilename.split('.',1)[0] | |
partsdict[key]=partdefdict | |
return partsdict | |
def connsforpartsdict(): | |
return dict([(key,extractconnectionsfromdict(partdefdict)) for key,partdefdict in parsealldefs().iteritems()]) | |
def partswithoffsetrot(): | |
for key,value in parsealldefs().iteritems(): | |
offset=value['OFFSET'] | |
if 'RX' in offset and (floatg(offset['RX']) or floatg(offset['RY']) or floatg(offset['RZ'])): | |
yield (key,value) | |
def alignftparts(placementbasepart,placementconnbp,placementconnmp): #dysfunctional | |
return placementbasepart.multiply(placementconnbp).multiply(placementconnmp.inverse()) | |
#return placementconnbp.multiply(placementconnmp.inverse()) | |
#return placementbasepart.multiply(placementconnmp.inverse()).multiply(placementconnbp) | |
#return placementconnmp.inverse().multiply(placementconnbp).multiply(placementbasepart) | |
def alignftpobjs(baseobj,moveableobj,baseconn=0,moveableconn=0): | |
#allpartsconndict = allpartsconndict or connsforpartsdict() | |
basepartname=fclabeltoftpart(baseobj.Label,baseobj.Name)[2] | |
basepartconns=getpartconnectionsbypartname(basepartname) #allpartsconndict[basepartname] | |
moveablepartname=fclabeltoftpart(moveableobj.Label,moveableobj.Name)[2] | |
moveablepartconns=getpartconnectionsbypartname(moveablepartname) #allpartsconndict[moveablepartname] | |
if isinstance(baseconn, int): | |
baseconnobj=basepartconns[baseconn] | |
else: | |
pass | |
#TBD: String search for connection name | |
if isinstance(moveableconn, int): | |
moveableconnobj=moveablepartconns[moveableconn] | |
else: | |
pass | |
#TBD: String search for connection name | |
correctedplacement=FreeCAD.Placement(baseobj.Placement.Base.sub(baseobj.Placement.Rotation.multVec(getoffset(basepartname))),baseobj.Placement.Rotation) | |
newplacement=alignftparts(correctedplacement,baseconnobj.Placement,moveableconnobj.Placement) | |
moveableobj.Placement=FreeCAD.Placement(newplacement.Base.add(newplacement.Rotation.multVec(getoffset(moveablepartname))),newplacement.Rotation) | |
class ftpart(): | |
def addtofreecad(self,doc=None,drawconns=True,drawchildren=True,placement=None,\ | |
name=None,labelprefix='',color=None): | |
import FreeCAD | |
if placement is None: | |
placement = FreeCAD.Placement() | |
#Todo Support Child Objects | |
partfname= self.getfullname() | |
if not labelprefix: | |
longname = '%s_%s' % (self.shortpartnum,partfname) | |
else: | |
longname = '%s_%s' % (labelprefix,partfname) | |
import Mesh | |
obj=self.addtofreecadobj(doc,name or self.shortpartnum) | |
obj.Label=longname | |
obj.Placement=placement.multiply(self.homepos()) | |
if FreeCAD.GuiUp: | |
obj.ViewObject.ShapeColor = color or self.getcolor() | |
obj.ViewObject.Transparency=self.gettransparancy() | |
if hasattr(obj.ViewObject,'CreaseAngle'): | |
obj.ViewObject.CreaseAngle = 12.00 | |
if drawchildren: | |
for ChildPartNum in self.children: | |
findpart(ChildPartNum).addtofreecad(doc,drawconns=False,drawchildren=True) | |
if drawconns: | |
drawconnections(self.partdefdict,FreeCAD.Vector(),FreeCAD.Rotation(),self.shortpartnum) | |
return obj | |
class ftpartmesh(ftpart): | |
def _parsedef(self,str1): | |
ftobjects=[] | |
for line in str1.splitlines(): | |
if line: | |
lines=line.strip() | |
if lines.startswith('[') and lines.endswith(']'): | |
newobj={'id':lines[1:-1].upper()} | |
ftobjects.append(newobj); | |
elif '=' in lines: | |
key, value = lines.split('=',1) | |
newobj[key.upper()]=value | |
retdict={} | |
for item in ftobjects: | |
id = item['id'] | |
retdict[id]=item.copy() | |
self.partdefdict=retdict | |
#saveconnections | |
connlist=[] | |
for secname,secdict in self.partdefdict.iteritems(): | |
if secname.startswith('CONN') and secname != 'CONNECTIONS': | |
try: | |
connlist.append(connection(secdict)) | |
except Exception as e: | |
print str(secdict).encode('utf8'),e | |
raise | |
connlist.sort(key=lambda x :x.num) | |
self.connections=connlist | |
#safechildren | |
children=[] | |
for secname,secdict in self.partdefdict.iteritems(): | |
if secname.startswith('CHILD') and secname != 'CHILDS': | |
children.append((int(secname[5:]),secdict['NUMBER'])) | |
children.sort()#key=lambda x:x[0]) | |
if len(children) > 0: | |
self.children=zip(*children)[1] | |
else: | |
self.children=() | |
#if 'COMMON' in partdefdict and 'ORGNUMBER' in partdefdict['COMMON']: | |
# key=partdefdict['COMMON']['ORGNUMBER'] | |
#else: | |
# key=relfilename.split('.',1)[0] | |
#partsdict[key]=partdefdict | |
def addtofreecadobj(self,doc,name=None): | |
import Mesh | |
meshobj=doc.addObject('Mesh::Feature',name or self.shortpartnum) | |
meshobj.Mesh=self.addmeshtofreecad() | |
return meshobj | |
def getfullname(self): | |
return self.partdefdict['COMMON'][partnamelang].decode('latin1') #Part name | |
def getoffset(self): | |
import FreeCAD | |
return FreeCAD.Vector(floatg(self.partdefdict['OFFSET']['X']),\ | |
floatg(self.partdefdict['OFFSET']['Y']),\ | |
floatg(self.partdefdict['OFFSET']['Z'])) | |
def homepos(self): | |
import FreeCAD | |
return FreeCAD.Placement(self.getoffset(),FreeCAD.Rotation()) | |
#FreeCAD.Placement(pos.add(rot.multVec(partoffset)),rot) | |
def gettransparancy(self): | |
if 'ALPHA' in self.partdefdict['COLOR']: | |
return ((1-floatg(self.partdefdict['COLOR']['ALPHA']))*100) | |
else: | |
return 0 | |
def getcolor(self): | |
partlibcolordict=self.partdefdict['COLOR'] | |
return int(partlibcolordict['RED'])/255.0,\ | |
int(partlibcolordict['GREEN'])/255.0, \ | |
int(partlibcolordict['BLUE'])/255.0 | |
class ftpartinternal(ftpart): | |
import FreeCAD | |
P=FreeCAD.Placement | |
V=FreeCAD.Vector | |
R=FreeCAD.Rotation | |
internalparts={\ | |
# ( (func,args,kwargs), placement, color ... ) | |
#Building blocks | |
'32879':((shape_baustein,(30,),{}),P(V(-30,0,7.5),R(-90,0,0)), ), #bs30sw | |
'16251':((shape_baustein,(30,),{}),P(V(-30,0,7.5),R(-90,0,0)), ), #bs30rt | |
'36528':((shape_baustein,(30,),{}),P(V(-30,0,7.5),R(-90,0,0)), ), #bs30ge | |
#'C0050':(), #standoff bs sw | |
'32880':((shape_baustein,(30,),{'bohrungen':1}),P(V(0,0,30),R(-90,0,-90)), ), #bs30sw hole | |
'32881':((shape_baustein,(15,),{}),P(), ), #bs15sw | |
'16252':((shape_baustein,(15,),{}),P(), ), #bs15rt | |
'36529':((shape_baustein,(15,),{}),P(), ), #bs15ge | |
#'C0051':(), #standoff bs sw | |
'32882':((shape_baustein,(15,True),{}),P(), ), #bs15dual | |
#31003-31005 #old building blocks | |
#struts | |
'38544':((shape_i_strebe,(15,),{}),P(V(0,-15,0),R()), ), #istrut15ge | |
'31764':((shape_i_strebe,(30,),{}),P(V(0,-15,0),R()), ), #istrut30ge | |
'36328':((shape_i_strebe,(45,),{}),P(V(0,-22.5,0),R()), ), #istrut45ge | |
'36322':((shape_i_strebe,(60,),{}),P(V(0,-30,0),R()), ), #istrut60ge | |
'32200':((shape_i_strebe,(75,),{}),P(V(0,-37.5,0),R()), ), #istrut75ge | |
'31765':((shape_i_strebe,(90,),{}),P(V(0,-45,0),R()), ), #istrut90ge | |
'36335':((shape_i_strebe,(120,),{}),P(V(0,-60,0),R()), ), #istrut120ge | |
'38542':((shape_i_strebe,(42.426,),{}),P(), ), #istrut42.4ge | |
'36326':((shape_i_strebe,(63.640,),{}),P(), ), #istrut63.6ge | |
'35058':((shape_i_strebe,(84.852,),{}),P(), ), #istrut84.8ge | |
'35059':((shape_i_strebe,(106.066,),{}),P(), ), #istrut106ge | |
'36336':((shape_i_strebe,(196.706,),{}),P(), ), #istrut196.6ge | |
'36914':((shape_i_strebe,(15,),{}),P(V(0,-15,0),R()), ), #istrut15gr | |
'36924':((shape_i_strebe,(120,),{}),P(V(0,-60,0),R()), ), #istrut120gr | |
'36370':((shape_i_strebe,(63.640,),{}),P(V(0,-31.8198,0),R()), ), #istrut63.6gr | |
'35057':((shape_i_strebe,(106.066,),{}),P(), ), #istrut106gr | |
'38538':((shape_x_strebe,(30,),{}),P(V(0,-15,0),R()), ), #xstrut30ge | |
'38541':((shape_x_strebe,(45,),{}),P(), ), #xstrut45ge | |
'38540':((shape_x_strebe,(60,),{}),P(), ), #xstrut60ge | |
'38545':((shape_x_strebe,(75,),{}),P(), ), #xstrut75ge | |
'38531':((shape_x_strebe,(90,),{}),P(), ), #xstrut90ge | |
'35060':((shape_x_strebe,(120,),{}),P(), ), #xstrut120ge | |
'36912':((shape_x_strebe,(30,),{}),P(), ), #xstrut30gr | |
'36913':((shape_x_strebe,(45,),{}),P(), ), #xstrut45gr | |
'36952':((shape_x_strebe,(60,),{}),P(), ), #xstrut60gr | |
'36923':((shape_x_strebe,(75,),{}),P(), ), #xstrut75gr | |
'38543':((shape_x_strebe,(90,),{}),P(), ), #xstrut90gr | |
'38546':((shape_x_strebe,(120,),{}),P(), ), #xstrut120gr | |
#angle gr | |
'35053':((shape_winkeltraeger,(15,),{}),P(), ), #angleg15ge | |
'36922':((shape_winkeltraeger,(15,),{}),P(), ), #angleg15sw | |
'27471':((shape_winkeltraeger,(15,),{}),P(), ), #angleg15rt | |
#zwei zapfen 36298,36950 | |
'36299':((shape_winkeltraeger,(30,),{}),P(), ), #angleg30ge | |
'27472':((shape_winkeltraeger,(30,),{}),P(), ), #angleg30rt | |
'36920':((shape_winkeltraeger,(30,),{}),P(), ), #angleg30sw | |
'36297':((shape_winkeltraeger,(60,),{}),P(), ), #angleg60ge | |
'36921':((shape_winkeltraeger,(60,),{}),P(), ), #angleg60sw | |
'78728':((shape_winkeltraeger,(60,),{}),P(), ), #angleg60rt | |
'32223':((shape_winkeltraeger,(120,),{}),P(), ), #angleg120rt | |
'36294':((shape_winkeltraeger,(120,),{}),P(), ), #angleg120ge | |
'36293':((shape_winkeltraeger,(120,),{}),P(), ), #angleg120sw | |
#def shape_winkeltraeger(laengemm=60): | |
} | |
def __init__(self,fname): | |
if fname.upper() in self.internalparts: | |
self.shortpartnum=fname | |
else: | |
raise KeyError #no BREP available | |
def getfullname(self): | |
return self.shortpartnum #ToDo | |
def homepos(self): | |
return self.internalparts[self.shortpartnum][1] | |
def getcolor(self): | |
return 0.5,0.5,0.5 | |
def gettransparancy(self): | |
return 0 | |
#import FreeCAD | |
#return FreeCAD.Placement(partoffset,FreeCAD.Rotation()) | |
# def getoffset(self): | |
# import FreeCAD | |
# return FreeCAD.Vector() | |
def addtofreecadobj(self,doc,name=None): | |
import Part | |
obj=doc.addObject('Part::Feature',name or self.shortpartnum) | |
func,args,kwargs=self.internalparts[self.shortpartnum][0] | |
obj.Shape=func(*args,**kwargs) | |
return obj | |
class ftpartfromfile(ftpartmesh): | |
def __init__(self,fname): | |
import struct | |
f1=pythonopen(fname) | |
partheader=f1.read(45) | |
self.shortpartnum=partheader[40:45] | |
filelengths=struct.unpack('IIIIII',partheader[16:40]) | |
filesdict={'partnum':self.shortpartnum} | |
for key,length in zip(('stl','bmp','def','lowresstl','jpg','nrm'), filelengths): | |
filesdict[key]=f1.read(length) | |
self.filesdict=filesdict | |
self._parsedef(filesdict['def']) | |
def addmeshtofreecad(self,highres=True): | |
if highres: | |
stlstr=self.filesdict['stl'] | |
else: | |
stlstr=self.filesdict['lowresstl'] | |
import FreeCAD,Mesh | |
import os,tempfile,time | |
dir1=tempfile.gettempdir() | |
#inputfilename=os.path.join(dir1,'pisc-%s.stl' % self.shortname) | |
inputfilename=tempfile.mktemp('-%s.stl'%self.shortpartnum) | |
inputfile = pythonopen(inputfilename,'wb') | |
inputfile.write(stlstr) | |
inputfile.close() | |
partmesh = Mesh.read(inputfilename) | |
os.unlink(inputfilename) | |
return partmesh | |
class ftpartformrepository(ftpartmesh): | |
def __init__(self,shortnum): | |
import os | |
if os.path.isfile(shortnum): #full path of def | |
pdir,pname = os.path.split(shortnum) | |
self.shortpartnum=pname.rsplit('.',1)[0] | |
self.libpath=os.path.normpath('%s/..'%pdir) | |
deffilename=shortnum | |
else: | |
self.shortpartnum=shortnum | |
deffilename=ftdpath+'/def/%s.def'% shortnum | |
self.libpath=ftdpath | |
deffile=pythonopen(deffilename) | |
self._parsedef(deffile.read()) | |
def addmeshtofreecad(self,highres=True): | |
import FreeCAD,Mesh | |
if highres: | |
stldir='hires' | |
else: | |
stldir='lores' | |
#partname=os.path.splitext(os.path.basename(filename))[0] #use self.shortname | |
partmeshfilename='%s/%s/%s.stl'%(self.libpath,stldir,self.shortpartnum) | |
partmesh = Mesh.read(partmeshfilename) | |
return partmesh | |
class ftmodel(): | |
def __init__(self,filename): | |
self.modellist=def2list(filename) | |
def addtofreecad(self,doc=None,drawconns=True,drawchildren=True): | |
import FreeCAD | |
groups={} | |
for ftpartobj in self.modellist: | |
if ftpartobj['id'].startswith('PART'): | |
groupname='phase%02d' % int(ftpartobj['PHASE']) | |
if groupname not in groups: | |
groups[groupname] = doc.addObject("App::DocumentObjectGroup",groupname) | |
partname=ftpartobj['NAME'].split('_')[0][1:] #revove trailing F from Part Number | |
pos=FreeCAD.Vector(floatg(ftpartobj['XPOS'])*10,floatg(ftpartobj['YPOS'])*10,floatg(ftpartobj['ZPOS'])*10) | |
dirz=FreeCAD.Vector(floatg(ftpartobj['XDIR']),floatg(ftpartobj['YDIR']),floatg(ftpartobj['ZDIR'])) | |
upy=FreeCAD.Vector(floatg(ftpartobj['XUP']),floatg(ftpartobj['YUP']),floatg(ftpartobj['ZUP'])) | |
missingx=upy.cross(dirz) #first row of rotation matrix | |
rotmat=FreeCAD.Matrix(missingx[0],upy[0],dirz[0],0,missingx[1],upy[1],dirz[1],0,missingx[2],upy[2],dirz[2]) | |
rot=FreeCAD.Placement(rotmat).Rotation | |
shortname ='N%04d_%s' %(int(ftpartobj['id'][4:]),ftpartobj['NAME'][1:]) | |
longname ='P%02d_%s' %(int(ftpartobj['PHASE']),shortname) | |
bgr=int(ftpartobj['COLOR']) | |
partmodelcolor=(bgr & 0x0000ff ) /(0x0000ff*1.0),(bgr & 0x00ff00 ) /(0x00ff00*1.0),(bgr & 0xff0000 ) /(0xff0000*1.0) | |
obj=findpart(partname).addtofreecad(doc,drawconns=False,drawchildren=False,\ | |
placement=FreeCAD.Placement(pos,rot),name=shortname,labelprefix=longname,color=partmodelcolor) | |
groups[groupname].addObject(obj) | |
def compareparts(partname): | |
import FreeCAD | |
doc=FreeCAD.ActiveDocument | |
rpart = ftpartformrepository(partname) | |
ipart = ftpartinternal(partname) | |
rpart.addtofreecad(doc,drawconns=False,drawchildren=False) | |
ipart.addtofreecad(doc,drawconns=False,drawchildren=False) | |
def findpart(partname): | |
part = None | |
try: | |
part = ftpartinternal(partname) | |
except KeyError: | |
part = ftpartformrepository(partname) | |
return part | |
def registerFCTypes(): | |
import FreeCAD | |
FreeCAD.addExportType("fischertechnik designer Model (*.ftm)","piscator") | |
FreeCAD.addImportType("fischertechnik designer Model (*.ftm)","piscator") | |
FreeCAD.addImportType("fischertechnik designer Part (*.def)","piscator") | |
FreeCAD.addImportType("fischertechnik designer Part (*.part)","piscator") | |
if __name__ == '__main__': | |
pass | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment