Idf.py

Go to the documentation of this file.
00001 #***************************************************************************
00002 #*   (c) Milos Koutny (milos.koutny@gmail.com) 2010                        *
00003 #*                                                                         *
00004 #*   This file is part of the FreeCAD CAx development system.              *
00005 #*                                                                         *
00006 #*   This program is free software; you can redistribute it and/or modify  *
00007 #*   it under the terms of the GNU Library General Public License (LGPL)   *
00008 #*   as published by the Free Software Foundation; either version 2 of     *
00009 #*   the License, or (at your option) any later version.                   *
00010 #*   for detail see the LICENCE text file.                                 *
00011 #*                                                                         *
00012 #*   FreeCAD is distributed in the hope that it will be useful,            *
00013 #*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00014 #*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00015 #*   GNU Library General Public License for more details.                  *
00016 #*                                                                         *
00017 #*   You should have received a copy of the GNU Library General Public     *
00018 #*   License along with FreeCAD; if not, write to the Free Software        *
00019 #*   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
00020 #*   USA                                                                   *
00021 #*                                                                         *
00022 #*   Milos Koutny 2010                                                     *
00023 #***************************************************************************/
00024 
00025 import FreeCAD, Part, os, FreeCADGui, draftGui
00026 from FreeCAD import Base
00027 from math import *
00028 
00029 ##########################################################
00030 # Script version dated 17-May-2010                     #
00031 ##########################################################
00032 #Configuration parameters below - use standard slashes / #
00033 ##########################################################
00034 
00035 #model_tab_filename="c:/libraries/3d_libraries/brno_cis/brno_footprints_models.csv"
00036 model_tab_filename = FreeCAD.getResourceDir()+ "Mod/Idf/lib/footprints_models.csv"
00037 #step_path="c:/libraries/3d_libraries/brno_cis/step/"
00038 step_path=FreeCAD.getResourceDir()+ "Mod/Idf/lib/"
00039 #model_tab_filename="c:/Libraries/3d_libraries/tandberg/footprints_models.csv"
00040 #step_path="c:/Libraries/3d_libraries/tandberg/step/"
00041 ignore_hole_size=1 # size in MM to prevent huge number of drilled holes
00042 EmpDisplayMode=2 # 0='Flat Lines', 1='Shaded', 2='Wireframe', 3='Points'; recommended 2 or 0
00043 
00044 IDF_sort=0 # 0-sort per refdes [1 - part number (not preffered)/refdes] 2-sort per footprint/refdes
00045 
00046 IDF_diag=1 # 0/1=disabled/enabled output (footprint.lst/missing_models.lst) 
00047 IDF_diag_path="C:/temp" # path for output of footprint.lst and missing_models.lst
00048 
00049 
00050 ########################################################################################
00051 #              End config section do not touch code below                              #
00052 ########################################################################################
00053 
00054 pythonopen = open # to distinguish python built-in open function from the one declared here
00055 
00056 def open(filename):
00057         """called when freecad opens an Emn file"""
00058         docname = os.path.splitext(os.path.basename(filename))[0]
00059         doc = FreeCAD.newDocument(docname)
00060         FreeCAD.setActiveDocument(docname)
00061         message='Started with opening of "'+filename+'" file\n'
00062         FreeCAD.Console.PrintMessage(message)
00063         process_emn(doc,filename)
00064 
00065 def insert(filename,docname):
00066     """called when freecad imports an Emn file"""
00067     FreeCAD.setActiveDocument(docname)
00068     doc=FreeCAD.getDocument(docname)
00069     FreeCAD.Console.PrintMessage('Started import of "'+filename+'" file')
00070     process_emn(doc,filename)
00071         
00072 def process_emn(doc,filename):
00073    """process_emn(document, filename)-> adds emn geometry from emn file"""
00074    emnfile=pythonopen(filename, "r")
00075    emn_unit=1.0 #presume milimeter like emn unit
00076    emn_version=2 #presume emn_version 2
00077    board_thickness=0 #presume 0 board height
00078    board_outline=[] #no outline
00079    drills=[] #no drills
00080    placement=[] #no placement
00081    place_item=[] #empty place item
00082    emnlines=emnfile.readlines()
00083    emnfile.close()   
00084    passed_sections=[]
00085    current_section=""
00086    section_counter=0
00087    for emnline in emnlines:
00088        emnrecords=split_records(emnline)
00089        if emnrecords[0][0:4]==".END":
00090           passed_sections.append(current_section)
00091           current_section=""
00092        elif emnrecords[0][0]==".":
00093           current_section=emnrecords[0]
00094           section_counter=0
00095        section_counter+=1
00096        if current_section==".HEADER"  and section_counter==2:
00097           emn_version=int(float(emnrecords[1]))
00098           FreeCAD.Console.PrintMessage("Emn version: "+emnrecords[1]+"\n")
00099        if current_section==".HEADER"  and section_counter==3 and emnrecords[1]=="THOU":
00100           emn_unit=0.0254
00101           FreeCAD.Console.PrintMessage("UNIT THOU\n" )
00102        if current_section==".HEADER"  and section_counter==3 and emnrecords[1]=="TNM":
00103           emn_unit=0.000010
00104           FreeCAD.Console.PrintMessage("TNM\n" )
00105        if current_section==".BOARD_OUTLINE"  and section_counter==2:
00106           board_thickness=emn_unit*float(emnrecords[0])
00107           FreeCAD.Console.PrintMessage("Found board thickness "+emnrecords[0]+"\n")
00108        if current_section==".BOARD_OUTLINE"  and section_counter>2:
00109           board_outline.append([int(emnrecords[0]),float(emnrecords[1])*emn_unit,float(emnrecords[2])*emn_unit,float(emnrecords[3])])
00110        if current_section==".DRILLED_HOLES"  and section_counter>2 and float(emnrecords[0])*emn_unit>ignore_hole_size:
00111           drills.append([float(emnrecords[0])*emn_unit,float(emnrecords[1])*emn_unit,float(emnrecords[2])*emn_unit])
00112        if current_section==".PLACEMENT"  and section_counter>1 and fmod(section_counter,2)==0:
00113           place_item=[]
00114           place_item.append(emnrecords[2]) #Reference designator
00115           place_item.append(emnrecords[1]) #Component part number
00116           place_item.append(emnrecords[0]) #Package name
00117        if current_section==".PLACEMENT"  and section_counter>1 and fmod(section_counter,2)==1:
00118           place_item.append(float(emnrecords[0])*emn_unit) #X
00119           place_item.append(float(emnrecords[1])*emn_unit) #Y
00120           place_item.append(float(emnrecords[emn_version])) #Rotation
00121           place_item.append(emnrecords[emn_version+1]) #Side
00122           place_item.append(emnrecords[emn_version+2]) #Place Status
00123           FreeCAD.Console.PrintMessage(str(place_item)+"\n")
00124           placement.append(place_item)
00125    FreeCAD.Console.PrintMessage("\n".join(passed_sections))
00126    FreeCAD.Console.PrintMessage("Proceed "+str(Process_board_outline(doc,board_outline,drills,board_thickness))+" outlines\n")
00127    placement.sort(key=lambda param: (param[IDF_sort],param[0]))
00128    process_emp(doc,filename,placement,board_thickness)
00129    place_steps(doc,placement,board_thickness)
00130 
00131 def Process_board_outline(doc,board_outline,drills,board_thickness):
00132     """Process_board_outline(doc,board_outline,drills,board_thickness)-> number proccesed loops
00133 
00134         adds emn geometry from emn file"""
00135     vertex_index=-1; #presume no vertex
00136     lines=-1 #presume no lines
00137     out_shape=[]
00138     out_face=[]
00139     for point in board_outline:
00140        vertex=Base.Vector(point[1],point[2],0) 
00141        vertex_index+=1
00142        if vertex_index==0:
00143           lines=point[0] 
00144        elif lines==point[0]:
00145            if point[3]!=0 and point[3]!=360:
00146               out_shape.append(Part.Arc(prev_vertex,mid_point(prev_vertex,vertex,point[3]),vertex))
00147               FreeCAD.Console.PrintMessage("mid point "+str(mid_point)+"\n")
00148            elif point[3]==360:
00149               per_point=Per_point(prev_vertex,vertex)
00150               out_shape.append(Part.Arc(per_point,mid_point(per_point,vertex,point[3]/2),vertex))
00151               out_shape.append(Part.Arc(per_point,mid_point(per_point,vertex,-point[3]/2),vertex))
00152            else:
00153               out_shape.append(Part.Line(prev_vertex,vertex))
00154        else:
00155           out_shape=Part.Shape(out_shape)
00156           out_shape=Part.Wire(out_shape.Edges)
00157           out_face.append(Part.Face(out_shape))
00158           out_shape=[]
00159           vertex_index=0 
00160           lines=point[0] 
00161        prev_vertex=vertex
00162     if lines!=-1:
00163       out_shape=Part.Shape(out_shape)
00164       out_shape=Part.Wire(out_shape.Edges)
00165       out_face.append(Part.Face(out_shape))
00166       outline=out_face[0]
00167       FreeCAD.Console.PrintMessage("Added outline\n")
00168       if len(out_face)>1:
00169         for otl_cut in out_face[1: ]:
00170           outline=outline.cut(otl_cut)
00171           FreeCAD.Console.PrintMessage("Cutting shape inside outline\n")
00172       for drill in drills:
00173         FreeCAD.Console.PrintMessage("Cutting hole inside outline\n")
00174         out_shape=Part.makeCircle(drill[0]/2, Base.Vector(drill[1],drill[2],0))
00175         out_shape=Part.Wire(out_shape.Edges)
00176         outline=outline.cut(Part.Face(out_shape))
00177       doc_outline=doc.addObject("Part::Feature","Board_outline")
00178       doc_outline.Shape=outline 
00179       FreeCADGui.Selection.addSelection(doc_outline)
00180       FreeCADGui.runCommand("Draft_Upgrade")
00181       outline=FreeCAD.ActiveDocument.getObject("Union").Shape
00182       FreeCAD.ActiveDocument.removeObject("Union")
00183       doc_outline=doc.addObject("Part::Feature","Board_outline")
00184       doc_outline.Shape=outline.extrude(Base.Vector(0,0,-board_thickness))
00185       grp=doc.addObject("App::DocumentObjectGroup", "Board_Geoms")
00186       grp.addObject(doc_outline)
00187       doc.Board_outline.ViewObject.ShapeColor=(0.0, 0.5, 0.0, 0.0)
00188     return lines+1
00189 
00190 def mid_point(prev_vertex,vertex,angle):
00191     """mid_point(prev_vertex,vertex,angle)-> mid_vertex
00192        
00193        returns mid point on arc of angle between prev_vertex and vertex"""
00194     angle=radians(angle/2)
00195     basic_angle=atan2(vertex.y-prev_vertex.y,vertex.x-prev_vertex.x)-pi/2
00196     shift=(1-cos(angle))*hypot(vertex.y-prev_vertex.y,vertex.x-prev_vertex.x)/2/sin(angle)
00197     midpoint=Base.Vector((vertex.x+prev_vertex.x)/2+shift*cos(basic_angle),(vertex.y+prev_vertex.y)/2+shift*sin(basic_angle),0)
00198     return midpoint
00199 
00200 def split_records(line_record):
00201     """split_records(line_record)-> list of strings(records)
00202        
00203        standard separator list separator is space, records containting encapsulated by " """
00204     split_result=[]
00205     quote_pos=line_record.find('"')
00206     while quote_pos!=-1:
00207        if quote_pos>0:
00208           split_result.extend(line_record[ :quote_pos].split())
00209           line_record=line_record[quote_pos: ]
00210           quote_pos=line_record.find('"',1)
00211        else: 
00212           quote_pos=line_record.find('"',1)
00213        if quote_pos!=-1:
00214           split_result.append(line_record[ :quote_pos+1])
00215           line_record=line_record[quote_pos+1: ]
00216        else:
00217           split_result.append(line_record) 
00218           line_record=""
00219        quote_pos=line_record.find('"')
00220     split_result.extend(line_record.split())
00221     return split_result
00222         
00223 def process_emp(doc,filename,placement,board_thickness):
00224    """process_emp(doc,filename,placement,board_thickness) -> place components from emn file to board"""
00225    filename=filename.partition(".emn")[0]+".emp"
00226    empfile=pythonopen(filename, "r")
00227    emp_unit=1.0 #presume milimeter like emn unit
00228    emp_version=2 #presume emn_version 2
00229    comp_height=0 #presume 0 part height
00230    comp_outline=[] #no part outline
00231    comp_GeometryName="" # no geometry name
00232    comp_PartNumber="" # no Part Number
00233    comp_height=0 # no Comp Height
00234    emplines=empfile.readlines()
00235    empfile.close()   
00236    passed_sections=[]
00237    current_section=""
00238    section_counter=0
00239    comps=[]
00240    for empline in emplines:
00241      emprecords=split_records(empline)
00242      if emprecords[0][0:4]==".END":
00243         current_section=""
00244         passed_sections.append(current_section)
00245         if comp_PartNumber!="":
00246           comps.append((comp_PartNumber,[Process_comp_outline(doc,comp_outline,comp_height),comp_GeometryName]))
00247           comp_PartNumber=""
00248           comp_outline=[]
00249      elif emprecords[0][0]==".":
00250         current_section=emprecords[0]
00251         section_counter=0
00252      section_counter+=1
00253      if current_section==".HEADER"  and section_counter==2:
00254         emp_version=int(float(emprecords[1]))
00255         FreeCAD.Console.PrintMessage("Emp version: "+emprecords[1]+"\n")
00256      if (current_section==".ELECTRICAL" or current_section==".MECHANICAL") and section_counter==2 and emprecords[2]=="THOU":
00257         emp_unit=0.0254
00258      if (current_section==".ELECTRICAL" or current_section==".MECHANICAL") and section_counter==2 and emprecords[2]=="MM":
00259         emp_unit=1
00260      if (current_section==".ELECTRICAL" or current_section==".MECHANICAL") and section_counter==2:
00261         comp_outline=[] #no part outline
00262         comp_GeometryName=emprecords[0] # geometry name
00263         comp_PartNumber=emprecords[1] # Part Number
00264         comp_height=emp_unit*float(emprecords[3]) # Comp Height
00265      if (current_section==".ELECTRICAL" or current_section==".MECHANICAL") and section_counter>2:
00266         comp_outline.append([float(emprecords[1])*emp_unit,float(emprecords[2])*emp_unit,float(emprecords[3])]) #add point of outline
00267    FreeCAD.Console.PrintMessage("\n".join(passed_sections))
00268    #Write file with list of footprint
00269    if IDF_diag==1:
00270      empfile=pythonopen(IDF_diag_path+"/footprint.lst", "w")
00271      for compx in comps:
00272        empfile.writelines(str(compx[1][1])+"\n")
00273      empfile.close()
00274    #End section of list footprint  
00275    comps=dict(comps)
00276    grp=doc.addObject("App::DocumentObjectGroup", "EMP Models")
00277    for place_item in placement:
00278      if comps.has_key(place_item[1]):
00279        doc_comp=doc.addObject("Part::Feature",place_item[0])
00280        FreeCAD.Console.PrintMessage("Adding EMP model "+str(place_item[0])+"\n")
00281        doc_comp.Shape=comps[place_item[1]][0]
00282        doc_comp.ViewObject.DisplayMode=EmpDisplayMode
00283        z_pos=0
00284        rotateY=0
00285        if place_item[6]=='BOTTOM':
00286           rotateY=pi
00287           z_pos=-board_thickness
00288        placmnt=Base.Placement(Base.Vector(place_item[3],place_item[4],z_pos),toQuaternion(rotateY,place_item[5]*pi/180,0))
00289        doc_comp.Placement=placmnt
00290        grp.addObject(doc_comp)
00291    return 1
00292 
00293 def Process_comp_outline(doc,comp_outline,comp_height):
00294     """Process_comp_outline(doc,comp_outline,comp_height)->part shape
00295        Create solid component shape base on its outline"""
00296     vertex_index=-1; #presume no vertex
00297     out_shape=[]
00298     if comp_outline==[]:  #force 0.2mm circle shape for components without place outline definition
00299        comp_outline.append([0.0,0.0,0.0])
00300        comp_outline.append([0.1,0.0,360.0])
00301     for point in comp_outline:
00302        vertex=Base.Vector(point[0],point[1],0) 
00303        vertex_index+=1
00304        if vertex_index>0:
00305          if point[2]!=0 and point[2]!=360:
00306             out_shape.append(Part.Arc(prev_vertex,mid_point(prev_vertex,vertex,point[2]),vertex))
00307             FreeCAD.Console.PrintMessage("mid point "+str(mid_point)+"\n")
00308          elif point[2]==360:
00309             per_point=Per_point(prev_vertex,vertex)
00310             out_shape.append(Part.Arc(per_point,mid_point(per_point,vertex,point[2]/2),vertex))
00311             out_shape.append(Part.Arc(per_point,mid_point(per_point,vertex,-point[2]/2),vertex))
00312          else:
00313             out_shape.append(Part.Line(prev_vertex,vertex))
00314        prev_vertex=vertex
00315     out_shape=Part.Shape(out_shape)
00316     out_shape=Part.Wire(out_shape.Edges)
00317     out_shape=Part.Face(out_shape)
00318     out_shape=out_shape.extrude(Base.Vector(0,0,comp_height))
00319     #Part.show(out_shape)
00320     return out_shape
00321 
00322 def place_steps(doc,placement,board_thickness):
00323     """ place_steps(doc,placement,board_thickness)->place step models on board 
00324 
00325         list of models and path to step files is set at start of this script
00326                  model_tab_filename= "" &   step_path="" """
00327     model_file=pythonopen(model_tab_filename, "r")
00328     model_lines=model_file.readlines()
00329     model_file.close()   
00330     model_dict=[]
00331     model_file=pythonopen(IDF_diag_path+"/missing_models.lst", "w")
00332     keys=[]
00333     #prev_step="*?.*?" #hope nobody will insert this step filename
00334     step_dict=[]
00335     for model_line in model_lines:
00336         model_records=split_records(model_line)  
00337         if len(model_records)>1 and model_records[0] and not model_records[0] in keys:
00338            keys.append(model_records[0])  
00339            model_dict.append((str(model_records[0]).replace('"',''),str(model_records[1]).replace('"','')))
00340     model_dict=dict(model_dict)
00341     validkeys=filter(lambda x:x in  [place_item[2] for place_item in placement], model_dict.keys())
00342     FreeCAD.Console.PrintMessage("Step models to be loaded for footprints: "+str(validkeys)+"\n")
00343     grp=doc.addObject("App::DocumentObjectGroup", "Step Models")
00344     for validkey in validkeys:
00345          step_dict.append((validkey,Part.read(step_path+"/"+model_dict[validkey])))
00346          FreeCAD.Console.PrintMessage("Reading step file "+str(model_dict[validkey])+" for footprint "+str(validkey)+"\n")
00347     step_dict=dict(step_dict)
00348     for place_item in placement:
00349       if step_dict.has_key(place_item[2]):
00350         step_model=doc.addObject("Part::Feature",place_item[0]+"_s")
00351         FreeCAD.Console.PrintMessage("Adding STEP model "+str(place_item[0])+"\n")
00352         #if prev_step!=place_item[2]:
00353         #   model0=Part.read(step_path+"/"+model_dict[place_item[2]])
00354         #   prev_step=place_item[2]
00355         step_model.Shape=step_dict[place_item[2]]
00356         z_pos=0
00357         rotateY=0
00358         if place_item[6]=='BOTTOM':
00359            rotateY=pi
00360            z_pos=-board_thickness
00361         placmnt=Base.Placement(Base.Vector(place_item[3],place_item[4],z_pos),toQuaternion(rotateY,place_item[5]*pi/180,0))
00362         step_model.Placement=placmnt
00363         grp.addObject(step_model)
00364       else: 
00365         if IDF_diag==1:
00366             model_file.writelines(str(place_item[0])+" "+str(place_item[2])+"\n")
00367     model_file.close() 
00368     
00369 def toQuaternion(heading, attitude,bank): # rotation heading=arround Y, attitude =arround Z,  bank attitude =arround X
00370     """toQuaternion(heading, attitude,bank)->FreeCAD.Base.Rotation(Quternion)"""
00371     c1 = cos(heading/2)
00372     s1 = sin(heading/2)
00373     c2 = cos(attitude/2)
00374     s2 = sin(attitude/2)
00375     c3 = cos(bank/2)
00376     s3 = sin(bank/2)
00377     c1c2 = c1*c2
00378     s1s2 = s1*s2
00379     w = c1c2*c3 - s1s2*s3
00380     x = c1c2*s3 + s1s2*c3
00381     y = s1*c2*c3 + c1*s2*s3
00382     z = c1*s2*c3 - s1*c2*s3
00383     return  FreeCAD.Base.Rotation(x,y,z,w)  
00384 
00385 def Per_point(prev_vertex,vertex):
00386     """Per_point(center,vertex)->per point
00387 
00388        returns opposite perimeter point of circle"""
00389     #basic_angle=atan2(prev_vertex.y-vertex.y,prev_vertex.x-vertex.x)
00390     #shift=hypot(prev_vertex.y-vertex.y,prev_vertex.x-vertex.x)
00391     #perpoint=Base.Vector(prev_vertex.x+shift*cos(basic_angle),prev_vertex.y+shift*sin(basic_angle),0)
00392     perpoint=Base.Vector(2*prev_vertex.x-vertex.x,2*prev_vertex.y-vertex.y,0)
00393     return perpoint
00394 
00395   

Generated on Wed Nov 23 19:00:18 2011 for FreeCAD by  doxygen 1.6.1