While in a for loop: read data; create faces; group them; repeat


#1

Dear friends,
Once again I need the Community’s support. I am reading a CSV file, creating three faces for each row of the CSV file. I want these three faces to be a single group. That is, I want one group for each line of the CSV file.
My code as is below, creates the faces but all faces for all rows of the CSV file are in the same group.
Apparently, I have not understood the involved hierarchies.

mod = Sketchup.active_model # Open model
ents = mod.entities # All entities in model
sel = mod.selection # Current selection

require 'sketchup.rb'
require 'csv'

#Load CSV panel and read
chosen_file=UI.openpanel("Open csv file", nil, "csv|*.csv||");0
lines=IO.readlines(chosen_file);0
 
lines.each{|line|
  
line.chomp!
next if line.empty?
xyz=line.split(",");0 # splits each row by comma

x_helio=xyz[0].to_f;0 #converts x to meters….the same for y_helio, z_helio, x1, y1,z1 etc

cyl_curve=ents.add_circle [x_helio.m, y_helio.m, z_helio.m],[0,0, -5], 3
cyl_face=ents.add_face cyl_curve
cyl_face.pushpull -1.9.m
cyl_face.material="Color1"

face1_curv=ents.add_face [[x1.m,y1.m,z1.m], [x2.m,y2.m,z2.m], [x3.m,y3.m,z3.m], [x4.m,y4.m,z4.m]]
face1_curv.pushpull 10
face_curv.material="Color2"

face2_curv=ents.add_face [[x1.m,y1.m,z1.m], [x2.m,y2.m,z2.m], [x3.m,y3.m,z3.m], [x4.m,y4.m,z4.m]]
face2_curv.pushpull -5
face2_curv.material="Color3"

face_group = ents.add_group ents.to_a
face_group.name = "I_am_Groot"
}

Thank you in advance.
Chris


#2

@Cris1, Create the empty group first and then add entities to the group.

lines.each{|line| 
newgroup =ents.add_group
newgroup.name = "I_am_Rocket" # saw GOG-I this weekend 
#create face f1 
newgroup.entities.add_face f1}

See http://ruby.sketchup.com/Sketchup/Group.html#entities-instance_method for another example.

Yogesh


#3

You been doing too much C++? // is an empty regex in Ruby, not a comment delimiter!


#4

:slight_smile: made the corrections…

PS- I am learning ruby and SketchUp api’s so let me know if the suggestion is incorrect


#5

@Cris1

(1) Ruby does not need “;” at the end of statements, nor the literal 0 on the end of every line.

(2) after push pulling, your face reference may be invalid:

cyl_face = newgroup.entities.add_face(cyl_curve)
cyl_face.pushpull(-1.9.m)
cyl_face.material= "Color1"

… you might have to paint the face first in many situations:

cyl_face = newgroup.entities.add_face(cyl_curve)
cyl_face.material= "Color1"
cyl_face.pushpull(-1.9.m)

#6

Thank you very much for your input. It totally worked.
I have one question if you dont mind. These groups I just created with your help with the above ruby code, I would like to place them in a harsh terrain by importing a geolocation and select choose terrain. Due to slopes of the landscape, I am using ThomsThoms Raytracer to get my group of faces in the landscape.
Here is the thing: the group generated with the above code, is not landing properly in the landscape with the RayTracer tool. If however, I explode it manually and regroup it in Sketchup, then it lands properly.
Are there different types of groups? Is the group we created above in any way different than groups made manually in Sketchup?


#7

It seems likely that the axes and origin of the group you create in your code is not the same as what you get if you create it in the GUI or if you explode and regenerate it in the GUI. The GUI automatically aligns the group axes with the model’s global axes and places the group’s origin at the nearest corner of the group’s bounding box to the global origin. If you turn on ModelInfo->Components->Show Component Axes you can check this idea.


#8

Dear slbaumgartner, you are 100% right. When I create the group with the above ruby code, SU builds it with reference to the 0,0,0. If I explode the groups and remake them components manually one by one in SU, then, using your nomenclature, the new axes is indeed the nearest corner of the group’s bounding box to the global origin. This setting works beautiful with ThomThoms Raytracer,
So, my issue now is how to create the components with the above ruby code and then change the axes/origin to be the one nearest to the bounding box as if it was created in SU.


#9

It may have to do with my drawing method, perhaps exploding and rebuilding in Ruby perhaps? I could try it and report back but I can’t get it rebuild in a component. I can explode it though.


#10

The key concepts to grasp here are that the positions within a Group’s Entities collection are described with respect to the Group’s origin and the Group as a whole is positioned in the model using the Group’s Transformation.

You are reading in positions in model coordinates from your file and using them to add Entities to the Group’s collection. As a result, they are placed with respect to the Group’s origin - which by default is equated to the model’s origin. So, even if [1,1,0] is the lower left of the Group, [0,0,0] will still be the Group’s Entities origin.

To fix this, choose a point that you want to be the Group’s origin - perhaps the one closest to the model origin. Subtract that point’s coordinates from all of the values before adding them to the Group’s Entities. For example:

origin_point = [x1.m, x2.m, x3.m]
x_helio = xhelio - origin_point

etc.

Also transform the entire Group to the chosen point:

tr = Geom::Transformation.translation origin_point
newgroup.transform! tr

Note: typing this off the top of my head. You may need to explicitly create Point3d or Vector3d objects to get the right object types.


#11

Thank you slbaumgartner. I will play around with your suggestion and report back.


#12

Dear slbaumgartner, it worked like a charm. Simple translation; no need for additional Point3d or Vector3d. Just adopted the origin_point and subtracted all the other pairs as you suggested, followed by the group transform commands you provided.
Thank you very much.