Find the lower left corner of the quad

8YPF(OU%7B)VYFF2)~4F72H0Z
I want to lay out the components from the bottom left corner of a face.
I wrote a code to get the nearest point of the quadrilateral from the origin and sort the four endpoints.
But many of the actual points closest to the origin are not the lower left corner of the quadrilateral,just like the picture.
How can I do to find the points in the lower left corner of the quadrilateral and sort them?
I would be grateful if you could help me with the answer.

have a look at the :corner method…

entity.bounds.corner(0) may be what you need, unless there is rotation or skew…

john

If you want to order corners (vertices) by say their height you can do something like this:

vertices.sort_by { |v| v.position.z }

If you want to sort them by their “bottom-leftness” you can temporarily transform the coordinates before sorting:

transformation = Geom::Transformation.new(Geom::Vector3d.new(1, 0, 1))
vertices.sort_by { |v| v.position.transform(transformation).z }
1 Like

vertices.sort_by { |v| v.position.z }

Fails because it is passing three element arrays [aka point3ds], and that will not work with .position - because that only applies to a vertex

So using…

vertices = [[3,2,1], [2,3,4], [4,2,1], [5,6,2]]
vertices.sort_by { |v| v.z }

WILL work properly…

It doesn’t fail if vertices are vertices, only if you for whatever reason put something that isn’t vertices in the variable named vertices.

@skyfire, I think your post is related to your preivous posts and so I think you’re looking for something like this :

#Collect the selected face positions
face_entity = Sketchup.active_model.selection[0]
face_positions = face_entity.vertices.collect{|v| v.position}

#Order the face positions according to their distance to the lower left corner face's space and select the closest one
ref_position = face_entity.bounds.min
closest_position_from_bottom_left = face_positions.sort_by{|p| p.distance(ref_position)}[0]

#Find were the closest position from the bottom left corner is in the list and rebuild an ordered list
closest_position_index = face_positions.index(closest_position_from_bottom_left)
ordered_face_positions = face_positions[closest_position_index ..-1] \
                       + face_positions[0...closest_position_index]

thanks so much sir,I succeed using your ways!

Hi @milmandre, I was also looking for some solution when it comes to certain corners of a face. I found that the entity.bounds.min doesn’t return with my desired i.e bottom-left corner. My goal is to find one specific corner of a face when looking at it from face.normal.reverse direction. So I used the face.outer_loop to get the vertices in order. For my vertical faces (walls) it seems working well and the below code selects the first vertex on the outer loop. This seems to be always according to a certain order, starting from the bottom-left. …however, I am not very sure about the underlying code for the loop but would be great if @DanRathbun or other experienced guys could confirm it.

bottom_left_pnt = face.outer_loop.vertices[0]

Here is a small screen video to demonstrate it.

I think I have to work on this further. I tested this in my more complex model and it is not the case that the face.outer_loop.vertices always start at a sort of bottom-left corner. Please, if anyone , take my previous comment with some salt.

Not just that, but “bottom left” is ambiguous. As seen from what direction? Is the answer the same when you orbit the model?

I don’t think that’s for sure.
First of all: What “bottom-left corner” is meaning?
Second the face.outer_loop.vertices kep the order of the loop vertices, but the start point could be depends of something else than “bottom-left” E.g.
This proves the opposite of your claim.
You will get four lines here…

def test_bottom_left
  pts = [ [1,1,1], [2,1,1], [2,1,2], [1,1,2] ]
  ents = Sketchup.active_model.active_entities
  4.times{|i|
    face = ents.add_face( pts )
    bottom_left_pnt = face.outer_loop.vertices[0]
    ray = ents.add_edges( bottom_left_pnt, ORIGIN )
    UI.messagebox('Next?')
    ents.erase_entities( face.edges ) if i<3
    pts<<pts.shift
  }
end
test_bottom_left

So you have to find (and define) other way to find the “bottom-left”. Or have to be sure how the face is created.

My solution …

I used this little component to test the vertex locations …

Point Indicator.skp (37.9 KB)

image

  # get_lower_left_corner()
  # Returns the lower left vertex and position of a vertical quad face, as
  #  viewed in the opposite direction of the face's normal.
  # @param quad [Sketchup::Face] the quad face reference.
  # @return [Array<(Sketchup::Vertex,Geom::Point3d)>,nil] nil if not a vertical
  # face, otherwise an array of the lower left vertex and it's 3D position.
  def get_lower_left_corner(quad, testing: false)
    unless quad.normal.perpendicular?(Z_AXIS)
      puts "Not a vertical face!"
      return nil
    end
    loop = quad.outer_loop
    verts = loop.vertices
    center = Geom.intersect_line_line(
      [ verts[0], verts[2] ],  # diagonals
      [ verts[1], verts[3] ]
    )
    bottom_pts = verts.map(&:position).sort_by(&:z)[0..1]
    cross = bottom_pts.map do |pt|
      center.vector_to(pt).cross(quad.normal)
    end
    vec = cross.find {|vec| vec.z > 0.0 }
    index = cross.index(vec)
    bottom_left_pt = bottom_pts[index]
    bottom_left_vertex = verts.find do |vert|
      vert.position == bottom_left_pt
    end
    if testing
      puts "bottom_pts[0] cross product: #{cross[0]}"
      puts "bottom_pts[1] cross product: #{cross[1]}"
      model = Sketchup.active_model
      ents = model.active_entities
      cdef = model.definitions["Point Indicator"]
      ents.add_instance(cdef,bottom_left_pt)
    end
    return [ bottom_left_vertex, bottom_left_pt ]
  end

NOTE: The example does not verify that the face is a quad.

1 Like

The direction is face.normal.reverse in SU terms, so basically we are looking at the face from normal direction.

Thank you, …I have certain info about how the faces is created, but I wouldn’t consider them as I have no a real pattern I could rely on.

Hi @DanRathbun, thanks for this work around. I have arrived to the same solution yesterday evening which I brought from fluid mechanics, the vortex vectors :slight_smile: …so from the vectors cross products’ z component we can differentiate in which side they are positioned on the face. …BTW, I think the left side points should be the one with negative z, shouldn’t be?
Only thing I need to stay with, is the bounding box center. Not all walls are rectangular, so sometimes it can be very complex polygons, but always vertical edges. I guess, if the wall is rotated around the Z_AXIS then bigger the bounding box. But the wall will then be in the diagonal anyway, so the center could be used, correct? …I have not done the code yet but I’ll use yours :wink: thanks again.

Bounding boxes are always aligned to the global axes. So the BB center might not even be on the wall’s face.

My solution accepts the vertical wall face no matter what the orientation to the global axes may be.

I tested it to see which, and it was the positive.

Hi @DanRathbun, correct, you are right. It’s because the order of the cross product. In my model I did the contrary, so in my case it is the negative z are on the left hand side.
If a and b are vectors:

a x b = - b x a 

I see your point withe bounding boxes. Actually for this small test we need only the little z component of the vector, so the center might be outside the face.plane. Anyhow, I will follow it up as my code evolves and make changes when needed.

1 Like