Why are the vertices of the faces not moved with transformation? Is there any way around this?

For some reason, the vertices are floating. Do you know any way to fix this? Also, I’m essentially trying to deposit m1 on top of the existing top faces uniformly and I am open to any suggestions on how to do that if this way seems to not be the best.

SKETCHUP_CONSOLE.clear
module Practice
  model = Sketchup.active_model # Open model
  manager = model.options
  provider = manager["UnitsOptions"]
  provider["LengthUnit"] = 2
  
  sub_t = 2.mm
  via_t = 5.mm
  via_into = 1.mm
  insulator_t = 2.mm
  m1_t = 2.mm
  current_z = 0.mm
  prev_z = 0.mm
  
  ents = model.active_entities
  layers_array = model.layers
  
  # add layers to the array.
  substrate_tag = layers_array.add "substrate"
  insulator_tag = layers_array.add "insulator"
  via_tag = layers_array.add "via"
  m1_tag = layers_array.add "m1"
  
  # create substrate
  substrate = ents.add_group # add substrate group
  sub_face = substrate.entities.add_face [0,0,0], [5.mm,0,0], [5.mm,8.mm,0], [0,8.mm,0] # add face
  if sub_face.normal.z == 1 # want to extrude in negative direction
    sub_face.reverse!
  end
  sub_face.pushpull sub_t # extrude 10mm below
  
  
  # now do via and insulator layer
  via = ents.add_group
  via.name = "via"
  via.entities.add_face([1.mm,1.mm,0], [4.mm,1.mm,0], [4.mm,3.mm,0], [1.mm,3.mm,0])
  via.entities.add_face([1.mm,5.mm,0], [4.mm,5.mm,0], [4.mm,7.mm,0], [1.mm,7.mm,0])
  via_faces = via.entities.grep(Sketchup::Face) # very fast!
  points = via_faces.flat_map(&:vertices).uniq.map(&:position)
  # go up by 25 mm 
  vector = Geom::Vector3d.new(0, 0, via_t)
  top_face_pts=[]
  via_faces.each{|f| top_face_pts.push(f.vertices.map(&:position).map{|p| p.offset(vector)})}
  
  # adds the top face
  top_face_pts.each{|pts| via.entities.add_face pts}
  # create a new edge for each point
  points.each{|pt| via.entities.add_edges(pt,pt.offset(vector))}
  current_z = via_t - via_into
  # add faces
  via_edges = via.entities.grep(Sketchup::Edge)
  via_edges.each(&:find_faces)
  #new_via_edges.each(&:find_faces)
  
  
  new_transform = Geom::Transformation.new([0,0,-via_into])
  via.transformation = new_transform
  substrate=via.trim(substrate)
  
  # now do insulator
  insulator = ents.add_group
  ins_face = insulator.entities.add_face [0,0,0], [5.mm,0,0], [5.mm,8.mm,0], [0,8.mm,0]
  if ins_face.normal.z == -1 # want to extrude in negative direction
    ins_face.reverse!
  end
  ins_face.pushpull insulator_t
  # changed!
  insulator = via.trim(insulator)
  
  top_ins_pts = insulator.entities.grep(Sketchup::Face).first.vertices.map(&:position)
  
  m1 = ents.add_group
  groups = ents.grep(Sketchup::Group)
  faces = groups.flat_map{|g| g.entities.grep(Sketchup::Face)}
  positions = groups.flat_map{|g| g.entities.grep(Sketchup::Face)}.flat_map(&:vertices).map(&:position)
  top_points = positions.find_all{|p| p.z > prev_z && p.z < current_z+m1_t}
  top_points = top_points.map(&:to_a).uniq
  vector = Geom::Vector3d.new(0, 0, m1_t)
  top_points.each{|pt| m1.entities.add_edges(pt,pt.offset(vector))}
  m1_edges = m1.entities.grep(Sketchup::Edge)
  m1_edges.each(&:find_faces)
  
  #name groups
  substrate.name = "substrate" # name substrate group
  via.name = "via"
  insulator.name = "insulator"
  m1.name = "m1"
  
  via.layer = via_tag
  insulator.layer = insulator_tag
  substrate.layer=substrate_tag # add layer tag.
  m1.layer = m1_tag
  
  model.rendering_options['DisplayColorByLayer']= true # colors each layer
end

When working inside a group, your transformations (and coordinates) are relative to the group’s local origin.

To move group m1 as a whole, apply a transformation to the group using m1.transform!(transform).

m1 builds off of the top faces. I tried transform! the way below but the points are still found above where they are supposed to be.

new_transform = Geom::Transformation.new([0,0,-via_into])
via.transform! new_transform
substrate=via.trim(substrate)

I’m not sure what the result of your code is supposed to be. After it runs, the m1 group contains these 16 edges (I toggled view rest of model back and forth because some of them lie atop edges from other objects):

edges

It’s not obvious to me where you expected those edges to be relative to the previous contents, so I’m not sure what may be wrong with your attempt to transform vertices.

Your call to find_faces finds none because these edges don’t frame any faces, they are spatially disjoint.

I would suggest that you learn the primitive but effective technique of debugging by adding puts or p statements in your code to show the values of entities on the SketchUp Ruby Console. That may reveal to you where your logic is going wrong.

Also it would help if you cut up the code into methods with names that suggest what each task does, and add comments to the code for what the next lines or few lines of code are expected to do.

Add a menu item to fire off the code rather than loading the module with a bunch of sequential code statements. (Ie, SketchUp code is event driven, so get in the habit of writing event driven code.)

For example you are doing things like setting model units and rendering options every time the module is loaded. Modules usually are loaded once when SketchUp first starts, and then code is executed in methods by calling the methods. For ease in method calling, insert a extend self line near the top of the module.

I’m trying to make this.



Where the red is an object m1.
In the future I will have faces that will have curves though and I just want to uniformly add a layer of a certain thickness to it.

In this gif I think I can see at least part of your problem: the origin of the via group is below the model’s 0 z plane, and that affects the positions you get because as @DanRathbun pointed out earlier, you are getting coordinates within the group’s coordinates.

origin

This is the ultimate goal in the end.

To start from this


followed up like this

to generate this

This is actually weird because the insulator layer should be there as well giving you the edges. I have seen this happen randomly but I wasn’t really sure what to make of it.

See also my discussion in your other topic. Entities in different groups are isolated from each other in the independent Entities collections of their ComponentDefinitions. Those edges that you expect to complete the Faces exist only in the insulator Group, they do not exist in either the model’s Entities collection or in the new avi group’s Entities, so they can’t complete Faces with the new Edges you created.

I think if I were trying to code this I’d go about in a different way. Since your code is drawing things and then gathering them into groups, I’d keep track of where I put them in the model before creating the group and reuse those coordinates to draw additional geometry to form additional groups instead of trying to retrieve them back by a somewhat complicated analysis of what you actually just drew.

I suspect you are speaking in the sense of the PCB industry. In SketchUp coding a “Layer” refers to a property tag that has display behaviors which can be shared among multiple objects.
In SketchUp these are not geometric entity collections, have no sense of 3 dimensional placement and transcend the hierarchy of the model’s geometric entity tree. (Any object, at any level of the tree, no matter it’s nesting or what object owns it, can share whatever display tag the user set it to use.)
This is why they have been renamed “Tag” in the GUI. (Many newbs expected geometry could be assigned to a layer and be separated from other geometry.) The API class for Tags is still named “Layer”.

“Objects” in the SketchUp sense can be group instances, component instances or section planes (and perhaps also other complex entities like dimensions, text callouts and guides.)
But “objects” in an object-oriented programming sense are Ruby code objects which references (aka identifiers) point at. Almost everything in Ruby is “an object”, methods, classes, modules, numbers, text strings, even true, false and nil. (“Almost” means with the exception of interpreter functions and operators.)

“Definitions”, as you have found out, has a specific meaning with regard to SketchUp components and the API, but also has meaning in the programming sense of defining methods, procs, modules and classes in code files. So when talking about coding always preceed “definition” with what kind of definition (method, proc, etc.)

Thanks for the tips. Sorry I don’t really have the best grasp of ruby. I’m not a software engineer and I just started programming 3 weeks ago. I don’t fully understand the way things are supposed to be structured yet. How does this look?

SKETCHUP_CONSOLE.clear
module Practice
  module Create_structure
    extend self
    # this makes the chosen face orient up
    def face_up(face)
      if face.normal.z == -1
        face.reverse!
      end
    end
    
    # this makes the chosen face orient down
    def face_down(face)
      if face.normal.z == 1
        face.reverse!
      end
    end
    
    # this gets the highest point in a group. (not used yet)
    def get_highest_point_in_group(group)
      max_pt = entities.flat_map(&:vertices).map(&:position).map(&:z).max
      return max_pt
    end
    
    # get model set up
    model = Sketchup.active_model # Open model
    manager = model.options 
    provider = manager["UnitsOptions"] 
    provider["LengthUnit"] = 2 # set default values to mm
    ents = model.active_entities
    layers_array = model.layers # create an array to store layer information
    
    # set up initial variables
    sub_t = 2.mm # thickness of substrate
    via_t = 5.mm # thickness of via (i.e total height)
    via_into = 1.mm # penetration of via into thing below it.
    insulator_t = 2.mm # thickness of insulator layer
    m1_t = 2.mm # thickness of m1 layer
    
    # This will be used to track the positions for adding edges off the previous layer
    # think of it like a hill and a valley that we are building on. 
    highest_pt = 0.mm # current highest (like a hill)
    highest_low_pt = 0.mm # this will be the lowest occupied point (like a valley)
    
    # add layer tags to layers_array
    substrate_tag = layers_array.add "substrate" 
    insulator_tag = layers_array.add "insulator" 
    via_tag = layers_array.add "via"
    m1_tag = layers_array.add "m1"
    
    # create substrate
    substrate = ents.add_group # add substrate group
    sub_face = substrate.entities.add_face [0,0,0], [5.mm,0,0], [5.mm,8.mm,0], [0,8.mm,0] # add face
    if sub_face.normal.z == 1 # want to extrude in negative direction
      sub_face.reverse!
    end
    sub_face.pushpull sub_t # extrude 10mm below (no need to update lowest point at this part)
    
    # now deposit the via layer
    via = ents.add_group # add group
    via.name = "via" # name group (debugging)
    via.entities.add_face([1.mm,1.mm,0], [4.mm,1.mm,0], [4.mm,3.mm,0], [1.mm,3.mm,0]) # adds face to via group
    via.entities.add_face([1.mm,5.mm,0], [4.mm,5.mm,0], [4.mm,7.mm,0], [1.mm,7.mm,0]) # adds face to via group
    via_faces = via.entities.grep(Sketchup::Face) # find the via faces 
    points = via_faces.flat_map(&:vertices).uniq.map(&:position) # get all the positions of the 
    # go up by 25 mm 
    vector = Geom::Vector3d.new(0, 0, via_t) # the vector will be used to generate the thickness of this layer.
    top_face_pts=[] # empty array to push the via top face points into
    via_faces.each{|f| top_face_pts.push(f.vertices.map(&:position).map{|p| p.offset(vector)})} # creates points that will be used to make the top face.
    # There is probably a better way to do this with the push_pull function but I want to make a pyramidal shape later
    # adds the top face (later version this will possibly be an offset)
    top_face_pts.each{|pts| via.entities.add_face pts} # adds the top faces 
    # create a new edge for each point
    points.each{|pt| via.entities.add_edges(pt,pt.offset(vector))} # add egdges from the bottom face points to the top face points.
    highest_pt += via_t # update the highest point. (since we started at 0 we add the height of the extrusion
    via_edges = via.entities.grep(Sketchup::Edge) # find all the via groups edges.
    via_edges.each(&:find_faces) # add faces using the edges found above.
    move_via = Geom::Transformation.new([0,0,-via_into]) # transformation to move the via so that it penetrates the substrate.
    via.transform! move_via # move the via into the substrate
    highest_pt -= via_into # update highest point
    substrate=via.trim(substrate) # trim is non destructive method to remove the part of the substrate that the via penetrates into.
    
    # Add the insulator layer
    insulator = ents.add_group # adds insulator to group
    ins_face = insulator.entities.add_face [0,0,highest_low_pt], [5.mm,0,highest_low_pt], [5.mm,8.mm,highest_low_pt], [0,8.mm,highest_low_pt] # adds face to insulator
    face_up(ins_face) # makes the face upright
    ins_face.pushpull insulator_t # pulls the face up to the correct thickness starting at the insulator layer.
    # update information
    calc_total_height = insulator_t + highest_low_pt
    if calc_total_height < highest_pt # if this is the new highest low point
      highest_low_pt = calc_total_height # update highest low point
    else # if this is now the highest point.
      highest_low_pt = highest_pt # update highest low point to old highest point
      highest_pt = calc_total_height # overwrite highest point to be the new total height.
    end
    insulator = via.trim(insulator) # trim the insulator using the via.
    
    # deposit m1 uniformly
    m1 = ents.add_group
    groups = ents.grep(Sketchup::Group)
    #faces = groups.flat_map{|g| g.entities.grep(Sketchup::Face)}
    positions = groups.flat_map{|g| g.entities.grep(Sketchup::Face)}.flat_map(&:vertices).map(&:position)
    top_points = positions.find_all{|p| p.z >= highest_low_pt && p.z <= highest_pt}
    top_points = top_points.map(&:to_a).uniq
    vector = Geom::Vector3d.new(0, 0, m1_t)
    top_points.each{|pt| m1.entities.add_edges(pt,pt.offset(vector))}
    m1_edges = m1.entities.grep(Sketchup::Edge)
    m1_edges.each(&:find_faces)
    
    #name groups
    substrate.name = "substrate" # name substrate group
    via.name = "via"
    insulator.name = "insulator"
    m1.name = "m1"
    
    via.layer = via_tag
    insulator.layer = insulator_tag
    substrate.layer=substrate_tag # add layer tag.
    m1.layer = m1_tag
    
    model.rendering_options['DisplayColorByLayer']= true # colors each layer
  end
end

You are correct. I am trying to model a PCB design using sketchup.

It is starting out good, ie the first 3 methods, but you need to continue with the rest of the code.

Basically most all of the code needs to be in methods, except for declarations of module/class variables (ie, @@vars) and local constants, which should be at the top of the first file loaded (of an extension set of files as the others would likely use these vars or constants.)

Also, there are 2 kinds of conditional expressions. The kind preceding a block …

    def face_up(face)
      if face.normal.z == -1
        face.reverse!
      end
    end

… and those “in modifier position” …

    def face_up(face)
      face.reverse! if face.normal.z == -1
    end

… or …

    def face_up(face)
      face.reverse! unless face.normal.z == 1
    end

You will eventually need to create some unique toplevel namespace module name, derived from your name or that of your company.

The submodule name is usually the extension name with spaces removed.

The extension subfolder name is the toplevel module name + “_” + the submodule name.
The extension registrar Ruby file has this same name but with a ".rb" file extension.

See: class SketchupExtension

Yep, I spent many years in the broadcast electronics industry and designed a few simple PCBs in the old days, even preCAD on a lightbox with decals and tape.

One thing to be wary of for your project: SketchUp has a built-in tolerance of 0.001 inch below which it concludes that vertices were meant to be the same and merges them. This can cause loss of very short edges and their associated faces. The geometry you are drawing is probably safe, as it involves simple prismatic shapes larger than this tolerance. But you should be aware of this potential if you start to draw things involving curves or circles or trying to model smaller features. The workaround in code is the same as in the GUI: draw at a larger scale, create a component or group, and then scale an instance down using a transformation. This kind of scaling does not trigger the tolerance test.

1 Like

I was also going to say the same.

As well as … since PCB design software and even manual layout uses component decals, you should likely be doing the same thing in your extension. Ie, your extension would have a library of via components that you’ll instantiate into the model, into a “layer” group’s entities collection, at a specific transformation. (“Layer” here is in the PCB-sense, as in “solder side layer” etc.)

This is how I would do it. I would not waste time trying to draw pads, pad arrays and vias using code.
Instead I’d manually draw them using SketchUp at a higher scale using the “Dave Method” and save them out into a local component definition library.

The extension would use the DefinitionList#load and Entities#add_instance methods to place PCB components into the model.

@slbaumgartner @DanRathbun I would use the dave method but I am going to try to build the model from a gds file (2D top down view).

How are you planning to import the gds file into SketchUp? Are you just importing an image to trace, or actually importing the gds data itself? This will affect what work you have to do and what import issues you may have to clean up.

I already have the import figured out and working. At this point, I just need to figure out how to use the polygons (which are x,y data) as a mask.