# Name : opener # Description : create a hole with the selected face # Author : Didier Bur # Usage : select a face and run the script, it will pushpull this face to the nearest other face. # Date : 09.Sept.2oo4 # Type : tool require 'sketchup.rb' def in_face( p, f ) v = f.outer_loop.vertices i = 0 sum_angles = 0 two_pi = Math::PI * 2 #6.28318530717959 0.upto (v.length - 1) do |a| if(i != (v.length - 1)) vec1 = p.vector_to v[i] vec2 = p.vector_to v[i + 1] else vec1 = p.vector_to v[i] vec2 = p.vector_to v[0] end sum_angles = sum_angles + vec1.angle_between(vec2) i = i + 1 end if( (two_pi - 0.1) < sum_angles) and ( sum_angles < (two_pi + 0.1)) return true else return false end end def make_holes model = Sketchup.active_model model.start_operation "hole" ss = model.selection if ss.empty? UI.messagebox("No selection.") return nil end # What's in the selection ? i = 0 not_a_face = 0 0.upto( ss.length - 1 ) do |something| element = ss[i] if( element.typename != "Face") not_a_face = not_a_face + 1 end i = i + 1 end # of upto if( not_a_face != 0 ) UI.messagebox(not_a_face.to_s + " objects among " + ss.length.to_s + " are not faces and will be ignored.") end # Select all faces in the model and stores an array of normals entities = model.active_entities all_faces = [] all_norms = [] all_norms_reverse = [] all_plans = [] j = 0 entities.each { |current_ent| if ( current_ent.typename == "Face" ) all_faces[j] = current_ent all_norms[j] = current_ent.normal all_norms_reverse[j] = current_ent.normal.reverse all_plans[j] = current_ent.plane j = j + 1 end #of if } i = 0 m = 0 faces_and_heights = [] ss.each{|current_face| current_face = ss[i] if( current_face.typename == "Face") # Face's plane equation, normal and reverse plan = current_face.plane #current_norm = all_norms[i] current_norm = current_face.normal #current_norm_reverse = all_norms_reverse[i] current_norm_reverse = current_face.normal.reverse # First vertex of face first_point = current_face.vertices[0].position # Find the closest parallel face in the faces/normals_reverse array #------------------------------------------------------------------ #Virtual line for intersection to nearest face current_norm_reverse.length = 1000 second_point = Geom::Point3d.new( (first_point[0] + current_norm_reverse[0]), (first_point[1] + current_norm_reverse[1]), (first_point[2] + current_norm_reverse[2])) v_line = [Geom::Point3d.new(first_point), second_point] k = 0 all_dists = [] mini = 10000 ind_face = 0 all_norms_reverse.each{|norm| if ( norm.parallel?(current_norm_reverse) and ( norm == current_norm ) and (plan[3] = all_plans[k][3]) ) # Calculate the distance between current_face and parallel faces paral_face = all_faces[k] paral_plan = paral_face.plane inters = Geom.intersect_line_plane(v_line, paral_plan) t = in_face( inters, paral_face ) dist = first_point.distance_to_plane(paral_face.plane) if ( dist < mini ) and ( dist != 0 ) and t mini = dist ind_face = k end end k = k + 1 } # Stores face and pushpull-heigth faces_and_heights[m] = current_face faces_and_heights[m + 1] = (0 - mini) #Next face in the selection i = i + 1 m = m + 2 end #of if } # end of ss.each # Pushpulls i = 0 0.upto( ss.length - 1 ) do |m| faces_and_heights[i].pushpull (faces_and_heights[i + 1]) i = i + 2 end # of upto end # of def if( not file_loaded?("opener.rb") ) # This will add a separator to the menu, but only once add_separator_to_menu("Plugins") # To add an item to a menu, you identify the menu, and then # provide a title to display and a block to execute. In this case, # the block just calls the create_box function UI.menu("Plugins").add_item("Hole") { make_holes } end #----------------------------------------------------------------------------- file_loaded("opener.rb")