Transform a model vertex by vertex

ruby

#1

Hello,
I’m trying to transform the whole model using my ruby script by iterating over all it’s vectors and setting their respective positions. However the only available method is ents.transform_by_vectors, which isn’t particulary well documented. It works OK (apart from breaking all the faces into triangles) as long as there are no curves. But any time I apply a vector to a vertex of a curve, the whole curve gets deformed.

Before translation by [0,0,10]:

After translation:

The square and the straight line were translated correctly, the curves are distorted. When I convert them to poly and explode them, everything works as expected.

This is my code:

entities = Sketchup.active_model.active_entities
vertices = []

entities.each{ |e|
  if e.is_a? Sketchup::Edge
    vertices.push *e.vertices
  end
}

vertices.uniq! #ensure every vertex is transformed only once!

vertices.each{ |v|
  entities.transform_by_vectors(
    [v], [Geom::Vector3d.new(0,0,10)]
  )
}

Obviously there are much simpler ways to move the whole model up by 10", but I’m going to need more complex transformations, which require vertex-by-vertex aproach.


Best way to rotate/stretch a face with API?
#2

Unless each vertex needs to move by a different amount, I think you are going about this the wrong way! Vertices are not really geometry, they are Entities referenced by Edges and Faces to define their points in space. Multiple Edges and Faces can refer to the same Vertex.

When you move a single Vertex, the Faces that reference it almost certainly become non-planar. SketchUp fixes them by folding (creating one or more diagonals). Then when you move the next vertex, it has to repeat this process. In the case of your curves, the result is pretty messy and not what you wanted!

To preserve sanity, you need to transform entire Faces not individual Vertices. Transforming a Face implies moving all of its Vertices and implicitly all of the Edges in its loops. Also, note that #transform_by_vectors takes two Arrays, so you can stage up a whole collection of Entities to transform in one call.


#3

If you just want to move all active entities up by 10", then it’s relatively simple - use a translation…

x = 0.0
y = 0.0
z = 10.0
tr = Geom::Transformation.translation( Geom::Vector3d.new(x, y, z) )
entities = Sketchup.active_model.active_entities
entities.transform_entities( tr, entities.to_a )

???


#4

For moving all vertices by the same vector, use Entities#transform_entities

transform_by_vectors can be used to transform entities by different vectors all at the same time - so there is no need to iterate the entities collection.

entities.transform_by_vectors(entities.to_a, vectors)

#5

But this is a good point:

EDIT: Cool, transforming all the vertices at once doesn’t break the faces! But curves are still wrong :’(


#6

Could you elaborate on this? Unless the transformations are non-rigid (i.e. intentionally distort the shape in ways other than move, rotate, scale, and skew) there is no reason not to use a Transformation to move entire Entities as TIG and Jim showed.


#7

Those transformations are non-rigid, trust me :wink:
I want to convert Cartesian coordinates to cylindrical coordinates, so that I could “unwrap” a cylindrical surface, edit it (stretch, push/pull, …) and “wrap” it back to a cylinder.

That means transforming every vertex so that:

x,y,z = [sqrt(x**2,y**2), atan2(y,x), z]

Which is, of many transformations I know, a very non-rigid one :smiley:


#8

Now you say !
So every vertex needs to have a corresponding vector translation defined

entities.transform_by_vectors(
    [all_of_the_vertices], [all_of_their_corresponding_vectors]
 )

#9

Actually it was in the original question.

Thanks, you’re the third one to say :wink:
Yeah, it’s quite hard to follow the pace of this discussion, that’s true.

.

I think the problem could be that the curves are either ArcCurve or Curve.is_polygon?==true, which maybe forces them to be regular. Which means that if there’s no way to “iregularize” them, I’ve probably got to explode them and “reweld” into an irregular curve. Aww, boring :frowning:


#10

You didn’t actually explain that [clearly] - as in your example every transformation had the same vector !
And of course if every vector has a separate vector-transformation you need to pass two full arrays - in one go !

One way [untested] might be to find the arc_curve vertices from the various edges and then transform those en mass and then catch up with any remaining vertices ?

Try it with a simple collection of edges/arcs etc.

But remember that if you skew the arc in anyway, then it’s no longer an arc - rather it’s then a curve - do it manually by scaling just slightly an arc to see it’s no longer an arc…


#11

I’ve solved it by simply exploding all the curves before the transformation by applying the edge.eplode_curve function. In theory, they could be rewelded after the transformation if I stored all the curve’s vertices inside an array.

The whole code now looks like this:

entities = Sketchup.active_model.active_entities
vertices = []
vectors = []

# iterate through edges and find their vertices
entities.each{ |e|
  if e.is_a? Sketchup::Edge
    if e.curve
      # regular curves (arcs, poly) cannot be transformed
      # vertex by vertex, they have to be exploded
      vertices.push *e.explode_curve.vertices
    else
      vertices.push *e.vertices
    end
  end
}

vertices.uniq! # ensure every vertex is transformed only once!

vertices.each{ |v|
  # here you can compute new coords of the vertex,
  # compute the translation vector and push it to the array
  
  pos = vector.position.clone
  pos.z += random(10)/10
  vectors.push v.position.vector_to(pos)
}

entities.transform_by_vectors( vertices, vectors )

.

I think something like this code should be in the documentation of transform_by_vectors, so that people don’t have to learn it by trial and error… @TIG? @DanRathbun? :slight_smile:


Ruby API Documentation - Errors & Suggestions
#12

Yes I see the doc for this method has a comment in it for a better example.