#------------------------------------------------------------------------------------------------ # Permission to use, copy, modify, and distribute this software for # any purpose and without fee is hereby granted. #------------------------------------------------------------------------------------------------ # THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. #------------------------------------------------------------------------------------------------ # IT WAS CREATED AND TESTED IN A WINDOWS ENVIRONMENT ONLY AND MAY NOT FUNCTION ON A MAC. #------------------------------------------------------------------------------------------------ # THIS PLUGIN IS ANOTHER FIGMENT OF MY IMAGENATION AND IS NOT BASED ON OR COMPLY WITH ANYTHING! #------------------------------------------------------------------------------------------------ # Name: SDM_FloorGenerator # By: sdmitch # Usage: To generate brick, tile, wood, tweed or herringbone grid pattern on a selected face. # Note: The grid pattern will be placed on the side of the face picked starting at the # closest corner to the pick point if origin corner is selected. The grid pattern can # be placed on faces of any orientation,ie vertical or horizontal. The grid pattern # will be painted with the current material, random color or material as selected. # The materials to be applied MUST be in sub-folder BTW_Textures in the plugins folder. # Date: Mar 2014 #------------------------------------------------------------------------------------------------ require 'Sketchup' # ------------------ MENU SETUP ---------------------- # unless $sdm_tools_menu $sdm_tools_menu = UI.menu("Plugins").add_submenu("SDM Tools") $sdm_Edge_tools = $sdm_tools_menu.add_submenu("Edge Tool") $sdm_Face_tools = $sdm_tools_menu.add_submenu("Face Tool") $sdm_CorG_tools = $sdm_tools_menu.add_submenu("CorG Tool") $sdm_Misc_tools = $sdm_tools_menu.add_submenu("Misc Tool") end unless file_loaded?(__FILE__) $sdm_Face_tools.add_item('FloorGenerator') { Sketchup.active_model.select_tool SDM::SDM_FloorGenerator.new } file_loaded(__FILE__) end # ------------------------------------------------------ # module SDM class SDM_FloorGenerator @@dlg=@@opt=nil def initialize @mod=Sketchup.active_model @ent=@mod.active_entities @sel=@mod.selection @vue=@mod.active_view @ip=Sketchup::InputPoint.new @colors=Sketchup::Color.names @images = Dir[File.join(File.dirname(__FILE__).gsub('\\','/'),"BTW_Textures/*.{jpg,png,tif,bmp,gif,tga,epx}")]; @textures=[]; @images.each{|i| @textures<1 Sketchup.active_model.options["UnitsOptions"]["LengthUnit"]=2; #millimeters @defaults=["Corner","0","Current","Yes","Yes","0.5","No","No","0","No","No","30","150","50","12","6"] if @@opt=="Brick" @defaults=["Corner","0","Current","Yes","Yes","0","No","No","0","No","No","30","300","300","6","3"] if @@opt=="Tile"; @defaults=["Corner","0","Current","Yes","Yes","0","No","No","0","No","No","30","2000","100","3","3"] if @@opt=="Wood"; @defaults=["Corner","0","Current","Yes","Yes","0","No","No","0","No","No","30","150","50","12","6"] if @@opt=="Tweed" @defaults=["Corner","0","Current","Yes","Yes","0","No","No","0","No","No","30","150","50","12","6"] if @@opt=="Hbone"; else Sketchup.active_model.options["UnitsOptions"]["LengthUnit"]=0; #inches @defaults=["Corner","0","Current","Yes","Yes","0.5","No","No","0","No","No","30","6.0","2.0","0.5","0.25"] if @@opt=="Brick"; @defaults=["Corner","0","Current","Yes","Yes","0","No","No","0","No","No","30","12.0","12.0","0.25","0.125"] if @@opt=="Tile"; @defaults=["Corner","0","Current","Yes","Yes","0","No","No","0","No","No","30","60.0","3.0","0.125","0.125"] if @@opt=="Wood"; @defaults=["Corner","0","Current","Yes","Yes","0","No","No","0","No","No","30","6.0","2.0","0.5","0.25"] if @@opt=="Tweed"; @defaults=["Corner","0","Current","Yes","Yes","0","No","No","0","No","No","30","6.0","2.0","0.5","0.25"] if @@opt=="Hbone"; end @spt,@rot,@app,@flw,@fww,r2r,@rtt,@rtr,txs,@rfr,@bev,twa,tbx,tby,gw,gd=Sketchup.read_default("FloorGenerator",@@opt,@defaults) @BTW_x=tbx.to_l;@BTW_y=tby.to_l;@GW=gw.to_l;@HW=@GW/2.0;@GD=gd.to_l; txw,txh=txs.split(","); if txh then @txs=[txw.to_l,txh.to_l] else @txs=txs.to_l end @twa=twa.to_f;@r2r=r2r.to_f; unless @@dlg @@dlg=UI::WebDialog.new("FloorGenerator", false,"BTW",300,300,10,10,true) @@dlg.set_html( "
Pattern Brick Tweed H\'bone
Tile Wood Reset
Size : Length
: Width
: Gap Width
: Gap Depth
Options : Grid Origin
: Grid Rotation
: Apply Material

#{@@opt!='Wood' ? '' : ''} Random Position Texture
: Rotate Texture
Random Imperfections
Add Bevel to #{@@opt}

: Texture Size (w,h)
#{@@opt!='Brick' && @@opt!='Tile' ? '' : ''} #{@@opt!='Tweed' ? '' : ''}

#{@@opt} Defaults
" ) @@dlg.add_action_callback("btw_specs") {|d,p| p.gsub!("?",""); tokens=p.split("&") @flw=@fww=@rtt=@rtr=@rfr=@bev="No" tokens.each{|t| var,val = t.split("="); case var when "FLW" then @flw=val when "FWW" then @fww=val when "WAG" then @rtt=val when "WIG" then @rtr=val when "WOB" then @rfr=val when "BVL" then @bev = val when "ORG" then @spt = val when "ROT" then @rot = val when "MAT" then @app = val when "TS" then @tsz=val when "R2R" then @r2r=val.to_f;r2r=val when "TWA" then @twa=val.to_f when "BL" then @BTW_x = val.to_l when "BW" then @BTW_y = val.to_l when "BGW" then @GW = val.to_l;@HW = @GW/2.0 when "BGD" then @GD = val.to_l end } tbx=@BTW_x.to_s.gsub('"','\"'); tby=@BTW_y.to_s.gsub('"','\"'); twa=@twa.to_s; @txs=nil (txw,txh=@tsz.split(","); if txh then @txs=[txw.to_l,txh.to_l] else @txs=@tsz.to_l end) if @tsz && @tsz != "0" gw=@GW.to_s.gsub('"','\"'); gd=@GD.to_s.gsub('"','\"');txs=@tsz.gsub('"','\"'); @defaults=[@spt,@rot,@app,@flw,@fww,r2r,@rtt,@rtr,txs,@rfr,@bev,twa,tbx,tby,gw,gd] Sketchup.write_default("FloorGenerator",@@opt,@defaults) } @@dlg.add_action_callback("OptionChanged") {|d,p| @@opt=p; if @@opt=="Reset" then ["Brick","Tile","Wood","Tweed","Hbone"].each{|o| Sketchup.write_default("FloorGenerator",o,nil)};@@opt="Tile"; end @@dlg.close; @@dlg=nil; self.dialog } RUBY_PLATFORM =~ /(darwin)/ ? @@dlg.show_modal() : @@dlg.show() end end def onMouseMove(flags, x, y, view) @ip.pick view,x,y; view.tooltip = @ip.tooltip; view.refresh Sketchup::set_status_text "Select Grid Pattern, change then Update options or sizes. Select Face for #{@@opt} pattern" end def onLButtonDown(flags, x, y, view) ph = view.pick_helper; ph.do_pick x,y; face=ph.best_picked; @cp=@ip.position; if face.is_a?(Sketchup::Face) torb = (face.area/(@BTW_x*@BTW_y)).ceil if (@BTW_x <= 1 || @BTW_y <= 1) || ( torb > 500) then return if UI.messagebox("Tile demensions may be to small. #{torb} #{@@opt} needed. Continue?",MB_YESNO)==7 view.refresh; end @mod.start_operation "SDM_FloorGenerator" eye = @vue.camera.eye; ctr=face.bounds.center face.reverse! if ((ctr.vector_to(eye)).angle_between(face.normal))>Math::PI/2.0 @edges=face.edges;@norm=face.normal; l=0; fpts=[]; lpts=[] if @@opt=="Brick" for loop in face.loops for v in loop.vertices lpts< 0 end end @mod.commit_operation rescue Exception => e @mod.abort_operation UI.messagebox("Error #<#{e.class.name}:#{e.message}.>") end end end def onRButtonDown(flags, x, y, view) onCancel(flags,view) end def onCancel(flags,view) Sketchup.send_action "selectSelectionTool:" end def deactivate(view) @@dlg.close; @@dlg=nil Sketchup.active_model.options["UnitsOptions"]["LengthUnit"]=@current_units end def draw(view) if( @ip.valid? && @ip.display? ) @ip.draw(view) end end # ################################################## # Compute the data points of the grid ################################################## # def grid_data(face) pts=face.outer_loop.vertices.collect{|v| v.position} ndx=0; cp=1e6; ls=0.0; lp=pts.length-1; # assume regular 4 sided rectangle if @spt == "Corner" pts.each_with_index{|p,i| d=p.distance(@cp);ndx=i if dls; ls=[ls,d].max} end dmax = [@BTW_x,@BTW_y].max; if (face.bounds.diagonal >= dmax*1.5); # make sure the rectangle is big enough to sub-divide ctr=face.bounds.center; ctr=ctr.project_to_plane face.plane unless ctr.on_plane? face.plane d1=pts[ndx].distance(pts[ndx-lp]); d2=pts[ndx].distance(pts[ndx-1]); if d1 >= d2 @v1=pts[ndx].vector_to(pts[ndx-lp]).normalize pol=ctr.project_to_line([pts[ndx],pts[ndx-lp]]) rot = -@rot.to_f else @v1=pts[ndx].vector_to(pts[ndx-1]).normalize pol=ctr.project_to_line([pts[ndx],pts[ndx-1]]); rot = @rot.to_f end @v2=pol.vector_to(ctr).normalize; if @rot != "0" tr=Geom::Transformation.rotation(pts[ndx],@norm,rot.degrees) @v1.transform! tr; @v2.transform! tr end dx = @BTW_x + @GW; dy = @BTW_y + @GW; nx=(face.bounds.diagonal*1.5/dx).ceil;ny=(face.bounds.diagonal*1.5/dy).ceil; pt0 = ctr.offset(@v1,-dx*(nx/2)).offset(@v2,-dy*(ny/2)) @data=[];row=[]; cnt=0; max=nx*ny; #@ent.add_cpoint(pt0) case @@opt when "Wood" yd = 0.0; #nx *= 2; ny *= 2; for i in 0..ny p0=row[0]=pt0.offset(@v2,yd) d = 0.0; ty = dy for j in 1..nx tx=dx if j==1 begin tx=rand*dx end until tx>=dx*0.25 && tx<=dx*0.75 else unless @flw=="Yes" begin tx=rand*dx end until tx>=dx*0.75 end end d += tx; row[j]=p0.offset(@v1,d) end unless @fww=="Yes" begin ty=rand*dy end until ty>=dy*0.5 end row.push ty; yd += ty @data[i]=row;row=[] end when "Tweed" dx=@BTW_y + @GW;dy=@BTW_x + @GW; xd=dx/Math.cos(@twa.degrees) yd=dy*Math.cos(@twa.degrees) xx=dy*Math.sin(@twa.degrees) ny=((face.bounds.diagonal*1.5)/yd).ceil;nx=((face.bounds.diagonal*1.5)/xd).ceil; pt0 = ctr.offset(@v1,-(xd*(nx/2))).offset(@v2,-(yd*(ny/2))) for i in 0..ny row[0]=pt0.offset(@v2,yd*i) if i%2==1 then row[0].offset!(@v1,-xx) end for j in 1..nx row[j]=row[j-1].offset(@v1,xd) end @data[i]=row;row=[] end end case @@opt when "Brick","Tile" ; self.brick_tile(face,dx,dy,nx,ny,pt0) when "Wood" ; self.wood(face,nx,ny) when "Tweed" ; self.tweed(face,nx,ny) when "Hbone" ; self.hbone(face) end else UI.messagebox "This face is to small for #{@tile_size} tiles";return false end end # ################################################## # Create brick/tile pattern grid on selected face ################################################## # def brick_tile(f,dx,dy,nx,ny,pt0) tg=@ent.add_group;tge=tg.entities;tgt=tg.transformation fg=@ent.add_group f; cnt=0; max=nx*ny; md=1e9 for i in 0..ny+1 p0=pt0.offset(@v2,dy*i) p1=p0.offset(@v1,dx*nx) tge.add_face(p0-@norm,p0+@norm,p1+@norm,p1-@norm) unless i>0 p1=p0.offset(@v2,dy*ny) tge.add_face(p0-@norm,p0+@norm,p1+@norm,p1-@norm) end end cnt=0; max=nx*ny for i in 0..ny p0=pt0.offset(@v2,dy*i) i>0 ? xd=((i*@r2r)%100/100.0)*dx : xd=dx for j in 0..nx cnt+=1;self.progress_bar(cnt,max,"#{@@opt} Grid") p1=p0.offset(@v1,xd);xd=dx p2=p1.offset(@v2,dy) tge.add_face(p1-@norm,p1+@norm,p2+@norm,p2-@norm) (d=@cor.distance(p0); (cpt=p0;md=d) if md>d) if @spt=="Corner" p0=p1 end end (tr = Geom::Transformation.new(cpt.vector_to(@cor)); tge.transform_entities(tr,tge.to_a )) if @spt=="Corner" @egrp=@ent.add_group;@ege=@egrp.entities;@egt=@egrp.transformation tge.intersect_with(true,tgt,@ege,@egt,false,fg) fg.explode; tg.erase!; end # ################################################## # Create wood pattern grid on selected face ################################################## # def wood(f,nx,ny) tg=@ent.add_group;tge=tg.entities;tgt=tg.transformation fg=@ent.add_group f; cnt=0; max=nx*ny; md=1e9 for i in 0...ny dy = @data[i][-1]; for j in 1..nx cnt += 1; self.progress_bar(cnt,max,"Wood grid") p0=@data[i][j-1].offset(@norm,-1) p1=@data[i][j-1].offset(@norm,1) p2=@data[i][j].offset(@norm,1) p3=@data[i][j].offset(@norm,-1) tge.add_face(p0,p1,p2,p3) p0=@data[i][j].offset(@norm,-1) p1=@data[i][j].offset(@norm,1) p2=@data[i][j].offset(@v2,dy).offset(@norm,1) p3=@data[i][j].offset(@v2,dy).offset(@norm,-1) tge.add_face(p0,p1,p2,p3) (d=@cor.distance(@data[i][j-1]); (cpt=@data[i][j-1];md=d) if md>d) if @spt=="Corner" end end (tr = Geom::Transformation.new(cpt.vector_to(@cor)); tge.transform_entities(tr,tge.to_a )) if @spt=="Corner" @egrp=@ent.add_group;@ege=@egrp.entities;@egt=@egrp.transformation tge.intersect_with(true,tgt,@ege,@egt,false,fg) fg.explode; tg.erase!; end # ################################################## # Create Tweed pattern grid on selected face ################################################## # def tweed(f,nx,ny) tg=@ent.add_group;tge=tg.entities;tgt=tg.transformation fg=@ent.add_group f; cnt=0; max=nx*ny; md=1e9 for i in 1..ny for j in 1..nx cnt += 1; self.progress_bar(cnt,max,"Tweed grid") p0=@data[i-1][j-1].offset(@norm,-1) p1=@data[i-1][j-1].offset(@norm,1) p2=@data[i][j-1].offset(@norm,1) p3=@data[i][j-1].offset(@norm,-1) tge.add_face(p0,p1,p2,p3) p0=@data[i-1][j-1].offset(@norm,-1) p1=@data[i-1][j-1].offset(@norm,1) p2=@data[i-1][j].offset(@norm,1) p3=@data[i-1][j].offset(@norm,-1) tge.add_face(p0,p1,p2,p3) (d=@cor.distance(@data[i-1][j-1]); (cpt=@data[i-1][j-1];md=d) if md>d) if @spt=="Corner" end p0=@data[i-1][nx].offset(@norm,-1) p1=@data[i-1][nx].offset(@norm,1) p2=@data[i][nx].offset(@norm,1) p3=@data[i][nx].offset(@norm,-1) tge.add_face(p0,p1,p2,p3) end (tr = Geom::Transformation.new(cpt.vector_to(@cor)); tge.transform_entities(tr,tge.to_a )) if @spt=="Corner" @egrp=@ent.add_group;@ege=@egrp.entities;@egt=@egrp.transformation tge.intersect_with(true,tgt,@ege,@egt,false,fg) fg.explode; tg.erase! end # ################################################## # Create Herringbone pattern grid on selected face ################################################## # def hbone(f) dx=@BTW_y + @GW;dy=@BTW_x + @GW;ctr=f.bounds.center ny=((f.bounds.diagonal*1.2)/(dy*0.707107)).ceil;nx=((f.bounds.diagonal*1.2)/(dx/0.707107)).ceil; pt = ctr.offset(@v1,-((dx/0.707107)*(nx/2))).offset(@v2,-((dy/0.707107)*(ny/2))/2); vup=@v1.transform Geom::Transformation.rotation(pt,f.normal,45.degrees) vdn=@v1.transform Geom::Transformation.rotation(pt,f.normal,-45.degrees) fg=@ent.add_group f; fgt=fg.transformation; fg.name="Face" tg=@ent.add_group; tge=tg.entities; tgt=tg.transformation xv=@v1;xv.length=dx/0.707107; yv=@v2;yv.length=dy/0.707107 cnt=0; max=nx*ny; md=1e9; gp=pt; p0=pt.offset(Geom::Vector3d.new(0,0,-1)) for i in 0..ny/2 for j in 0..nx cnt += 1; self.progress_bar(cnt,max,"H'bone grid") p1=p0.offset(vdn,dy) p2=p1.offset(vup,dx) p3=p2.offset(vdn,-dy) b=tge.add_face(p0,p1,p2,p3) b.reverse! unless b.normal.samedirection? @norm b.pushpull 2 cnt += 1; self.progress_bar(cnt,max,"H'bone grid") p1=p0.offset(vup,dy) p2=p1.offset(vdn,-dx) p3=p2.offset(vup,-dy) b=tge.add_face(p0,p1,p2,p3) b.reverse! unless b.normal.samedirection? @norm b.pushpull 2 (d=@cor.distance(gp);(cpt=gp;md=d) if md>d) if @spt=="Corner" p0 += xv; gp += xv end pt.offset!(yv); gp=pt p0=pt.offset(Geom::Vector3d.new(0,0,-1)) end (tr = Geom::Transformation.new(cpt.vector_to(@cor)); tge.transform_entities(tr,tge.to_a )) if @spt=="Corner" @egrp=@ent.add_group;@ege=@egrp.entities;@egt=@egrp.transformation tge.intersect_with true,tgt,@ege,@egt,false,fg fg.explode; tg.erase! end # ######################################################## # Common sub-routines ######################################################## # def paint_it(f) if @app=="Current" f.material = @mod.materials.current f.material.texture.size = @txs if f.material && f.material.texture && @txs self.ate(f) if f.material && f.material.texture && (@@opt=="Tweed" || @@opt=="Hbone") elsif @app=="Rand_Clr" name = @colors[rand(@colors.length)] unless @mod.materials[name] mat = @mod.materials.add(name) mat.color = name end f.material = @mod.materials[name] elsif @app=="Rand_Tex" i=rand(@textures.length);name = @textures[i] unless @mod.materials[name] mat = @mod.materials.add(name) mat.texture = @images[i] mat.texture.size = @txs if @txs end f.material = @mod.materials[name] self.ate(f) if f.material && f.material.texture && (@@opt=="Tweed" || @@opt=="Hbone") end end def ate(f) #Align Texture to Edge return unless f.material # Skip if no material or no texture return unless f.material.texture l=n=0; f.outer_loop.edges.each_with_index{|e,i| d=e.length;(n=i;l=d) if d>l} vector = f.edges[n].line[1] # Align to longest bounding edge return unless f.normal.perpendicular? vector # Skip if vector isn't in the plane of the face achorPoint = f.edges[n].line[0] # Define point to rotate around textureWidth = f.material.texture.width vector.length = textureWidth # Change vector's length to materials width points = [achorPoint, [0,0,0], [achorPoint[0]+vector[0],achorPoint[1]+vector[1],achorPoint[2]+vector[2]], [1,0,0]] # Reposition material f.position_material(f.material, points, true) end def wig(f)#Random Texture Translation if f.material && f.material.texture tw = Sketchup.create_texture_writer # Create uv helper to get current material position uvh = f.get_UVHelper true, false, tw; pointPairs = []; #puts "rtt" vector = f.edges[0].line[1] # Get a vector in the face's plane vector.length = rand(f.material.texture.height+f.material.texture.width) # Set random length (between 0 and materials height + width) trans = Geom::Transformation.rotation f.outer_loop.vertices[0].position, f.normal, rand(360).degrees # Rotate vector randomly in plane vector.transform! trans; trans = Geom::Transformation.translation vector # Create translation, move point by vector (0..1).each do |j| # Loop some points around face point3d = f.outer_loop.vertices[j].position # Select a 3d point point3dRotated = point3d.transform(trans) # Move 3d point pointPairs << point3dRotated # Save model's 3d point to array point2d = uvh.get_front_UVQ(point3d) pointPairs << point2d # Save material's corresponding 2d point to array end#each f.position_material(f.material, pointPairs, true) #Set material position (pair up model 3d points with texture 2d point) end end def wag(f)#Random Texture Rotation if f.material && f.material.texture tw = Sketchup.create_texture_writer # Create uv helper to get current material position uvh = f.get_UVHelper true, false, tw; angle=90*rand(4) if @rtr=="90"; angle=180*rand(2) if @rtr=="180"; angle=rand(360) if @rtr=="Rand" trans = Geom::Transformation.rotation f.outer_loop.vertices[0].position, f.normal, angle.degrees #Define rotation pointPairs = []; #puts "rtr angle=#{angle}" (0..1).each do |j| # Loop some points around face point3d = f.outer_loop.vertices[j].position # Selet a 3d point point3dRotated = point3d.transform(trans) # Rotate 3d pont pointPairs << point3dRotated # Save model's 3d point to array point2d = uvh.get_front_UVQ(point3d) pointPairs << point2d # Save material's corresponding 2d point to array end#each f.position_material(f.material, pointPairs, true) #Set material position (pair up model 3d points with texture 2d point) end end def wob(f,d)#Random Face Rotation i = rand(2); norm = f.normal.axes[i]; unless @@opt=="Tweed" || @@opt=="Hbone" angle=Math.atan(rand*d/[@BTW_y/2,@BTW_x/2][i])*(rand<=>0.5) else angle=Math.atan(rand*d/(f.bounds.width/2))*(rand<=>0.5) end tr=Geom::Transformation.rotation(f.bounds.center,norm,angle) @fge.transform_entities(tr,f) end def bev(f,d)#Bevel Face Edge b=self.g_offset(f,d) if b v=b.normal;v.length=d tr=Geom::Transformation.translation(v) @fge.transform_entities(tr,b) end end def edge_to_close(f,p) return true if p[0].distance(p[1]) <= @GW for loop in f.loops loop.edges.each{|e| if e.line[1].parallel?(p[0].vector_to(p[1])) for i in 0..1 pp=p[i].project_to_line(e.line) if e.bounds.contains?(pp) if p[i].distance(pp) <= @GW return true end end end end } end return false end def g_offset(face,dist) return nil unless (dist.class==Fixnum || dist.class==Float || dist.class==Length) return nil unless self.ctr_to_edge(face); @c_pts=face.outer_loop.vertices.collect{|v|v.position}; unless dist==0 edges=face.outer_loop.edges;last=edges.length-1 0.upto(last) do |i| unless edges[i].length > @GW if edges[i].line[1].perpendicular?(edges[i-1].line[1]) if edges[i].line[1].perpendicular?(edges[i-last].line[1]) @c_pts -= [edges[i].start,edges[i].end]; #remove short edge between square corners end end end end ang=Math.acos(face.normal.z); ctr=face.bounds.center tr=Geom::Transformation.rotation(ctr,face.normal.axes[0],-ang) @c_pts.each{|cp| cp.transform! tr}; # put points on the XY plane @a_dfl=[]; @e_vec=[]; @e_azm=[]; @d_azm=[]; @d_vec=[]; last=@c_pts.length-1; 0.upto(last) {|i| @e_vec[i]=@c_pts[i].vector_to(@c_pts[i-last]);@e_azm[i]=self.vector_to_azimuth(@e_vec[i])} 0.upto(last) {|i| @a_dfl[i]=self.deflection(@e_azm[i-1],@e_azm[i])/2; @d_azm<<((@e_azm[i]+90.degrees-@a_dfl[i])%360.degrees); @d_vec< 2) ? (@fge.add_face(@o_pts)) : (return nil) else @fge.add_face(@c_pts) end end def vector_to_azimuth(v) # -------------------------------------------------------------------------------- # Compute Azimuth based on positive x-axis as 0 degrees # -------------------------------------------------------------------------------- v.normalize! va=Math::acos(v.z) # Vertical angle if v.x < 0 az = Math::PI - Math::asin(v.y) # Quadrant III or IV elsif v.y < 0 az = Math::PI * 2 + Math::asin(v.y) # Quadrant II else az = Math::asin(v.y) # Quadrant I end return az # Horizontal angle end def azimuth_to_vector(a) v=Geom::Vector3d.new(Math.cos(a),Math.sin(a),0) end def deflection(az1,az2) defl = az2-az1 defl += Math::PI * 2 if defl < -Math::PI defl -= Math::PI * 2 if defl > Math::PI return defl end def progress_bar(cnt,max,opt) pct = (cnt*100)/max; pct=[pct,100].min; @pb = "|"*pct Sketchup::set_status_text(@pb + " #{pct}% of #{opt} done.") end def ctr_to_edge(f) edges=f.edges; c=self.calc_centroid(f); edges.each{|e| return false if c.distance_to_line(e.line) <= @HW} return true end def calc_centroid(f) tx=0.0;ty=0.0;tz=0.0; p=f.outer_loop.vertices.collect{|v|v.position} p.each{|v| tx+=v.x;ty+=v.y;tz+=v.z} ax=tx/p.length;ay=ty/p.length;az=tz/p.length c=Geom::Point3d.new(ax,ay,az);#ent.add_cpoint(c) area = 0.0;cx = 0.0;cy = 0.0;cz = 0.0; for i in 0...p.length areat = (p[i].distance(p[i-1])*(c.distance_to_line([p[i],p[i].vector_to(p[i-1])])))/2.0 area = area + areat; cx = cx + areat * ( p[i].x + p[i-1].x + c.x ) / 3.0; cy = cy + areat * ( p[i].y + p[i-1].y + c.y ) / 3.0; cz = cz + areat * ( p[i].z + p[i-1].z + c.z ) / 3.0; end cx = cx / area;cy = cy / area;cz = cz / area; Geom::Point3d.new(cx,cy,cz) end end end