How to split faces from Ruby code

Any suggestions of how to split faces at a certain z value to a lower and upper part, i.e. draw a line at given z value along all faces?

I.e. split the faces in the attached file e.g. at 1 meter.

Untitled.skp (139.5 KB)

mod = Sketchup.active_model
ent = mod.active_entities
sel = mod.selection
SKETCHUP_CONSOLE.clear
faces = sel.grep(Sketchup::Face).reject{|f|f.normal.z != 0}
for f in faces
btm = f.edges.min_by{|x|x.start.position.z+x.end.position.z}
sp = btm.start.position.offset(Z_AXIS,1.m)
ep = btm.end.position.offset(Z_AXIS,1.m)
new = ent.add_line(sp,ep)
end

Thanks for the quick reply! Would you be so kind to take a look at the actual model, attached to this post. There are two issues:

1 Not all faces start from the level z=0.00, hence the code also adds a new line z=1.00m to the upper part of the cylinder. Any suggestions of how to tell SU to look at absolute coordinates?

2 The code has not effect on the two surfaces, any idea why?

What I would like to accomplish is to slice the surface in 0.25cm faces and add color to each based on the z position, e.g. the face z=[31.00;31.25] gets colour x1. I have sorted out the colouring issue but each face needs to be split at z=31.00 and z=31.25 in order for the code to be able to add colour in the specified range.

Untitled2.skp (1.3 MB)

  1. The code simply adds 1m to the low side of each vertical face.

  2. The code has no effect on the surfaces because none of its’ faces are vertical

  3. It will require 1700+ slices of the surface at a 0.25cm interval. Are you sure about that?

Is it possible to adjust the code so that it adds 1m from the level of z=0.00, instead from the minimum z value of each face?

Is it difficult to include all surfaces, or say surfaces that have an angle of [0;89.99] degrees?

The landscape already has some thousands of surfaces, would an additional 1700+ make any difference? To answer your question, yes. If performance becomes a problem, then the algorithm that makes the landscape most be tweaked to make less triangles. I’m using the Toposhaper plugin for that, it has an parameter for that.

I’d be inclined to do this in a different way.
If the aim is to ‘slice’ the geometry into horizontal pieces, then [in code] why not add a temporary group containing a large flat face, that’s larger that the geometry’s bounds, then transform it up by the step [0.25cm], then intersect the geometry with that to form the cut-lines, then increment the step and transform again, repeating until the group is at the geometry’s top.
Now collect all of the faces and apply the materials according to their .bounds.min.z / .bounds.max.z etc…

1 Like

That sounds as a much more robust solution.

@TIG, is there some straightforward way to accomplish that? Attached is the landscape and below is the code that I’m using to add colour to the landscape. In this example colour is added to faces which have their lowest point above 32.00m. The problem is that there are faces which are both below and above z=32.00m. These faces do not get coloured. Hence the need for splitting them at z=32.00m.

Untitled3.skp (2.6 MB)

  model = Sketchup.active_model
  clselection = model.selection
  faces = []
  if clselection.empty?
    UI.messagebox "Nothing selected. All faces that are not grouped will be coloured."
    entities = model.active_entities
  else
    entities = model.selection
  end
  model.start_operation("add colour", true)
  entities.each do |a|
    if a.typename == ( "Face" )
      z_min  = a.bounds.min[2].to_m
      puts z_min 
      if z_min >= 32
        faces.push a
      end
    end
  end
  faces.each do |a|
     col = [255,0,0]
     a.material = col 
     a.back_material = col
   end
  model.commit_operation

Intersect with model does the trick, however, how to do it in Ruby is beyond my skills?

This colors all faces above 32m

model = Sketchup.active_model
height = 32.m
colour = "Red"
model.start_operation("Add Colour by Height", true)
model.selection.grep(Sketchup::Face).each{|f|
  if f.bounds.min.z >= height
    f.material = colour
    f.back_material = colour
  end
}
model.commit_operation

To get those which straddle 32m try this…

model = Sketchup.active_model
height = 32.m
colour = "Red"
model.start_operation("Add Colour by Height", true)
model.selection.grep(Sketchup::Face).each{|f|
  if f.bounds.max.z > height
    f.material = colour
    f.back_material = colour
  end
}
model.commit_operation

There are many ways to set a min / max or range of heights…

@TIG, thanks! This is a straightforward way but does not solve the problem, because what colour to choose for a face that extends both below and above z=32.00? The only way, as I see it, is to split it in two, e.g. using the intersect plane as you suggested.

For clarity…
Do you want to split every face that straddles z=32m, and apply different colors to those above and those below ?
If so you need to split the faces at a plane then apply 2 colors above and below the cut…
Try this…

model = Sketchup.active_model
ents = model.active_entities
ss = model.selection 
height = 32.m
colorup = "Red"
colordn = "Green"
model.start_operation("Add Colour by Height", true)
#split selected faces at z=height
fs = ss.grep(Sketchup::Face)
f0=fs[0]
ss.clear
bb = model.bounds
min = bb.min; min.z=height
max = bb.max; max.z=min.z; max.z=height
man = min.clone; man.x=max.x
mix = min.clone; mix.y=max.y
gp = ents.add_group()
gp.entities.add_face(min, man, max, mix)
tr = gp.transformation
gp.entities.intersect_with(true, tr, ents, tr, false, fs)
gp.erase!
faces = f0.all_connected.grep(Sketchup::Face)
#color faces up & dn from plane
faces.each{|f|
  if f.bounds.min.z >= height
    f.material = colorup
    f.back_material = colorup
  else
    f.material = colordn
    f.back_material = colordn
  end
}
model.commit_operation
ss.add(faces)

preselect the face to process
the surface’s faces are split horizontally at the height…
it only colors the connected faces in the selected surface - so multi-part [discrete] surfaces need to be processed in separate bits.
height sets the cut plane
colorup is >= height [red]
colordn is < height [green]
it’s one step undo-able
the faces are re-selected on completion.