Best way to rotate/stretch a face with API?


#1

I’m a complete novice with Ruby and the Sketchup API, and I’m looking for a sensible way to extend the end of an I-beam, but chopped at an angle. e.g., from red to green:

I could use Transformation.rotation() on the end face, but that wouldn’t extend the top edge of the green beam out along the same line as the red.

I have a horrible solution (that works) using a Transformation.translation() along the line… of 5 separate edges :grimacing: and calculating 6 different distances. There must be a better way?!

ridgey = height*Math.tan(angle)*Math.cos(angle)
ridgez = height*Math.tan(angle)*Math.sin(angle)
tr = Geom::Transformation.translation(Geom::Vector3d.new(0,ridgey.mm,ridgez.mm))
entities.transform_entities(tr, entities[20])
ridgey = (height-45)*Math.tan(angle)*Math.cos(angle)
ridgez = (height-45)*Math.tan(angle)*Math.sin(angle)
tr = Geom::Transformation.translation(Geom::Vector3d.new(0,ridgey.mm,ridgez.mm))
entities.transform_entities(tr, entities[17])
entities.transform_entities(tr, entities[21])
ridgey = 45*Math.tan(angle)*Math.cos(angle)
ridgez = 45*Math.tan(angle)*Math.sin(angle)
tr = Geom::Transformation.translation(Geom::Vector3d.new(0,ridgey.mm,ridgez.mm))
entities.transform_entities(tr, entities[15])
entities.transform_entities(tr, entities[23])

(I’m using SketchUp 2013, if that makes a difference)

Many thanks


#2

I’d make a plane for the new end face (see overview of Geom module.)

Then use each edge object’s #line() method to get an array of lines to intersect with the plane.

See Geom::intersect_line_plane module method.

Something like (untested):

# assume var "plane" was set using a point
#  on the plane and it's normal vector,
# and var "edges" is an array of the horizontal edge objects

lines = edges.map {|e| e.line }
new_points = lines.map {|line| Geom.intersect_line_plane(line, plane) }

vecs = []
verts = []

new_points.each_with_index {|pt,i|
  verts << edges[i].end
  vecs << edges[i].end.position.vector_to(pt)
}

ents = edges[0].parent
ents.transform_by_vectors( verts, vecs )

Ref related topic in this category:


#3

Oh, and BTW, your can get a valid plane for the starting face via:
Sketchup::Face#plane()

… and then create a rotational transform (about the lower edge,) and apply it to the two members (point and vector) of the plane array, to get the new face’s plane.


#4

Many thanks, but I’ve been struggling to understand that explanation! Sorry, still very much a novice, and I haven’t used planes yet.

I probably should have included a few more lines of code, to show that my code created that object in the first place with just a face + pushpull, so hopefully it should be easy to grab that new end ‘face’ and manipulate it?

This is the code I’m now using:

# create 3D object from array of Geom::Point3d, origin (x,y,z)
face1 = entities.add_face(points)
face1.pushpull(-length_pushpull.mm)

# create face at far end of 3D object
points2 = points.map { |a| Geom::Point3d.new(a.x, a.y+length_pushpull.mm, a.z) }
face2 = entities.add_face(points2)

# scale new face on z-axis only, then rotate
zscale = 1/Math.cos(angle)
tr1 = Geom::Transformation.scaling(Geom::Point3d.new(x.mm, y+length_pushpull.mm, z.mm), 1,1,zscale)
tr2 = Geom::Transformation.rotation(Geom::Point3d.new(x.mm, y+length_pushpull.mm, z.mm), X_AXIS, -angle)
entities.transform_entities(tr2 * tr1, face2)

which I’m MUCH happier with :smiley:

Is there a better way to get a face at the far end of the pushpull, rather than manually copy of the array? I thought I could use something like face2 = face1.pushpull(distance, true) but that doesn’t return the face.


#5

I would only do a pushpull inside a group with the face to be pp’d. (Later explode the group if necessary.)

You know the vector direction of the pp operation, so the end face will have a normal vector pointing the same direction.

endface = group.entities.grep(Sketchup::Face).find {|face| face.normal == direction }

#6

Many thanks, that works beautifully :slight_smile:


#7

Just be aware Enumerable.find will return nil if no match, so always check result for “nilness” and bail if it is.

return false if endface.nil?

It prevents NoMethodError like:
Error #<NoMethodError: undefined method 'transform!' for NilClass>, … etc.


#8

Ta. I can see that is good practice in a general case, but surely if it can’t find a face I created on only the previous line, then something far more serious has gone wrong?!