Creating a Hole in a Solid


#1

I have an existing solid that I would like to pushpull a circular hole through. I was trying to use the following code to do this:

circlearray = entities40.add_circle center_pt1, normal1, radius1, 24
new_face41 = entities40.add_face circlearray
new_face41.pushpull -@Wallsheaththk

However this code does not seem to work, after some testng I was able to get this code to work instead:

circlearray = entities40.add_circle center_pt1, normal1, radius1, 24
edge1 = circlearray[0]
facearray = edge1.faces
new_face41 = facearray[0]
new_face41.pushpull -@Wallsheaththk

Seems like a convoluted path to get at the face of the circle to then pushpull the hole…


#2

Agreed. It would have been more intuitive if the #add_circle method returned a closed Sketchup::Arcurve object instead, and that class have a #inner_face method.

It is too late now to change that (or the #add_arc method) so we work with what they’ve given us, which is the Sketchup::Edge#curve method.

Anyway, … there are several ways to do it, and some of the reference assignments can be skipped as they just eat time.


circle_array = entities40.add_circle( center_pt1, normal1, radius1, 24 )

if circle_array # nil if failure
  face = circle_array[0].common_face(circle_array[3])
  if face # nil if failure
    # do some code
  end
end

I choose to promote the use of non-adjacent edges of the circle, though it likely doesn’t matter which two edges of the circle you use. Ie,

face = circle_array.first.common_face(circle_array.last)

Another approach …

before = entities40.grep(Sketchup::Face)
circle_array = entities40.add_circle( center_pt1, normal1, radius1, 24 )
added_faces = entities40.grep(Sketchup::Face) - before

if added_faces is size 1, you’re done, otherwise you use Enumerable#find and some boolean test to determine which face is the one you want. But the upper example is the more direct and easiest.

I show you this before and after array subtraction filtering method just because it is generic to many situations.

You can even create nifty block form methods that do this filtering …


  def collect_added_faces( ents, &block )
    #
    before = ents.grep(Sketchup::Face)
    # Call the block passing in the ents reference:
    yield ents
    ents.grep(Sketchup::Face) - before
    #
  end


  def collect_added_entities( ents, &block )
    #
    before = ents.grep(Sketchup::Drawingelement)
    # Call the block passing in the ents reference:
    yield ents
    ents.grep(Sketchup::Drawingelement) - before
    #
  end

And then to use them using your example variable names …

new_faces = collect_added_faces( entities40 ) do |ents|
  ents.add_circle( center_pt1, normal1, radius1, 24 )
end

#3

So in your opinion which would be the quickest and least computationally expensive method to do what I am trying to do?

You are right in that I’ve got a lot of variable assignments going on for a very mundane or supposedly simple task, I’m not very happy with the way this has worked out.


#4

I don’t really understand the problem.

You secoond code example can be reformated as:

edge = entities40.add_circle(center_pt1, normal1, radius1, 24).first
face = edge.faces.first
face.pushpull(-@Wallsheaththk)

You don’t have to use as many intermediate variables; you are allowed to chain method calls on one line.


#5

Thank-you for the code examples. I’ve tightened up the lines of code and chained the method calls as you suggested. I typically gravitate toward a more verbose style initially since it helps me understand what is going on and then I try to compress things once I’ve tested it and I know its working.


#6

I recommend my first example because it has checks after the #add… call which can sometimes fail.
If you chain onto these creation methods, expect sometimes that you’ll get weird NoMethodError exceptions on NilClass. (There are many posts here in this category where coders are wondering why they get these errors and it happens that the arguments passed to the geometry creation methods are sometimes incompatible and the API will spit out nil for a result.)

Once you get a valid egdes array, you can choose either the #common_face or #first.faces.first pattern.


#7

When performance is concerned, and you have a specific case like this, profile it. That’ll be your only source of truth. There is so much that could affect performance.

I’ve been trying to make a wrapper to make ruby-prof work in SketchUp; https://github.com/SketchUp/speedup

It’s still an early proof of concept, but if feel free to try to play around with it. I’ve used it for some of my own personal projects and every time I find interesting results. Bottlenecks often appear where you don’t expect them.

Benchmark your code for real world timing. Then profile to find the bottlenecks. (the profiling process itself have an overhead, so the times from that as relative measurements)