How to get outer points of a curved terrain?

Dear Sir,

How to get the outer points of a curved terrain? The example is as follows, the selected terrain is in blue.

one possible quick code snippet:

sel = Sketchup.active_model.selection # Current selection
edges = sel.grep(Sketchup::Edge) # grep out the edges
vertices = edges.collect(&:vertices).flatten.uniq # find out vertices
points = vertices.map(&:position) # get the positions

… then …

outer_edges = edges.find_all { |edge| edge.faces.size == 1 }
vertices = outer_edges.map(&:vertices).flatten.uniq
points = vertices.map(&:position) # get the positions
1 Like

Oh, I missed the word “outer”… :blush:

Taking a closer look at the original image and thinking over, maybe it’s even better:

sel = Sketchup.active_model.selection # Current selection
edges = sel.grep(Sketchup::Edge) # grep out the edges
outer_edges = edges.find_all{ |edge| edge.faces.one?{ |face| sel.include?(face) } }
vertices = outer_edges.map(&:vertices).flatten.uniq
points = vertices.map(&:position) # get the positions
1 Like
(not enough coffee) ... disregard.

I disagree with this. It is not necessary to force the iteration of each and every face in each and every edge’s faces collection. (ie, the Enumerable#one? method must iterate every member of a collection.)

It will be much faster to just check that the size of an edge’s faces collection has only 1 member.

Secondly, sel.include?(face) is going to be true for every face of every edge , since they are ALL selected. Therefore edge.faces.one? is always going to return false, which will cause edges.find_all to always return an empty array.

I have to argue. :slight_smile:
We probably interpret the task in different ways?

I made a very simplified version of the “terrain”, it is a 3x3 inch square grid just to easy simulate some selection and result of the sample snippet.
test_outer_points.skp (15.8 KB)

def test_outer_points
  sel = Sketchup.active_model.selection
  edges = sel.grep(Sketchup::Edge)
  outer_edges_da = edges.find_all { |edge| edge.faces.size == 1 }
  outer_edges_de = edges.find_all{ |edge| 
    edge.faces.one?{ |face| sel.include?(face) } 
  }
  vertices_da = outer_edges_da.map(&:vertices).flatten.uniq
  vertices_de = outer_edges_de.map(&:vertices).flatten.uniq
  points_da = vertices_da.map(&:position)
  points_de = vertices_de.map(&:position)
  puts "Dan: #{points_da}\nDez: #{points_de}"
end

Let see what will be the result with different selections: (see screenshots)
__
Test 1
image

test_outer_points
Dan: []
Dez: [Point3d(1, 2, 0), Point3d(2, 2, 0), Point3d(1, 1, 0), Point3d(2, 1, 0)]

__
Test 2
image

test_outer_points
Dan: [Point3d(0, 0, 0), Point3d(1, 0, 0), Point3d(0, 1, 0)]
Dez: [Point3d(0, 0, 0), Point3d(1, 0, 0), Point3d(0, 1, 0), Point3d(1, 1, 0)]

__
Test 3
image


test_outer_points
Dan: [Point3d(3, 1, 0), Point3d(3, 2, 0)]
Dez: [Point3d(1, 2, 0), Point3d(2, 2, 0), Point3d(3, 1, 0), Point3d(3, 2, 0), Point3d(2, 1, 0), Point3d(1, 1, 0)]

What do you think?

You get a point if it is outer of all terrian, I get a point as the selection’s outer…

1 Like

I know you cheated by changing the terms of the exercise.

The OP stipulated a terrain mesh, which is triangulated.

I fail to see how that makes a difference in the results.

1 Like

I don’t think the terms were cheated, but sure my simplified example does not triangulated. This is does not make any difference about the result, as @Neil_Burkholder mentioned.
(Just out of curiosity, I downloaded one model from 3DWH and checked on it. I have got similar result.)

If you apply the code to the selected part of the terrain mesh as shown in the first post, your method for outer_edges will result an empty array. This is not necessarily a bad thing, but this is a fact.
You and me interpreted the “terms of the exercise” differently.
__

My interpretation: it will find all that edges where only one of the faces of the edge included in a selection.
Actually itt will find a outer edges of the selection. Let say, this is a the border or bounding edges of selection. This is not necessarily a good thing, but this is a fact.

I know that myself and perhaps the original poster do not speak English at the native level … this can of course cause some misunderstanding and “bad” explanation. Maybe that’s the case. Sorry. :blush:
:peace_symbol:

Sir,I have a question,is the mesh must be triangulated?The order of points acquired in this way is a bit messy.For example,in Test3, can I get test_outer_points output in a certain order?Like this:

Example:[Point3d(1, 1, 0), Point3d(2, 1, 0), Point3d(3, 1, 0), Point3d(3, 2, 0), Point3d(2, 2, 0), Point3d(1, 2, 0)]

You asked Dan, but since I made the method, I will answer if you don’t mind… :wink:

No

Yes, it will be always like “messy”.

No. Not from test_outer_points method. You have to write an other one to get the right order.

NO, I had a brain fart. Never mind what I said.

My eyes did not see the mesh outside the selection. So testing for 1 member face arrays would not work.

Sorry.

1 Like

Oh,thanks for your answer,maybe I didn’t express it clearly,the question I want to ask is is there any way to output the outer_points in a certain order

I guess you want the order of the points, where you can connect one after other with edges, to get a closed loop. Don’t you?
image

If you have SU 2020.1 or later, I have a relatively simple method in my mind (using Entities#weld-instance_method ). I can do it later today, if you wish…

If you are using SU 2018, as indicated in your profile, it will be more complex but can be done too. Maybe I can find some time to think about more…

Yes! That’s what I want,but I use SU 2018, if you can help me solve the problem, I would be very grateful!Waiting for your reply :slightly_smiling_face:

Something like this should work for 2018

def outer_edges
  sel = Sketchup.active_model.selection
  edges = sel.grep(Sketchup::Edge)
  edges.find_all { |edge| edge.faces.one? { |face| sel.include?(face) } }
end

def sort_points(edges)
  last_edge = edges.pop
  sorted = [last_edge]
  for i in (0..(edges.count - 1)) do
    last_edge = edges.find do |edge| 
      edge.start.position == last_edge.start.position || 
        edge.start.position == last_edge.end.position || 
        edge.end.position == last_edge.start.position || 
        edge.end.position == last_edge.end.position 
    end
    sorted.push(last_edge)
    edges.delete(last_edge)
  end
  sorted.map { |e| [e.start.position, e.end.position] }.flatten.uniq(&:to_s)
end

sort_points(outer_edges)
1 Like

Sir,the “sort_points” sort_points I output according to your code still has the triangulated problem.


For example,I chose two square grids,and the result is:

 [Point3d(0, 0, 0), Point3d(39.3701, 0, 0), Point3d(0, 39.3701, 0), Point3d(39.3701, 39.3701, 0), Point3d(78.7402, 39.3701, 0), Point3d(78.7402, 0, 0)]

Obviously the output points are not in order,I am not sure if there is some problems with my operation.

Did you copy and paste my method? This is what I get.

I seem to have succeeded here too, I don’t know where I made a mistake in the code before, thank you again for your help! :slightly_smiling_face: :slightly_smiling_face: