2d DXF to 3d model automation


#1

Hello All!

Apologies in advance for my noobie questions! I’m getting a
little lost in the Ruby API and could do with some pointers.

A bit of background: Pretty much all of my work in SU
involves importing a DXF and pulling into a ‘standard thickness’. Taking a PCB
outline from my PCB package and turning it into a 3d model.

From:


To:

The steps I take to achieve this are:

  1. File>Import DXF

  2. Select outline Tools>Utilities>Create Face

  3. Select All Edit>Intersect Faces>With Selection

  4. Select and Delete nested faces

  5. Select remaining face Pull to 1.6mm

  6. Save (to be imported into and assembly)

Using this as a model for my Ruby script I will add the API
methods.

  1. File>Import DXF
    UI.openpanel- toobtain DXF location
    Model.import- to import the DXF
    UI.inputbox- to obtain PCB thickness

  2. Select outline Tools>Utilities>Create Face
    Edge.find_faces – to createface. This was mentioned in this thread. How do I select an edge of the DXF? How should I Handle the DXF import?

  3. Select All Edit>Intersect Faces>With Selection
    Not sure what to do here! Help!

  4. Select and Delete nested faces
    I guess a loop is needed here using Face.area to
    find the face area and then delete all the smallest faces and leave only the
    largest? How do I find and select the nested faces?

  5. Select remaining face Pull to 1.6mm
    Face.pushpull to pull to thickness. Thickness taken from point 1

  6. Save (to be imported into and assembly)

Further to this adding a PNG as a texture to the top and bottom using UI.openpanel and Model.import would be very useful!

Is this approach reasonable? Please give me some pointers to head me in the correct direction!

I have attached my coding efforts so far.
AKDXF.rb (305 Bytes)
And a sample DXF.
Nest.dxf (6.9 KB)

Many Thanks Ed


Ruby API "Error opening AutoCad import file"
#2

Hello All!

I have made reasonable progress.

here is my code so far.

require 'sketchup.rb'

UI.menu("Extensions").add_item("AK PCB Tool"){
    SKETCHUP_CONSOLE.show
    ak_PCBTool #method call
}


#The pcb tool method
def ak_PCBTool

    # Vars for model
    mod = Sketchup.active_model
    ents = mod.active_entities
    
    # generate an open file dialog and store location in 'dxfLocation'
    # Only allow DXF import
    dxfLocation=UI.openpanel("Import PCB DXF", "", "DXF|*.dxf||") #cancel button not handled yet! todo

    #Generate user input box to get pcb thickness store in pcbInput array
    #akPrompts store name for imputbox array
    akPrompts = ["PCB Thickness"]
    #akDefaults store default value array .mm to give dimension to the array element
    akDefaults = [1.6.mm]
    pcbInput = UI.inputbox(akPrompts,akDefaults,"PCB Thickness") #cancel button not handled yet! todo
    #Take array element and store in var 'pcbThickness'
    pcbThickness = pcbInput.at(0)

    #improt dxf
    mod.import dxfLocation,false

    
    #find_face on all edges
        
    ents.each{|ent|
        if ent.typename == "Edge"
            #pcbEdges << ent
            ent.find_faces#this approach is brute force and not effiecnt but ensures all faces are found
            puts ent #verbose debug 
        end

    }
    
    
    #Create and populate an array of pcb faces. 
    pcbFaces = []
        ents.each{|ent|
    if ent.typename=="Face"
        pcbFaces << ent
        puts ent #verbose debug
    end    
    }
    
    puts "pcb faces:"
    puts pcbFaces.length #verbose debug
    
    
    
    #if there is one pcb face pull to pcbthickness
    if pcbFaces.length == 1
        pcbFace=pcbFaces[0]
        pcb=pcbFace.pushpull pcbThickness*-1# assumes pcb faces the same direction if pcb is ever pulled -ve to z this will need recoding to detect and correct
    end
        
end

So…

I can import my dxf, add faces to the imported edges and if I have a single face pull it to pcb thickness.

If I have more than one face I’m lost. How do I find the faces surrounded by a face? I guess I can then use face,erase!.

My first idea was to use face.area to find the smallest areas and erase them. the trouble with this is there is no guarantee that the smallest faces are the ones I want to erase.

Big Thanks Ed


#3

did you have a look at the code in this post?

it’s best to let users model units to work automatically…

akDefaults = ['1.6mm'.to_l] 

also, I’d consider adding a material to the pcb at the same time…
john


#4

Hi John,

Yes. I did have a look. I have probably misunderstood the thread. I will give it another look.

I see how that is better. noted and added.

I have not given materials much though yet. I plan to add .png to the top and bottom face in the future.
Is adding a material worth doing if I will be adding png textures later?

Ed


#5

you can add the png on import or add a ‘placeholder’ material to make swapping it later easy…

john


#6

Not sure it will fit your workflow, but this is a function that I use to do something very similar. Notice that it works only if the holes are all internal.

It uses the intersect_with method and requires:

  • a group of faces (faces to be cut). In your case this would be the face generated by the outerloop
  • an array of ‘tools’. This is an array of solid groups. In your case a tool would be generated by pushpulling the internal faces.

This is the code:

def self.cut_surfaces(tools, surface_group) #log
  #puts 'BEFORE INTERSECTION'
  #puts 'All entities in group'
  #puts surface_group.entities.each{|e| p e}
  #surface.entities.select{|e| e.is_a?(Sketchup::Face)}.each{|e| p e.to_s}
  orig_surfaces = surface_group.entities.select{|s| s.is_a?(Sketchup::Face)}
  tools.each do |t|
    arrays  = t.entities.intersect_with false, t.transformation, surface_group.entities, surface_group.transformation, false, surface_group
  end
  #puts 'AFTER INTERSECTION'
  #puts 'All surfaces in group'
  #surface_group.entities.grep(Sketchup::Face).each{|e| p e.to_s}

  del = surface_group.entities.grep(Sketchup::Face).select{|e| !orig_surfaces.include?(e)}
  #puts 'Surfaces to be deleted'
  #del.each{|e| p e}
  del.each do |e|
    surface_group.entities.erase_entities(e)
  end
  return true
end

Hope this helps.


#7

Hello All,

Thank you all for your help.

I have my script working quite well. I thought i would share my code here.

require 'sketchup.rb'

UI.menu("Extensions").add_item("AK PCB Tool 1.1"){    
    SKETCHUP_CONSOLE.show
    ak_PCBTool
}
#The pcb tool method
def ak_PCBTool
    
    # Vars for model
    mod = Sketchup.active_model
    ents = mod.active_entities
    pcbview = Sketchup.active_model.active_view
                
    # generate an open file dialog and store location in 'dxfLocation'
    dxfLocation=UI.openpanel("Import PCB DXF", "", "DXF|*.dxf||")
    
    if dxfLocation == nil
        puts "cancelled at DXF in!"
    else

        #Generate user input box to get pcb thickness store in pcbInput array
        #akPrompts store name for imputbox array
        akPrompts = ["PCB Thickness"]
        #akDefaults store default value array .mm to give dimension to the array element
        akDefaults = ['1.6.mm'.to_l]
        pcbInput = UI.inputbox(akPrompts,akDefaults,"PCB Thickness")
        #Take array element and store in var 'pcbThickness'
        if pcbInput == false
            puts "cancelled at Thickness!"
        else
            pcbThickness = pcbInput.at(0)

            
            #improt dxf
            mod.import dxfLocation,false

            #find_face on all edges
            totNumEdges=0
            pcbEdges=[]
            
            ents.each{|ent|
                if ent.typename == "Edge"
                    pcbEdges<<ent
                end

             }
            totNumEdges=pcbEdges.length
            
            puts "Total number of edges: " + totNumEdges.to_s
            
            faceCount=0
            
            while pcbEdges.length>0 do
                faceCount=faceCount+1
                anEdge=pcbEdges.at(0)
                connectedE=anEdge.all_connected
                anEdge.find_faces
                pcbEdges=pcbEdges-connectedE
                puts "face " + faceCount.to_s + " found"
            
            end
                        
             #Create and populate an array of pcb faces. 
             pcbFaces = []
             ents.each{|ent|
                 if ent.typename=="Face"
                     pcbFaces << ent
                end    
             }
            
             puts "pcb faces: " + pcbFaces.length.to_s #verbose debug
            
             puts "Removing inner faces and pull to thickness"
             faceCount=0
             pcbFaces.each{|face|
             faceCount=faceCount+1
                 faceEdges=face.edges        
                
                 if faceEdges.length==totNumEdges
                     face.pushpull pcbThickness*-1
                     puts "Face " + faceCount.to_s + " is the PCB face. Pulled to Thickness"
                 else
                     face.erase!
                     puts "Face " + faceCount.to_s + " is a void. Face erased"
                 end        
             }
            
            pcbview.zoom_extents
            
            puts "Done, you can now close the Ruby Console "
            
        end
    end
            
end

This is perhaps not the best method but it has saved alot of time already.

Thanks Again!

Ed