# 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.

# create substrate
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.name = "via"
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)})}

# create a new edge for each point
current_z = via_t - via_into
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
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)

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)
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
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):

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.

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

# create substrate
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.name = "via" # name group (debugging)
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)
# 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.

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
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)
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
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.