- require 'sketchup.rb'
- #SketchyBevel
- #Bevel all selected faces.
- #Adds a context menu item called Bevel
- #
- #
- #Copyright 2008 Chris Phillips
-
-
- module SketchyBevel
- if($bevel_menu_loaded==nil)
- $bevel_menu_loaded=true
-
- if(Sketchup.active_model.options["UnitsOptions"]["LengthUnit"]<2)
- $bevelSettings = [1.0.inch, "true"]
- else
- $bevelSettings = [5.0.cm, "true"]
- end
- UI.add_context_menu_handler { |menu|
- selection=Sketchup.active_model.selection
-
- if(!selection.empty?)
- menu.add_item("Bevel"){
- prompts = ["Amount","Cap Holes"]#,"Interations"]
- results = inputbox(prompts, $bevelSettings,["","true|false"], "Bevel selection")
- if (results)
- $bevelSettings=results
- Sketchup.active_model.selection.bevel(results[0],results[1])
- end
- }
- menu.add_item("Cap Holes"){
- Sketchup.active_model.start_operation("Cap Holes")
- Sketchup.active_model.selection.cap_holes()
- Sketchup.active_model.commit_operation()
- }
- end
- }
- end
- class Sketchup::Selection
-
- def find_adjcent_coplaner_edges(edge,plane)
- edges=[]
- edge.vertices.each{|v|
- v.edges.each{|e|
- next if(e==edge)
- if(!e.get_attribute("SPCAP","used",false)&&
- e.start.position.on_plane?(plane)&& e.end.position.on_plane?(plane))
- edges.push(e)
- e.set_attribute("SPCAP","used",true)
- edges.push(find_adjcent_coplaner_edges(e,plane))
- end
- }
- }
- return edges.flatten
-
- end
-
- def cap_holes()
- self.faces.each{|f|
- f.edges.each{|e|e.delete_attribute("SPCAP","used")}
- }
-
- newfaces=[]
- while(true)
- caps=[]
- self.faces.each{|f|
- f.edges.each{|e|
- if(e.faces.length<2)
- e.vertices.each{|v|
- v.edges.each{|ve|
- if(e!=ve && ve.faces.length<2 &&
- !e.get_attribute("SPCAP","used",false) && !ve.get_attribute("SPCAP","used",false) )
-
-
- #make face out of e and ve
- #e.set_attribute("SPCAP","used",true)
- #ve.set_attribute("SPCAP","used",true)
- pts=[v.position,e.other_vertex(v).position,ve.other_vertex(v).position]
- plane=Geom.fit_plane_to_points(pts)
- ce=find_adjcent_coplaner_edges(ve,plane)
- #ce.push(ve)
- #puts ce.inspect
- caps.push(ce)
- end
- }
- }
- end
- }
- }
- caps.each{|f|
- if(f.length==2)
- nf= [f[0].start.position.to_a,f[0].end.position.to_a,f[1].start.position.to_a,f[1].end.position.to_a].uniq
- #puts "tri"
- #puts nf.inspect
- newfaces.push(Sketchup.active_model.active_entities.add_face(nf))
- else
- #puts "quad"
- #puts f.inspect
- #puts f
- #cf=f.select{|tf|!tf.deleted?}
- newfaces.push(Sketchup.active_model.active_entities.add_face(f))
- end
- }
- #self.clear()
- #self.add(caps)
- #puts newfaces
- #self.add(newfaces) if(!newfaces.empty?)
- self.faces.each{|f|
- f.edges.each{|e|e.delete_attribute("SPCAP","used")}
- }
- puts caps.length
- break# if(caps.length==0)#found no caps so exit loop.
-
- end
- return newfaces
- end
- def old_cap_holes()
- newfaces=[]
- while(true)
- caps=[]
- self.faces.each{|f|
- f.edges.each{|e|
- if(e.faces.length<2)
- e.vertices.each{|v|
- v.edges.each{|ve|
- if(e!=ve && ve.faces.length<2 &&
- !e.get_attribute("SPCAP","used",false) && !ve.get_attribute("SPCAP","used",false) )
- #make face out of e and ve
- e.set_attribute("SPCAP","used",true)
- ve.set_attribute("SPCAP","used",true)
- caps.push([v.position,e.other_vertex(v).position,ve.other_vertex(v).position])
- end
- }
- }
- end
- }
- }
- caps.each{|f|newfaces.push(Sketchup.active_model.active_entities.add_face(f))}
- self.add(newfaces)
- self.faces.each{|f|
- f.edges.each{|e|e.delete_attribute("SPCAP","used")}
- }
- puts caps.length
- break if(caps.length==0)#found no caps so exit loop.
-
- end
- return newfaces
- end
-
-
- def bevel(amount,bCapHoles)
- Sketchup.active_model.start_operation("Bevel")
-
- #build a nested array of faces[edges[verts]]
- allFaces=self.faces.collect{|f|
- verts=f.outer_loop.vertices
- edges=[]
- i=0
- while i<verts.length
- edges.push([verts[i].position,verts[(i+1)%verts.length].position])
- i=i+1
- end
- edges
- }
- #make a simple array of all the points
- allPoints=allFaces.flatten
- #find any duplicate points. These points will make cap faces later.
- dupePoints=allPoints.collect{|p|allPoints.select{|p2|p==p2}}
-
- #simple array of all edges
- allEdges=[]
- allFaces.each{|f|
- f.each{|edge|allEdges.push(edge)}
- }
-
- #find overlapping edges. Each will be a bevel face
- dupeEdges=[]
- allEdges.each_with_index{|e,i|
- allEdges.each_with_index{|e2,i2|
- if(i!=i2&& (e[0]==e2[0]&&e[1]==e2[1])||(e[1]==e2[0]&&e[0]==e2[1]))
- dupeEdges.push([e,e2])
- end
- }
- }
-
- shrunkFaces=[]
- #Create a shrunken version of each face
- allFaces.each{|f|
- SketchyBevel::shrink_face(f,amount)
- pts=f.collect{|edge|edge[0]}
- shrunkFaces.push(Sketchup.active_model.active_entities.add_face(pts))
- }
- #Create the bevel. A 4 sided polygon made from each formerly overlapping edge
- newFaces=[]
- dupeEdges.each{|edge|
- newFaces.push(Sketchup.active_model.active_entities.add_face(edge.flatten))
- }
-
- bCapHoles="false"
- #~ caps=[]
- #~ capEdges=newFaces.each{|f|
- #~ f.edges.each{|e|
- #~ if(e.faces.length<2)
- #~ e.vertices.each{|v|
- #~ v.edges.each{|ve|
- #~ if(!e.get_attribute("SPBEVEL","alreadybeveled",false) &&
- #~ !ve.get_attribute("SPBEVEL","alreadybeveled",false) && e!=ve && ve.faces.length<2)
- #~ #make face out of e and ve
- #~ e.set_attribute("SPBEVEL","alreadybeveled",true)
- #~ ve.set_attribute("SPBEVEL","alreadybeveled",true)
- #~ caps.push([v.position,e.other_vertex(v).position,ve.other_vertex(v).position])
- #~ end
- #~ }
- #~ }
- #~ end
- #~ }
- #~ }
-
- #~ caps.each{|f|Sketchup.active_model.entities.add_face(f)}
-
- #capEdges=newFaces.collect{|f|
- # f.edges.select{|e|e.faces.length<2}
- # }
- #puts "caps"
- #puts capEdges.inspect
-
- #~ #cap the corners by drawing a face around each formerly duplicated point.
- dupePoints.each{|pts|
- #remove any duplicated points.
- tpts=pts.collect{|p|p.to_a}.uniq
- case tpts.length
- when 3
- Sketchup.active_model.active_entities.add_face(tpts)
- when 4
- 0.upto(tpts.length-2){|i|
- tri=[tpts[i],tpts[(i+1)%tpts.length],tpts[(i+2)%tpts.length]]
- Sketchup.active_model.active_entities.add_face(tri)
- }
-
- #Sketchup.active_model.entities.add_face(tpts[0],tpts[1],tpts[2])
- #Sketchup.active_model.entities.add_face(tpts[1],tpts[2],tpts[3])
- #ca=(tpts[2]-tpts[1]).cross(tpts[3]-tpts[2])
- #if((tpts[3]-tpts[2]).cross(tpts[0]-tpts[3]).dot(ca)<0.0)
- #Sketchup.active_model.entities.add_face(tpts[2],tpts[3],tpts[0])
- #else
- #Sketchup.active_model.entities.add_face(tpts[1],tpts[3],tpts[0])
- #end
- #Sketchup.active_model.entities.add_face(tpts.slice(0,3))
- end
-
- } if(bCapHoles=="true")
-
-
- #remove all the old geometry.
- Sketchup.active_model.selection.each{|e|e.erase!}
-
- Sketchup.active_model.selection.add(shrunkFaces)
- Sketchup.active_model.selection.add(newFaces)
- Sketchup.active_model.selection.add(cap_holes())
-
- Sketchup.active_model.commit_operation()
- end
- def faces()
- self.select{|s|s.class==Sketchup::Face}
- end
- end
-
-
- def self.shrink_face(face,amount)
-
- #TODO.face is really edges[].
-
-
- normal=(face.first[1]-face.first[0]).cross(face.last[1]-face.last[0]).normalize
- if(normal.length==0)
- plane=Geom.fit_plane_to_points([face[0][0],face[0][1],face[1][0],face[1][1],face[2][0],face[2][1]])
- puts normal
- normal=Geom::Vector3d.new(plane[0],plane[1],plane[2])
- puts normal
- end
- #normal=face.normal
- #Sketchup.active_model.entities.add_line(face.first[0],face.first[0]+normal)
-
- #find the direction to move each vert in face
- shrinkVectors=[]
- lastEdge=face.last #use last vert in face is connected to the first vert in face.
- face.each{|edge|
- #create 2 vectors from each edge
- va=lastEdge[0]-lastEdge[1]
- vb=edge[1]-edge[0]
- lastEdge=edge #use this next loop.
- #find angle between the two vectors.
- if(va.cross(vb).dot(normal)<0) #check to see if angle >180
- angle=(360.degrees-va.angle_between(vb)) #angle is >180
- else
- angle=va.angle_between(vb)
- end
- #rotate one vector 1/2 the angle between the two edges
- sv=va.transform(Geom::Transformation.new([0,0,0],normal,angle/2))
- #set the distance to move based on the amount of bevel.
- sv.length=amount/Math.sin(angle/2)
- #save.
- shrinkVectors.push(sv)
- }
-
- edge=face[0]
- before=(edge[0]-edge[1]).length
- after=(edge[0].transform(shrinkVectors[0])-edge[1].transform(shrinkVectors[1])).length
-
-
- bFlipped=false
- #~ if(edge[0]!=edge[1]&& (edge[0].transform(shrinkVectors[0])-edge[1].transform(shrinkVectors[1])).length>before)
- #~ bFlipped=true
- #~ end
-
- #now move each edge by the amount calculated. This actually shrinks the face.
- face.each_index{|index|
- edge=face[index]
- if(!bFlipped)
- edge[0].transform!(shrinkVectors[index])
- edge[1].transform!(shrinkVectors[(index+1)%face.length])
- else
- edge[0].transform!(shrinkVectors[index].reverse)
- edge[1].transform!(shrinkVectors[(index+1)%face.length].reverse)
- end
- }
- end
- end
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
复制代码
|