Why does this code break?
-
I'm trying to develop a plugin to create regular polyhedra - initially, the Platonic Solids.
I have already extended the original Sketchup Shapes plugin, which draws a cone (among other shapes) and works.
But when I copy and adapt the code to draw a Tetrahedron, it fails, and I can't see why.
This works, for a cone:
` # Create the base
circle = container.add_circle ORIGIN, Z_AXIS, radius, num_segments
base = container.add_face circle
base_edges = base.edgesCreate the sides
apex = [0,0,height]
edge1 = nil
edge2 = nil
base_edges.each do |edge|
edge2 = container.add_line edge.start.position, apex
edge2.soft = true
edge2.smooth = true
if edge1
container.add_face edge, edge2, edge1
end
edge1 = edge2
endCreate the last side face
edge0 = base_edges[0]
container.add_face edge0.start.position, edge0.end.position, apex`I've adapted the code to draw a Tetrahedron, centred on the origin, with size specified by the radius from origin to corner points.
All the variables are defined.
This code doesn't work. (Errors shown below the code)
` # Draw base and define apex point
base_down_by = -radius/3.0
triangle = container.add_ngon [0,0, base_down_by], Z_AXIS, radius * COS_ASIN_0333, 3
base = container.add_face triangle
base_edges = base.edges
p "Base edges: " + base.edges.inspectCreate the sides
apex = [0,0,radius]
edge1 = nil
edge2 = nil
base_edges.each do |edge|
p "Edge: " + edge.inspect
edge2 = container.add_line edge.start.position, apex
edge2.soft = false
edge2.smooth = falseif edge1 container.add_face edge, edge2, edge1 end edge1 = edge2
end # do
Create the last side face
edge = base_edges[0]
container.add_face edge.start.position, edge.end.position, apex`For some reason I can't make out, the third base edge is being deleted before or during the .each do loop.
Error messages in Ruby console:
"Base edges: [#<Sketchup::Edge:0xef0cc88>, #<Sketchup::Edge:0xef0cc70>, #<Sketchup::Edge:0xef0cc58>]" "Edge: #<Sketchup::Edge:0xef0cc88>" "Edge: #<Sketchup::Edge:0xef0cc70>" "Edge: #<Deleted Entity:0xef0cc58>" Error: #<TypeError: reference to deleted Edge> D:/Documents/GitHub/my-polyhedra/src/jwm_polyhedra/polyhedra.rb:126:in
start'
D:/Documents/GitHub/my-polyhedra/src/jwm_polyhedra/polyhedra.rb:126:inblock in create_entities' D:/Documents/GitHub/my-polyhedra/src/jwm_polyhedra/polyhedra.rb:124:in
each'
D:/Documents/GitHub/my-polyhedra/src/jwm_polyhedra/polyhedra.rb:124:increate_entities' D:/Documents/GitHub/my-polyhedra/src/jwm_polyhedra/parametric.rb:49:in
initialize'
D:/Documents/GitHub/my-polyhedra/src/jwm_polyhedra/polyhedra.rb:255:innew' D:/Documents/GitHub/my-polyhedra/src/jwm_polyhedra/polyhedra.rb:255:in
block in module:Polyhedra'
-e:1:incall'
What's deleting the third edge before it can be used? When I comment out the line that draws the edges, the base triangle is drawn correctly.
The code excerpt starts at line 114. Line 126 (where the code errors) is the line
base_edges.each do |edge|
Any pointers as to what's happening here would be welcome.
I'm using SU2014 Pro, on Windows 7 Pro 64 bit.
Many thanks in advance to anyone who can help me understand why this code isn't working.
I can find a workaround, I'm sure, but I'm really puzzled and would like to understand what's going wrong.
John McC
PS. Further exerimentation merely confuses me.
If I omit the line in the do loop
container.add_face edge, edge2, edge1
then there aren't any errors, and the tetrahedron is partially drawn, with all three edges going to the apex, and the last side face drawn, but the other two faces not drawn.If I replace this code altogether by:
# Create the sides apex = [0,0,radius] edge1 = nil edge2 = nil for i in 0..2 edge = base_edges[i] tetrahedron = container.add_face edge.start.position, edge.end.position, apex end
I get a tetrahedron drawn as I want, but with the reverse of all faces outside.
It doesn't matter what order I specify the points of the faces, they always come out reversed.
How do I iterate over the faces of the tetrahedron to do
face.reverse!
PPS
This seems to do the trick:
container.each do |entity| if entity.is_a? Sketchup::Face entity.reverse! end end
-
you should have a look at geodesic .rb, if you haven't already...
uses
Geom::PolygonMesh.new
to populate the point arrays...it's very fast.
john -
@driven said:
you should have a look at geodesic .rb, if you haven't already...
uses
Geom::PolygonMesh.new
to populate the point arrays...it's very fast.
johnIf you do that, be aware that a PolygonMesh triangulates each Face, which might not be what you wanted. The triangulating edges may be hidden, so be sure to view hidden geometry to know what you got.
-
Your code contains an assumption that can cause errors: you assume that the iterator will traverse the edges of the base triangle in reverse order to how their start and end are oriented. Suppose you label the edges of the base A, B, C in the order the iterator finds them. Your code begins by drawing an edge from A.start to the apex, and remembers it as edge1. In the next iteration it draws an edge from B.start to the apex and sets it as edge2. It then tries to add a face bounded by B, edge2 (from B.start) and edge1 (from A.start). But unless the iterator is traversing the edges in reverse direction to their start->end orientation, A.start is not a vertex of B at all! The Face will fail because the edges do not form a closed shape! If you insert a pause such as a UI.messagebox in the iteration you can see this happen as the edges are drawn. Switch to using A.end instead of A.start and it the nature of the problem will change. Of course, that just builds in the reverse assumption.
I didn't get your error about a deleted edge, so I'm not certain, but this may be happening because sometimes when SU adds a face it redraws the edges - possibly to reorder their start->end orientations to form a continuous loop. When it does this, even though the new edge is in the identical location to the original, the original is marked as deleted. As a rule, it is not safe to hold references across an operation that adds faces to an entities collection.
You might want to try just adding all the edges and then iterating the final collection invoking find_faces on each edge.
Steve
-
Many thanks to all respondents.
I am a good deal clearer now about what's happening.
I found a workaround before I saw the responses, but they will stand me in good stead for future work.
I iterated on the base edges instead of separate edge1, edge2, and this worked:
` # Draw base and define apex point
base_down_by = -radius/3.0
triangle = container.add_ngon [0,0, base_down_by], Z_AXIS, radius * COS_ASIN_0333, 3Reverse to get front face outside
triangle.reverse!
base = container.add_face triangle
base_edges = base.edgesCreate the sides
apex = [0,0,radius]
for i in 0..2
edge = base_edges[i]
tetrahedron = container.add_face edge.start.position, edge.end.position, apex
container.each do |entity|
if entity.is_a? Sketchup::Face
entity.reverse!
end #if
end #do
end #for`Sorry, I can't seem to get the code to indent here, which makes it a bit harder to read.
-
@johnwmcc said:
Many thanks to all respondents.
I am a good deal clearer now about what's happening.
I found a workaround before I saw the responses, but they will stand me in good stead for future work.
I iterated on the base edges instead of separate edge1, edge2, and this worked:
Sorry, I can't seem to get the code to indent here, which makes it a bit harder to read.
use the code box
# Draw base and define apex point base_down_by = -radius/3.0 triangle = container.add_ngon [0,0, base_down_by], Z_AXIS, radius * COS_ASIN_0333, 3 # Reverse to get front face outside triangle.reverse! base = container.add_face triangle base_edges = base.edges # Create the sides apex = [0,0,radius] for i in 0..2 edge = base_edges[i] tetrahedron = container.add_face edge.start.position, edge.end.position, apex container.each do |entity| if entity.is_a? Sketchup;;Face entity.reverse! end #if end #do end #for
-
I'd been wrapping the code in Ruby tags, which I thought would give syntax highlighting, but it doesn't seem to here.
I didn't see the meaning of the icon for Code box, and hadn't happened to hover the mouse over it, so didn't see the tooltip either.
What's the purpose of the Ruby tags, then?
-
differentiation...
Sketchup.version
on my mac returns this,
14.0.4899
try it on yours by selecting all, copy/paste ...etc...etcbut, mainly it was first kid on the block so it got kept...
john
-
The advantage of
code
tags is they can easily be copied and pasted in a code editor, or one of the SketchUp console dialogs.I have my editor and SketchUp set up[1] where I can run the pasted code in SketchUp with the push of a key. So testing posted code is fast and easy - copy, paste, run.
That's why it's important to post "run-able" code - leaving variables and constants undefined makes it harder to help.
Advertisement