 # 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”… 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. 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 ``````test_outer_points
Dan: []
Dez: [Point3d(1, 2, 0), Point3d(2, 2, 0), Point3d(1, 1, 0), Point3d(2, 1, 0)]
``````

__
Test 2 ``````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 ``````
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.  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… 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? 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 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！  