#!/mnt/card/ruby/bin/ruby # Copyright(c) 2006, John Simpson, SwaJime's Cove(sm), swajime.com # 1. Place this file in your Sketchup ruby plugins directory. # 2. Enable the extension "SwaJime's Toolkit" in Sketchup's Preferences (Window|Preferences|Extensions) # 3. Menu items for this toolkit should now be available in Sketchup's Plugin menu under "SwaJime's Toolkit" # # Suggestions/Criticisms welcome. status = nil if ($0 != 'SketchUp') dirName = File.dirname(__FILE__) fakeSketchup = File.join(dirName, 'fakeSketchup.rb') load fakeSketchup else require 'sketchup.rb' require 'extensions.rb' require 'LangHandler.rb' end include Math # include Sketchup # cannot use this line: it causes reloads of this file to fail $swaStrings = LanguageHandler.new("SwaJime") if $swaInfo.nil? then $swaInfo = {"about"=>SketchupExtension.new($swaStrings.GetString("SwaJime's Toolkit"), __FILE__)}; end # $swaInfo["about"].name = $swaStrings.GetString("SwaJime's Toolkit") $swaInfo["about"].creator = $swaStrings.GetString("John Simpson") $swaInfo["about"].copyright = $swaStrings.GetString("#{[0xA9].pack('U*')}2006, SwaJime's Cove#{[0x2120].pack('U*')}") $swaInfo["about"].version = "0.9 - 12/15/2006" $swaInfo["about"].description = $swaStrings.GetString( "Adds SwaJime's Tools to the plugins menu.\n" + "Includes Longitude/Latitude Tools from SwaJime's Cove.\n" + "http://www.swajime.com/\n\n" + "Some portions of this code are copyright#{[0xA9].pack('U*')}@Last Software.\n" + "Some portions of this code are copyright#{[0xA9].pack('U*')}Jim Foltz.\n") $swaInfo["help"] = $swaStrings.GetString( "LLAtude is short for (L)atitude, (L)ongitude, and (A)lti(tude). Yes, I made it up.\n\n" + "Set Range\n\tDefines the boundaries for sealevel and LLAtude grid lines.\n\n" + "Set Model Location\n\tAllows you to change the location of the model.\n\n" + "Show Sealevel\n\tDraws a \"sea\" at sealevel(limited by \"Set Range\"). This is probably out of sight under your model.\n\n" + "Use Tracking Tool\n\tThe LLAtude of the mouse position is displayed in the status bar. Left-click the mouse to place a construction point and label a specific point. Double-left-click the mouse to add longitude/latitude grid lines(limited by \"Set Range\") at a specific point.\n\n" + "Show As:\n\tSet display style of coordinates as sexagesimal or decimal.\n\n" + "About\n\tDisplays credits, authorship, etc.\n\n" + "Help\n" + "\tThis screen.\n" + "\n") if caller[0].nil? then calledFrom = $0 else calledFrom = File.basename(File.basename(caller[0]).split(':')[0],'.rb'); end $swaInfo["debug"] = [calledFrom] if calledFrom == "SketchUp" or calledFrom == "loadswa" status = Sketchup.register_extension($swaInfo["about"], false) else result = Sketchup.send_action "showRubyPanel:" puts "ruby panel" $swaInfo["debug"] << "Loading Extension" module Swa # Copyright(c) 2006, John Simpson, SwaJime's Cove(sm), swajime.com def Swa.showAbout() #UI.messagebox("\nVersion #{$swaInfo["about"]}\nCopyright(c) 2006, SwaJime's Cove\342\204\240, swajime.com", MB_MULTILINE, "About:") UI.messagebox("#{$swaInfo['about'].name}\n" + "Version: #{$swaInfo['about'].version}\n" + "Creator: #{$swaInfo['about'].creator}\n" + "Copyright: #{$swaInfo['about'].copyright}\n" + "\n#{$swaInfo['about'].description}", MB_MULTILINE, "About #{$swaInfo['about'].name}:") end class LLAtude # Copyright(c) 2006, John Simpson, SwaJime's Cove(sm), swajime.com @@A = 6378137.m(); # def Swa.A; A; end # meters, Equatorial radius (semi-major axis) @@IF = 298.257223563; # def Swa.IF; IF; end # Inverse Flattening @@F = 1.0/@@IF; # def Swa.F; F; end # Flattening @@B = @@A - @@A*@@F; # def Swa.B; B; end # Polar radius (semi-minor axis) @@EE = 2.0*@@F-@@F**2; # def Swa.EE; EE; end # eccentricity(squared) @@EE1 = (@@A**2-@@B**2)/(@@B**2); # def Swa.EE1; EE1; end # eccentricity prime (squared) @@PolarAxis = [Geom::Point3d.new(0,0,0), Geom::Vector3d.new(0,0,1)] @@showAs = "d°m's\"" # "d°m's\"" | "[d,m,s]" | "d" | "r" @@toGeo = nil @@fromGeo = nil @@range = nil # [33.706, 33.709, -117.632, -117.629,10] # nil def initialize (where) # where = [lat,lon,h] or Geom::Point3d or unsupported if where.kind_of?(Geom::Point3d) pt = where.transform(LLAtude.toGeo) @given = "Sketchup Point" @x = pt.x.to_inch @y = pt.y.to_inch @z = pt.z.to_inch elsif where.kind_of?(Array) lla = where @given = "LLAtude" @latitude = lla[0] @longitude = lla[1] @height = lla[2].inch else puts("LLAtude.new(argument) with argument type of #{where.class} is not supported.") end end def LLAtude.toDMS(angle) # Copyright(c) 2006, John Simpson, SwaJime's Cove(sm), swajime.com a = (angle*360000).round()/360000.0 d = a.truncate m = (60*a.abs%60).truncate s = ((3600*a.abs%60)*100).round/100.0 [d,m,s] end def to_s # Copyright(c) 2006, John Simpson, SwaJime's Cove(sm), swajime.com if @@showAs == "d°m's\"" dms = Swa::LLAtude.toDMS(latitude.radians.abs) display = "#{dms[0]}° #{dms[1]}\' #{dms[2]}\"" display << (latitude < 0 ? ' S, ' : ' N, ') dms = Swa::LLAtude.toDMS(longitude.radians.abs) display << "#{dms[0]}° #{dms[1]}\' #{dms[2]}\"" display << (longitude < 0 ? ' W' : ' E') display << ", #{height.to_s}" elsif @@showAs == "[d,m,s]" dmsLat = Swa::LLAtude.toDMS(latitude.radians) dmsLong = Swa::LLAtude.toDMS(longitude.radians) display = "#{dmsLat.inspect}, #{dmsLong.inspect}" display << ", #{height.to_s}" elsif @@showAs == "d" display = "#{latitude.radians}, #{longitude.radians}" display << ", #{height.to_s}" elsif @@showAs == "r" display = "#{latitude}, #{longitude}" display << ", #{height.to_s}" else display = "@@showAs #{@@showAs} is not supported" end end def to_a; [latitude, longitude, height]; end def pvcr; @pvcr.nil? ? @pvcr=@@A/sqrt(1-@@EE*(sin(latitude))**2) : @pvcr; end # prime vertical curvature radius (i.e. radius of Normal @ latitude) def mcr; @mcr.nil? ? @mcr=@@A*(1.0-@@EE)/((1.0-@@EE*(sin(latitude))**2)**1.5) : @mcr; end # meridional curvature radius (not used atm) def p; @p.nil? ? p=sqrt(x**2+y**2) : @p; end def t; @t.nil? ? t=atan2(z*@@A,p*@@B) : @t; end def latitude #; @lat.nil? ? @lat=atan2(z+@@EE1*@@B*(sin(t))**3,p-@@EE*@@A*(cos(t))**3) : @lat; end return @latitude if @given == "LLAtude" return atan2(z+@@EE1*@@B*(sin(t))**3,p-@@EE*@@A*(cos(t))**3) if @given == "Sketchup Point" puts "LLAtude.latitude() not available for #{@given}" nil end def longitude #; @long.nil? ? @long=atan2(y,x) : @long; end return @longitude if @given == "LLAtude" return atan2(y,x) if @given == "Sketchup Point" puts "LLAtude.longitude() not available for #{@given}" nil end def height #; @h.nil? ? @h=Sketchup.format_length(p/cos(latitude)-pvcr) : @h; end return @height if @given == "LLAtude" #return Sketchup.format_length(p/cos(latitude)-pvcr) if @given == "Sketchup Point" return (p/cos(latitude)-pvcr).inch if @given == "Sketchup Point" puts "LLAtude.height() not available for #{@given}" nil end def altitude; height(); end def x #; @x.nil? ? @x=(pvcr+height)*cos(latitude)*cos(longitude) : @x; end return @x if @given == "Sketchup Point" return (pvcr+@height)*cos(@latitude)*cos(@longitude) if @given == "LLAtude" puts "LLAtude.x() not available for #{@given}" nil end def y #; @y.nil? ? @y=(pvcr+height)*cos(latitude)*sin(longitude) : @y; end return @y if @given == "Sketchup Point" return (pvcr+@height)*cos(@latitude)*sin(@longitude) if @given == "LLAtude" puts "LLAtude.y() not available for #{@given}" nil end def z #; @z.nil? ? @z=(pvcr*(1-@@EE)+height)*sin(latitude) : @z; end return @z if @given == "Sketchup Point" return (pvcr*(1-@@EE)+@height)*sin(@latitude) if @given == "LLAtude" puts "LLAtude.z() not available for #{@given}" nil end def location; @loc.nil? ? @loc=Geom::Point3d.new([x,y,z]) : @loc; end def position; @pos.nil? ? @pos=location.transform(LLAtude.fromGeo) : @pos; end #def elevation # mifFile = File.new("", "r") # midFile = File.new("", "r") # mifFile.close # midFile.close #end def LLAtude.setModelLocation() # latlong.rb coppyright(C) 2006 jim.foltz@gmail.com # john@swajime.com added elevation, compressed, & added getOrientation si = Sketchup.active_model.shadow_info prompts = ["Country:","Location:","Latitude:","Longitude:","Elevation:","North Angle:","Show North Angle?"] choices = ["", "", "", "", "", "", "true|false"] defaults =[si["Country"],si["City"],si["Latitude"],si["Longitude"],si["Elevation"],si["NorthAngle"],si["DisplayNorth"]] ret = UI.inputbox( prompts, defaults, choices, "Custom Location" ) return unless ret # Method exits here if user hits Cancel button # # User hits OK, so set values si["Country"] = ret[0]; si["City"] = ret[1] si["Latitude"] = ret[2]; si["Longitude"] = ret[3]; si["Elevation"] = ret[4] si["NorthAngle"] = ret[5]; si["DisplayNorth"] = eval(ret[6]) @@toGeo = getOrientation! @@fromGeo = @@toGeo.inverse end def LLAtude.getOrientation!() si = Sketchup.active_model.shadow_info #si = Sketchup.active_model.shadow_info #if si["Elevation"].nil? then si["Elevation"] = 0.feet; end #if (si["Elevation"].zero?) then v = 1 else v = si["Elevation"]; end v=1325.feet @@origin = LLAtude.new([si["Latitude"].degrees, si["Longitude"].degrees, v]).location @@sl = (LLAtude.new([si["Latitude"].degrees, si["Longitude"].degrees, 0])).location # sealevel @@zaxis = Geom::Vector3d.new(@@origin.x-@@sl.x, @@origin.y-@@sl.y, @@origin.z-@@sl.z) @@pole = Geom.intersect_line_plane(@@PolarAxis, [@@origin, @@zaxis]) y_axis = Geom::Vector3d.new(@@pole.x-@@origin.x, @@pole.y-@@origin.y, @@pole.z-@@origin.z) northAngle = Geom::Transformation.rotation(@@origin, @@zaxis, si["NorthAngle"].degrees) @@yaxis = y_axis.transform!(northAngle) @@xaxis = @@yaxis * @@zaxis #@@xaxis.length = @@yaxis.length = @@zaxis.length = 1 Geom::Transformation.new @@xaxis, @@yaxis, @@zaxis, @@origin end def LLAtude.toGeo; @@toGeo.nil? ? @@toGeo = LLAtude.getOrientation! : @@toGeo; end def LLAtude.fromGeo; @@fromGeo.nil? ? @@fromGeo = toGeo.inverse : @@fromGeo; end def LLAtude.showAs() ret = UI.inputbox(["Show Coordinates As"], [@@showAs], ["d°m's\"|[d,m,s]|d|r"], "") return unless ret @@showAs = ret[0] end def LLAtude.resetRange() pt0 = Swa::LLAtude.new(ORIGIN) pta = Swa::LLAtude.new([((pt0.latitude.radians*60).floor/60.0).degrees,((pt0.longitude.radians*60).floor/60.0).degrees,0]) ptb = Swa::LLAtude.new([(pta.latitude.radians+1.0/60.0).degrees,(pta.longitude.radians+1.0/60.0).degrees,0]) @@range = [pta.latitude.radians,ptb.latitude.radians,pta.longitude.radians,ptb.longitude.radians,12] end def LLAtude.range() return @@range unless @@range.nil? LLAtude.resetRange() end def LLAtude.setRange ret = UI.inputbox(["Minimum Latitude","Maximum Latitude","Minimum Longitude","Maximum Longitude","Segments"], range(),"Set LLA range") return unless ret @@range = ret end end # class LLAtude # LLAtude.getOrientation!() class LLAtudeTool # Copyright(c) 2006, John Simpson, SwaJime's Cove(sm), swajime.com def activate @ip = Sketchup::InputPoint.new() @iptemp = Sketchup::InputPoint.new() @displayed = false end def onMouseMove(flags, x, y, view) # Copyright 2005, @Last Software, Inc. w/ minor modifications by John # show the screen position in the VCB Sketchup::set_status_text("#{x}, #{y}", SB_VCB_VALUE) # get a position in the model and show it in a tooltip @iptemp.pick view, x, y if( @iptemp.valid? ) changed = @iptemp != @ip @ip.copy! @iptemp pos = @ip.position; # get the text for the position msg = @ip.tooltip if( msg.length > 0 ) msg << " " end msg << pos.to_s # See if it is on any special geometry if( @ip.vertex == nil ) if( @ip.edge ) if( @ip.depth > 0 ) length = @ip.edge.length(@ip.transformation) else length = @ip.edge.length end msg << "\n " + $swaStrings.GetString("length") + " = #{length}" elsif( @ip.face ) if( @ip.depth > 0 ) area = @ip.face.area(@ip.transformation) else area = @ip.face.area end msg << "\n " + $swaStrings.GetString("area") + " = #{Sketchup.format_area(area)}" end end # show LLA coordinates lla = Swa::LLAtude.new(@ip.position) Sketchup.set_status_text(lla.to_s, SB_PROMPT) # set the tooltip to show this message view.tooltip = msg # see if we need to update the display for this point if( changed and (@ip.display? or @displayed) ) # This tells the view that we want it to update itself view.invalidate end end end def onLButtonDown(flags, x, y, view) puts("#{flags}, #{x}, #{y}, #{view}") @ip.pick view, x, y if( @ip.valid? ) lla = Swa::LLAtude.new(@ip.position) Swa.addLLA_cpoint(lla) Swa.addLLA_label(lla) end end def onLButtonDoubleClick(flags, x, y, view) puts("onLButtonDoubleClick") @ip.pick view, x, y if( @ip.valid? ) lla = Swa::LLAtude.new(@ip.position) Swa.addLLA_cpoint(lla) Swa.addLLAtudeLines(lla) end end def draw(view) if( @ip.valid? && @ip.display? ) @ip.draw view @displayed = true else @displayed = false end end end # class LLAtudeTool def Swa.addLLA_label(lla); Sketchup.active_model.active_entities.add_text(lla.to_s, lla.position); end def Swa.addLLA_cpoint(lla); Sketchup.active_model.active_entities.add_cpoint(lla.position); end def Swa.useLLAtudeTool(); Sketchup.active_model.select_tool LLAtudeTool.new; end def Swa.addLLAtudeLines(lla) # $swaInfo["lla"]=lla m = Sketchup.active_model m.start_operation($swaStrings.GetString("Show LLAtude Lines")) oldLayer = m.active_layer newLayer = m.layers.add('LLA data') newLayer.visible = true m.active_layer = newLayer group = m.active_entities.add_group e = group.entities t=Swa::LLAtude.fromGeo(); r=Swa::LLAtude.range ry_step = (r[1]-r[0])/r[4]; rx_step = (r[3]-r[2])/r[4] # r = lat_low,lat_high,long_low,long_high,segments pts=[]; i=0; r[0].degrees.step(r[1].degrees,ry_step.degrees){ |lat| pt=Swa::LLAtude.new([lat,lla.longitude,lla.altitude]); pts[i]=pt.position; i=i+1 } c1=e.add_curve(pts); ptq=[]; i=0; r[2].degrees.step(r[3].degrees,rx_step.degrees){|long|; pt=Swa::LLAtude.new([lla.latitude,long,lla.altitude]); ptq[i]=pt.position; i=i+1 } c2=e.add_curve(ptq); m.active_layer = oldLayer m.commit_operation group end def Swa.showSeaLevel # Copyright(c) 2006, John Simpson, SwaJime's Cove(sm), swajime.com m = Sketchup.active_model m.start_operation($swaStrings.GetString("Show Sea Level")) oldLayer = m.active_layer newLayer = m.layers.add('LLA data') newLayer.visible = true m.active_layer = newLayer group = m.active_entities.add_group e = group.entities t=Swa::LLAtude.fromGeo(); r=Swa::LLAtude.range; ry_step = (r[1]-r[0])/r[4]; rx_step = (r[3]-r[2])/r[4] pts=[]; i=0; r[0].degrees.step(r[1].degrees,ry_step.degrees){ |lat| pt=Swa::LLAtude.new([lat,r[2].degrees,0]); pts[i]=pt.position; i=i+1 }; pts[i]=pts[0]; c1=e.add_curve(pts); f=e.add_face(c1) ptq=[]; i=0; r[2].degrees.step(r[3].degrees,rx_step.degrees){|long|; pt=Swa::LLAtude.new([0.degrees,long,10.mile]); ptq[i]=pt.position; i=i+1 }; c2=e.add_curve(ptq); m.active_layer = oldLayer m.commit_operation group end def Swa.drawHyperbola(a = 20, c = 30) # Copyright(c) 2006, John Simpson, SwaJime's Cove(sm), swajime.com # http://www.mathwords.com/h/hyperbola.htm # http://mathworld.wolfram.com/Hyperbola.html model = active_model model.start_operation $swaStrings.GetString("Draw Hyperbola") entities = model.active_entities group = entities.add_group entities = group.entities b = sqrt(c**2 - a**2) pts = [] (0..15).each { |i| t = 0.1 * i pts[i] = [a/cos(t), b*tan(t), 0] } pts[16] = [c,0,0] face = entities.add_face([pts]) edges = entities.add_circle([-c,0,0], Geom::Vector3d.new(1, 0, 0), 1,24) status = face.followme(edges) # erase the outer shell l = entities.add_line([-c,0,0], [c,0,0]) v = l.vertices v[1].faces.each { |j| j.erase! } l.erase! edges.each { |e| e.erase! } model.commit_operation group end def Swa.showCities() # elevation 2402' # 32°13' 1.2", -110°58'1.2", 2402' #t ucson=Swa::LLAtude.new([32.217.degrees,-110.967.degrees,2402.feet]) # Sketchup.active_model.active_entities.add_cpoint(tucson.position) # Sketchup.active_model.active_entities.add_text("Tucson (AZ)", tucson.position) ## 33°42'27", -117°37'49.8", 1325' # modjeska=Swa::LLAtude.new([33.707500.degrees,-117.630500.degrees,1325.feet]) # Sketchup.active_model.active_entities.add_cpoint(modjeska.position) # Sketchup.active_model.active_entities.add_text("Modjeska (CA)", modjeska.position) # 40°1'1.2", 105°16'58.8"/40.0170N,105.2830W boulder=Swa::LLAtude.new([40.017.degrees,-105.283.degrees,0.feet]) Sketchup.active_model.active_entities.add_cpoint(boulder.position) Sketchup.active_model.active_entities.add_text("Boulder (CO)", boulder.position) end def Swa.getMIFfileList() $= = true # deprecated, but what is the new method? searchPath = $:|[File.dirname(__FILE__)]; fileList = [] searchPath.each {|directoryName| fileList = fileList|Dir[File.join(directoryName, "**", "*.mif")]} fileList end def Swa.loadMIF() llaList = []; i = 0 fileList.each {|mifFileName| midFileName = File.join(File.dirname(mifFileName), File.basename(mifFileName, ".mif") + ".mid") mifFile = File.open(mifFileName, "r") $swaInfo["mifPath"] << mifFile.path unless mifFile.nil? # process MIF File mifHeader = {}; midColumns = {} until mifFile.eof? do # read MIF File Header rowFields = mifFile.gets.scan(/\(.*\)|\S+/) mifHeader[rowFields[0]] = rowFields[1..rowFields.length] # puts("#{rowFields[0]} = #{mifHeader[rowFields[0]].inspect}") case rowFields[0] when "columns" rowFields[1].to_i.times{ # name, type, & width colInfo = mifFile.gets.scan(/\(.*\)|\S+/) midColumns[colInfo[0]] = colInfo[1..colInfo.length] } when "data" midData = []; obj = "" if File.exist? midFileName then midData=IO.readlines(midFileName); end dataLine = mifFile.gets() until /^\w.*/ === dataLine obj = dataLine until mifFile.eof? # read MIF Data Section dataLine = mifFile.gets() until /^\w.*/ === dataLine or dataLine.nil? obj << dataLine unless dataLine.nil? dataLine = mifFile.gets() objData = obj.split("\n") #puts("i:#{i}; obj:>#{objData.inspect}< : #{midData[i]}") primitive = objData[0].split(" ")[0] case objData[0] when /^point/ rv = objData[0].split(" ")[1..2]<> ["keys", "[]", "[]=", "each_pair", "sun_direction", "each_key", "old_keys", "each"] # #def insEl; Sketchup.active_model.shadow_info.entries; end # #end status