sketchucation logo sketchucation
    • Login
    1. Home
    2. ben.doherty
    3. Posts
    ℹ️ Licensed Extensions | FredoBatch, ElevationProfile, FredoSketch, LayOps, MatSim and Pic2Shape will require license from Sept 1st More Info
    B
    Offline
    • Profile
    • Following 0
    • Followers 0
    • Topics 5
    • Posts 17
    • Groups 1

    Posts

    Recent Best Controversial
    • UDP interface

      Does sketchup have a UDP interface? We were thinking of having a play with some wiimote/kinect interaction stuff --> http://ubimash.com

      posted in Developers' Forum
      B
      ben.doherty
    • RE: Some critique

      Thanks chaps. In some odd way I knew that, well, I owned all the bits of information, I just hadn't assembled them yet. I've updated it so that lesson 1 reads better (i.e. no variables)
      I'll go through and check the others now.

      posted in Developers' Forum
      B
      ben.doherty
    • Some critique

      I made some Ruby tutorials for sketchup to use in our office. They are a but rough around the edges as they've not really stretched their legs yet. I'd be really interested in getting some feedback on them. If anyone fancies having a look at them then I'd be really grateful!

      Link Preview Image
      BVN ruby lessons

      learning think in Ruby

      favicon

      BVN ruby lessons (bvnruby.wordpress.com)

      posted in Developers' Forum
      B
      ben.doherty
    • RE: Texture from url?

      While the JS debate rages, I've been playing with what can be done with ruby (being single-minded/stubborn/lazy)

      I've updated my !loadpaths file to match dan's latest http://forums.sketchucation.com/viewtopic.php?f=180&t=29412&hilit=load+path#p257058
      and when I try a couple of things I get this response:
      require 'net/http' Error: #<LoadError: C:/ruby186/lib/ruby/1.8/net/protocol.rb:21:inrequire': No such file to load -- socket>
      C:/ruby186/lib/ruby/1.8/net/protocol.rb:21
      require 'date/format'
      true`

      Any ideas on why net/http fails, but date/format works?

      I'm sure it will take some clever file management, and refresh cycle control etc, but it'd be cool to be able to specify a url to get a texture from. That'd open up a load of opportunities for data vis etc.

      posted in Developers' Forum
      B
      ben.doherty
    • RE: Texture from url?

      That would probably work, but wouldn't win any prizes for elegance.
      Given that ruby can access things on the internet as part of it's core library does anyone have any idea how to point it at those standard libraries?

      posted in Developers' Forum
      B
      ben.doherty
    • RE: Texture from url?

      Hi, I'm trying to do something similar.
      I'm not too fussed about using non sketchup ruby libraries, so I just want to do it in the most elegant way possible.

      I tried adding:
      $LOAD_PATH << "C:/Ruby186/lib/ruby/1.8/net"
      $LOAD_PATH << "C:/Ruby186/lib/ruby/1.8/i386-mingw32"
      to my load paths as a way to crunch through the error messages that I got (couldn't find socket etc.)

      Is there an easy way to do this that is nice and easy and clean?

      posted in Developers' Forum
      B
      ben.doherty
    • RE: InHalfspace?

      I'm not interested in finding out if the point is on the plane, simply if it is in the +ve half or the universe bisected by the plane.
      It's a pretty classic test that can be stacked up to do selection sets. i.e. to test if a point is in a cube you test to see if it is in the -ve halfspace of the planes of each of the faces. A fail on any test shows that it isn't in that halfspace, and therefore you can bail out.
      I've got to put some effort into packaging this up so that it takes a plane and a point (or a collection thereof)
      I think that there is probably something to be gained by pulling out the proper equation of the plane rather than doing the offset, but I'm not all that hot on maths. (not sure why they start you at university afterthe legal drinking age)

      posted in Developers' Forum
      B
      ben.doherty
    • InHalfspace?

      I couldn't see a method to find out if a point is in a halfspace defined by a plane or not, so I made this toy one.

      
      #setup gubbins
      mod = Sketchup.active_model
      ent = mod.entities
      sel = mod.selection
      
      #draw a face and select it, this checks that you have
      if sel[0].is_a? Sketchup;;Face
      #if you have, throw 100 points into the air
        for i in (0..100)
          ent.add_cpoint [rand*100,rand*100,rand*100]
        end
      #get the normal of the face
        normal = sel[0].normal
      #get the centroid of the face too
        offset = Geom;;Vector3d.new(sel[0].bounds.center.to_a)
      #make an empty collection to put your points into if they ARE in the halfspace
        pointsInPositiveHalfspace = []
      #loop through all the things in the model,
        ent.each{|e|
      # if they are points...
          if e.is_a? Sketchup;;ConstructionPoint
      #move them so that they are relative to the centroid of the face, not to the origin.
      #this one is the big mental shift
            shiftedPt = Geom;;Vector3d.new(e.position.to_a) - offset 
      #The dot product can be used as a half space test. A half space test can be used to 
      #determine if an object, lets say a zombie, is in front of the player (or camera) or 
      #behind it. If my player's forward vector is P and the vector from the player to the 
      #zombie (zombie's position - player's position) is Z, then dotting P and Z would give
      #me the angle between the two vectors. If the angle is positive our player is facing
      #the zombie, otherwise the zombie is behind us (never turn your back on a zombie!!!). 
            if (normal.dot shiftedPt) > 0
      #put that point into the colleciton      
              pointsInPositiveHalfspace << e
            end
          end
        }
      #for all the points in the collection...  
        for i in (1..pointsInPositiveHalfspace.length)
          if pointsInPositiveHalfspace[i].is_a? Sketchup;;ConstructionPoint
      #Draw a line from it's predecesor to it
            ent.add_line pointsInPositiveHalfspace[i-1].position, pointsInPositiveHalfspace[i].position
          end
        end
      end
      
      

      halfspace.JPG
      There's a lot to do to it before it is actually useful, but it may help me with my quest if there isn't a better way.

      posted in Developers' Forum
      B
      ben.doherty
    • RE: Taking pictures with cameras

      The other solution I suppose would be to select everything in the halfspace behind the camera and set it's material to a transparent png as suggested here http://groups.google.com/group/SketchUp3d/browse_thread/thread/2ce1ef1f54e7b6dc?pli=1 .

      That really is going to slow things down if I have to do 2 material sets for each entity.

      posted in Developers' Forum
      B
      ben.doherty
    • RE: Taking pictures with cameras

      Oh the arrogance. Nearly finished indeed!
      I tested this on a real model rather than my toy model and it had a complete fit.

      http://farm5.static.flickr.com/4120/4808393958_53df1efc85_d.jpg

      it seems that even though you've set the eye point, SketchUp takes pictures from the edge of the universe.
      This means that the images taken from the green positions are fine, but the ones taken from the red directions are actually taken from the outside of the building.

      I reproduced this to prove that I'm not mad.

      http://farm5.static.flickr.com/4117/4808393762_8dd31e280d_d.jpg

      The spikes are the view cones of 120 degree camera and a 60 degree camera. the circle is a cylinder behind the camera.

      http://farm5.static.flickr.com/4123/4808393916_b25c6cf78f_d.jpg

      http://farm5.static.flickr.com/4079/4807773187_fb134f2865_d.jpg

      If you take a picture from this position the cylinder is clearly visible in the image. Grrrr 😡

      http://farm5.static.flickr.com/4139/4808393690_0195d98f69_d.jpg

      If you use the walk around tools in the UI then you can go in front of the cylinder, but I couldn't find anything to do with this in the API.
      The thing that seemed to be the most likely to help was the section plane, but this seems to affect the shadows which isn't a lot of help.

      I think it might be time to start looking very seriously at OpenStudio, but it's a shame to have come this far to be thwarted at the end!
      Any ideas?


      V1.zip

      posted in Developers' Forum
      B
      ben.doherty
    • RE: Taking pictures with cameras

      I've got this to the point with this that I'm happy to call it 'working'. Thanks for all the advice. I've tried to take as much of it as I can on board.
      I still can't get my head around ImageMagick, but that's a whole different kettle of fish.

      I think that looking seriously into OpenStudio is a good idea. I'd looked at energy plus already and given up because of overwhelming complexity, but if there is a friend;y front end now that'd probably be a much better solution.

      I've learnt a hell of a lot through doing this though!

      If anyone has any more comments then I'd love to hear them.

      
      module BVNtools
        module Voyeur
          
          #require 'quick_magick.rb'
          
          if( not file_loaded?('makeTheFaces.rb') )
            # This will add a separator to the menu, but only once
            add_separator_to_menu('Voyeur')
            
            plugins_menu = UI.menu('Plugins')
            voyeur_menu = plugins_menu.add_submenu('Voyeur')
            voyeur_menu.add_item('make analysis faces') { makeAnalysisFaces }
            voyeur_menu.add_item('Window Looker') { start }
            
            toolbar = UI;;Toolbar.new 'Voyeur'
            
            cmdFM = UI;;Command.new('Face Maker'){ makeAnalysisFaces }
            cmdFM.small_icon = 'FM24.png'
            cmdFM.large_icon = 'FM16.png'
            cmdFM.tooltip = 'Makes offset faces for analysis'
            
            cmdWL = UI;;Command.new('Window Looker'){ start }
            cmdWL.small_icon = 'WL24.png'
            cmdWL.large_icon = 'WL16.png'
            cmdWL.tooltip = 'photographs windows for their shadows'
            
            toolbar = toolbar.add_item cmdFM
            toolbar = toolbar.add_item cmdWL
            toolbar.show
            
            file_loaded('makeTheFaces.rb')
          end
      
          class << self
            
            def isThisFourSided?(aFace)
              if aFace.vertices.length == 4
                return true
              else
                return false
              end
            end
            
            def getAnalysisDetails
              #this asks the user for some input to the process
              #OUTPUT it returns an array of strings that explain the various inputs
              #it'd be better/clearer if it returned a hash of formatted values
              #          [0                                        1           2                 3             4           5                 6                         7                   ]
              prompts  = ['image path',                                'cameraFOV','inset factor %', 'start hour', 'end hour', 'tests per hour', 'image width in pixels', 'Analysis Layer Name']
              defaults = ['C;\Users\bdoherty\Desktop\BVN\skTestImages','120',      '3',              '13',         '15',       '2',              '500',                   'Analysis'           ]
              lists    = ['',                                          '',         '',               '',           '',         '1|2|3|4|5',      '',                      ''                   ]
              input    = UI.inputbox prompts , defaults, lists, 'fill in some information'
              result = {;imagePath   => input[0].to_s.strip, 
                        ;cameraFOV   => input[1].to_f,
                        ;insetFactor => input[2].to_f * 0.01,
                        ;startTime   => input[3].to_i,
                        ;endTime     => input[4].to_i,
                        ;hourDivs    => input[5].to_i,
                        ;outputWidth => input[6].to_i,
                        ;analysisLayerName => input[7].to_s
                       } 
              return result
            end
            
            def isFaceHorizontal?(aFace)
              #This function checks to see if a face is horizontal, 
              #i.e. if its normal faces directly up or directly down
              #INPUT it takes in a face, and an identifier that is  
              #used to provide an error if it is horizontal.
              #OUTPUT returns true if it is horizontal, false if it isn't
              internalNormal = aFace.normal
              if internalNormal == [0,0,1] || internalNormal == [0,0,-1]          
                return false
              else
                return true
              end
            end
          
            def calculateEyeDistance(theCamera, faceWidth)
              #calculates the distance that a camera needs to be away from 
              #a face in order to see it with a given field of view
              eyeDistance = ((faceWidth/2)/Math.tan(theCamera.fov.degrees/2))
              eyeDistance = eyeDistance/25.4 #to fix the crazy inch thing
              if eyeDistance == 0 
                eyeDistance = 10000          
              end
              return eyeDistance
            end
          
            def shadowInfoString
              #extract shadow information from the model to show to the user
              shadowInfo  = Sketchup.active_model.shadow_info
              message = ""#declare an empty string outside the scope of the 'each'
              shadowInfo.each_pair {|key, value| message += "#{key} is #{value}\n" }
              return message
            end
            
            def inspectImage(path, fileName, iar)
              myImage = QuickMagick;;Image.read(path + fileName).first
              numColours = myImage.colors
              #i.convert "QuickMagick;;Image.read(path+fileName).first -colorspace rgb -colors 10 -format \"%c\"  histogram;info;"
              #hold is there because it seems that if you assign to a variable, then the program waits 
              #for a return value, otherwise it just keeps going without anything to process.
              hold = myImage.draw_text(100, 100, 'colours ' << numColours.to_s)
              hold = myImage.save(path + fileName)
            end
            
            def findAMaterial(listOfAllMaterials, name)
            #INPUT   A string name of a material
            #RETURNS the material object that corresponds to the string name given
              for i in (0...listOfAllMaterials.length)
                if listOfAllMaterials[i].name == name
                  return listOfAllMaterials[i]
                end
              end
            end
            
            def bound( valtoCheck, lowerBound, upperBound )
            #INPUT a number
            #RETURNS that number if it is between the boundaries, otherwise the boundary that it hits
            #i.e. bound(10,5,15) ==> 10
            #     bound(20,5,15) ==> 15
            #     bound( 0,5,15) ==>  5
              if valtoCheck > upperBound
                return upperBound
              elsif valtoCheck < lowerBound
                return lowerBound
              else
                return valtoCheck
              end
            end
          
          	def formatPath (pathString)
            #this adds a trailing / to the path if it doesn't have one
            #it also swaps the slashes from \ to /        
              pathString.gsub!("\\", '/')
              if pathString[pathString.length-1] == '/'
                puts '/ not added'
                return pathString
              else
                return pathString + '/'
              end
            end
        
            def getAnalysisDetailsFM
            #this asks the user for some input to the process
            #OUTPUT it returns an array of strings that explain the various inputs
            #            [ 0                       1            2              ]
              prompts  = ['Offset Distance (mm)', 'LayerName' , 'Material Name']
              defaults = ['10',                   'Analysis'  , 'Analysis'     ]
              lists    = ['',                     '',           ''             ]
              input    = UI.inputbox prompts , defaults, lists, "fill in some information"
              result = {
                        ;offset   => input[0].to_f,
                        ;layer    => input[1].to_s.strip,
                        ;material => input[2].to_s.strip
                       } 
              return result
            end
            
            def findALayer(listOflayers, layerNameToMatch)
            #INPUT   A string name of a layer
            #RETURNS the layer object that corresponds to the string name given
              layerToMatch = nil
              listOflayers.each{|l|
                if l.name == layerNameToMatch 
                  layerToMatch = l
                end
              }
              return layerToMatch
            end
            
            def layerFilter(setToFilter, layerNameToMatch)
              #INPUT   a set of entities to filter through & a string layername to search for
              #RETURNS an array of all the entities in the input set that are also on the input layer
              filteredSet = [] 
              #get the layer as an object so that the comparison 
              #isn't on the string name of the layer 
              layerToMatch = findALayer(Sketchup.active_model.layers, layerNameToMatch)
              #start filtering
              setToFilter.each{|e|
                if e.layer == layerToMatch
                  filteredSet << e
                end
              }
              return filteredSet
            end
        
            def propertyFilter(setToFilter)
              newSet = []
              setToFilter.each{|e|
                if e.is_a? Sketchup;;Face
                  if isFaceHorizontal?(e) and isThisFourSided?(e)
                    newSet << e
                  end
                end
              }
            end
      
            def start
              puts "\n\n\n"
              
              result = UI.messagebox shadowInfoString << "\n\nAre these details correct?", MB_YESNO
              if result == 6 # Yes
                #this writes a message to the status bar (SB_PROMPT means the left bit)
                Sketchup.set_status_text "great, lets get going", SB_PROMPT
                
                userInput   = getAnalysisDetails                          #there needs to be / on the end of the path, formatPath does that
                imagePath   = formatPath(userInput[;imagePath])           #the field of view is in degrees and must be between 1 and 120)
                cameraFOV   = bound(userInput[;cameraFOV],  0, 120 )      #this trims the image ever so slightly so that it doesn't include the lines around the face
                insetFactor = bound(userInput[;insetFactor],0,50)         #this is percentages between 0 and 1, i.e. 50% is 0.5 and 5% is 0.05
                startTime   = bound(userInput[;startTime]  ,0,23)         #hours in 24hr format
                endTime     = bound(userInput[;endTime]    ,startTime,24) #hours in 24hr format
                hourDivs    =       userInput[;hourDivs]                  #how many chunks to chop the hour into i.e. 4 = 15 minutes
                outputWidth = bound(userInput[;outputWidth],50,2000)      #width in pixels of the images, height is set by the aspect ratio
                analysisLayerName = userInput[;analysisLayerName]         #the name of the layer that all the analysis faces are on
                #############################################################################################
                
                model = Sketchup.active_model
                #entities = model.active_entities
                #ent = model.entities
                filteredSelection = layerFilter(model.active_entities, analysisLayerName)
                filteredSelection = propertyFilter(filteredSelection)
                model.shadow_info["DisplayShadows"] = true
                m = 60/hourDivs
                
                itWorked = windowLooker(imagePath,         cameraFOV,   insetFactor,
                                        startTime,         endTime,     hourDivs, 
                                        filteredSelection, m,           model,
                                        outputWidth)
                if itWorked 
                  puts "that all seemed to work out"
                  Sketchup.set_status_text "that all seemed to work out", SB_PROMPT
                end
              else
                Sketchup.set_status_text "OHNOES!! Set the location settings and try again", SB_PROMPT
              end
            end #def start
          
            def windowLooker( imagePath,        cameraFOV,   insetFactor,
                              startTime,        endTime,     hourDivs, 
                              currentSelection, m,           model,
                              outputWidth)
                              
              Dir.chdir imagePath
              for i in (0...currentSelection.length)
                #builds a folder name from the current face's attributes
                folderName = imagePath + 'apptNum_'   + currentSelection[i].get_attribute("analysisInfo","apptNumber") +
                                         '_apptType_' + currentSelection[i].get_attribute("analysisInfo","apptType")
                #if the folder already exists, don't try and make it again!!
                if not File.directory? folderName
                  Dir.mkdir(folderName)
                  #puts 'made  ' + folderName
                end
                Dir.chdir folderName
      
                for hour in (startTime..endTime)
                  for minutes in (0...hourDivs) # three dots ignores last value i.e. 0...3 ==> 0,1,2
                    Sketchup.set_status_text "#{hour.to_s};#{(m*minutes).to_s}", SB_PROMPT
                    # make the face local for the rest of this loop
                    face = currentSelection[i] 
                    
                    #set the time  Time.utc( year [, month, day, hour, min, sec, usec] )
                    timeNow =      Time.utc( 2010,   "Jun", 21,  hour, m*minutes,  0)
                    model.shadow_info["ShadowTime"] = timeNow 
                    
                    #get information about the face
                    normal = face.normal
                    centroidPoint = face.bounds.center
                    #0 = left front bottom              #1 = right front bottom
                    #2 = left back bottom               #3 = right back bottom
                    #4 = left front top                 #5 = right front top
                    #6 = left back top                  #7 = right back top
                    height = (face.bounds.corner(4).distance face.bounds.corner(0)).to_mm
                    width  = (face.bounds.corner(0).distance face.bounds.corner(3)).to_mm
                    
                    #setup for the camera
                    camera = Sketchup;;Camera.new              
                    camera.description  = 'camera looking at window ' << i.to_s
                    aspectRatio = width/height
                    camera.aspect_ratio = aspectRatio
                    camera.fov = cameraFOV 
                    #this shrinks the view to account for prudence and window frames etc.
                    width = width * (1-insetFactor) 
                    eyeDistance = calculateEyeDistance(camera, width)
                    
                    eyeOffset = normal.transform( Geom;;Transformation.scaling(eyeDistance))
                    
                    eye          = centroidPoint.offset(eyeOffset)
                    target       = centroidPoint
                    camera.set eye, target, Z_AXIS  
                    
                    #get the material and hold it, the face 
                    #must be white to avoid problems of translucency
                    holdMaterial = face.material              
                    face.material = "snow"
                    
                    #change the view
                    view = model.active_view
                    status = view.camera = camera
                    
                    #save the image
                    timeString = "#{"%02d" % hour}_#{("%02d" % (m*minutes))}"
                    apptNum = 'apptNum_' + currentSelection[i].get_attribute("analysisInfo","apptNumber")
                    fileName = apptNum + '_at_' + timeString + ".png" #'window' + ("%03d" %  i)
                    view.write_image fileName, outputWidth, outputWidth*(1/aspectRatio), false
                    
                    #TODO this is all the image magick stuff, 
                    #inspectImage(folderName, fileName, aspectRatio)
                    
                    #change the material back to what it was to begin with
                    face.material = holdMaterial              
                    view.refresh
                    statusText = apptNum + " successful at #{timeString}" 
                    puts statusText
                    Sketchup.set_status_text statusText, SB_PROMPT
                  end       #for minutes in (0...hourDivs)
                end         #for hour in (startTime..endTime)
              end           #for i in (0...currentSelection.length)
              Sketchup.active_model.entities.erase_entities(currentSelection.to_a)
            end             #windowLooker
      
            def makeAnalysisFaces
              
              mod = Sketchup.active_model
              ent = mod.entities
              #sel = mod.selection
              
              userInput = getAnalysisDetailsFM
              analysisLayer = mod.layers.add userInput[;layer]
              analysisMaterial = findAMaterial(mod.materials, userInput[;material])
              faceCounter = 0
      
              ent.each{ |e|
                if e.is_a? Sketchup;;ComponentInstance
                  e.definition.entities.each { |newE|
                    if ((newE.is_a? Sketchup;;Face) and (newE.material == analysisMaterial)) 
                      #make an empty group
                      tempGroup = ent.add_group
                      #make an offset face
                      normal = Geom;;Vector3d.new(newE.normal).normalize
                      offsetFactor = userInput[;offset].mm 
                      offset = normal.transform( Geom;;Transformation.scaling(offsetFactor))
                      newPoints = []
                      newE.vertices.each{|vertex| newPoints << vertex.position.offset(normal)}
                      #make a new face in that group
                      face = tempGroup.entities.add_face newPoints
                      face.layer = analysisLayer
                      #apply a transformation to the group taken from the instance of the component
                      tempGroup.transformation = e.transformation
                      #set some attributes for that face. They will be used later to indicate where the face came from.
                      face.set_attribute "analysisInfo", "apptType",   e.definition.name
                      face.set_attribute "analysisInfo", "apptNumber", e.name
                      #remove the group, the face stays where it is
                      tempGroup.explode 
                      
                      faceCounter = faceCounter + 1
                    end       #if newE.is_a? Sketchup;;Face
                   }          #e.definition.entities.each
                end           #if e.is_a
              }               #ent.each
              status = "Made  #{faceCounter.to_s} planes"
              Sketchup.set_status_text status, SB_PROMPT
              puts status
            end               #makeAnalysisFaces
          end                 #class
        end                   #facemaker
      end                     #voyeur
      

      Issues that I can see with it:

      It gets really slow with big models, I'm not sure where the slowness comes from, but I presume it is the filtering for the right entity.

      Most of the functions could be a lot more defensive

      nearly all the methods should really be tagged onto existing classes so it is myFace.horizontal ==> true rather than isFaceHorizontal?(aFace)

      but... it does the job for now, and maybe I can do that if I come back to it.

      p.s. Dan - mostly c# scripting languages in the past.

      posted in Developers' Forum
      B
      ben.doherty
    • RE: Getting SketchUp to play nicely with RubyGems?

      Dan Rathbun helped me with this the other day here

      I hacked it a bit to make it fit my system and it works fine now.

      posted in Developers' Forum
      B
      ben.doherty
    • RE: Entering components

      So here's the solution I came to. I'm sure that there are better ways to do this, but this was quick, and it worked. I suppose in the future adding a method to the vector class instead of making an instance method would be neater etc.

      [as I see it] Feature instances can't be entered, but their definitions can be. That means that you can find the face that you need to access, but it'll be at the origin. the Instance does have a transform, so if you apply that transform to the new object then you can make it be in the same place as the instance.
      But wait! you can't transform a face, but you can transform a group, so just throw the face into a group and transform that.

      Any raises on that? it doesn't seem too elegant, but it does the job.

      #module Voyeur
      #module FaceMaker
      
      if( not file_loaded?("makeTheFaces.rb") )
      
          # This will add a separator to the menu, but only once
          add_separator_to_menu("Voyeur")
          
          plugins_menu = UI.menu("Plugins")
          voyeur_menu = plugins_menu.add_submenu("Voyeur")
          voyeur_menu.add_item("make analysis faces") { makeAnalysisFaces }
      end
      
        def toP3d(vec)
          return Geom;;Point3d.new(vec.x,vec.y,vec.z)
        end
        
        def vertToVec(aVertex)
          unless aVertex == nil
            pos = aVertex.position
            theVec = Geom;;Vector3d.new(pos.x,pos.y,pos.z)
            return theVec 
          end
        end
        def scaleVec(vec,scalar)
          return Geom;;Vector3d.new(vec.x * scalar, vec.y * scalar, vec.z * scalar)
        end
        
        def makeAnalysisFaces
          
          mod = Sketchup.active_model
          ent = mod.entities
          sel = mod.selection
          
          analysisLayer = mod.layers.add 'analysis'
          
          ent.each{ |e|
            if e.is_a? Sketchup;;ComponentInstance
              e.definition.entities.each { |newE|
                if ((newE.is_a? Sketchup;;Face) and (newE.material != nil))
                  #puts newE.material.name
                  verts  = newE.vertices
                  normal = Geom;;Vector3d.new(newE.normal).normalize
                  offsetFactor = 3000.mm
                  offset = scaleVec(normal, offsetFactor)
                  newPoints = []
                  for i in (0...verts.length)
                    pvec = vertToVec(verts[i])
                    newPoints[i] = toP3d(pvec + offset)
                    #ent.add_cpoint newPoints[i]
                  end
                  tempGroup = ent.add_group
                  face = tempGroup.entities.add_face newPoints
                  #puts "#{e.name}'s transform matrix"
                  #puts e.transformation.to_a
                  tempGroup.transformation = e.transformation
                  tempGroup.set_attribute "analysisInfo", "apptType",   e.definition.name
                  tempGroup.set_attribute "analysisInfo", "apptNumber", e.name
                  tempGroup.layer = analysisLayer 
                end       #if newE.is_a? Sketchup;;Face
               }          #e.definition.entities.each
            end           #if e.is_a
          }               #ent.each
        end               #makeAnalysisFaces
      #end
      #end
      

      I am having lots of trouble with modules though, if I ask the ruby console for Voyeur::FaceMaker::instance_methods it gives me back a list of the methods as I'd expect, but if I say Voyeur::FaceMaker.makeAnalysisFaces it throws a #<NoMethodError.... any idea how to fix that? Currently I've just taken the module stuff out, which seems like bad karma.

      posted in Developers' Forum
      B
      ben.doherty
    • RE: Entering components

      I've actually cracked this. I found an old thread that explained it. I'll post it up in a bit when I finish it

      posted in Developers' Forum
      B
      ben.doherty
    • Entering components

      Hi, This seems really simple, so hopefully it is!

      I want to loop through all the instances of a component in a model, then loop through it's entities and find a face that has a special material (lets say kryptonite).

      From looking at the API it seems that Groups have a .entities, but ComponentInstances don't.

      Any hints on how is it done?

      Back story *(ignore this if you aren't too bothered about the application of this)*I'm analysing an appartment block. There are 8 or so appartment types, each is placed and named according to it's position, so one might be 'floor 5 appt 7'. There is a critical window that I need to test using the method outlined in the Taking pictures with cameras thread.

      My plan so far is to take a copy of each face and put it onto a layer, run the test for each window, and then delete them all. Essentially leaving the model untouched.

      posted in Developers' Forum
      B
      ben.doherty
    • RE: Taking pictures with cameras

      Thanks for that!
      I did actually need the aspect ratio to be right as I'm using (trying to at least) Image Magick to interrogate the images.

      See what you think.

      require 'sketchup.rb'
      require"quick_magick.rb"
      
      def start
        puts "************************"
        ##changeable stuff###########################################################################
        #you need to swap all \ backslashes for / forward slashes
        #you also need to put a / on the end of the path
        imagePath   = 'C;/Users/Ben/Desktop/BVN/skTestImages/'
        #the field of view is in degrees and must be between 1 and 120)
        cameraFOV   = 120 
        #this trims the image ever so slightly so that it doesn't 
        #include the lines around the face
        insetFactor =   0.05
        startTime   =  13  #hours in 24hr format
        endTime     =  15  #hours in 24hr format
        hourDivs    =   2  #how many chunks to chop the hour into i.e. 4 = 15 minutes
        outputWidth = 500
        #############################################################################################
        model = Sketchup.active_model
        #entities = model.active_entities
        #ent = model.entities
        currentSelection = model.selection
        model.shadow_info["DisplayShadows"] = true
        m = 60/hourDivs
        
        result = UI.messagebox shadowInfoString + "\n\nAre these details correct?", MB_YESNO
        if result == 6 # Yes
          #this writes a message to the status bar (SB_PROMPT means the left bit)
          Sketchup.set_status_text "great, lets get going", SB_PROMPT
          itWorked = windowLooker(imagePath, 
                                  cameraFOV, 
                                  insetFactor, 
                                  startTime, 
                                  endTime, 
                                  hourDivs, 
                                  currentSelection, 
                                  m, 
                                  model,
                                  outputWidth)
          if itWorked 
            puts "that all seemed to work out"
          end
        else
          Sketchup.set_status_text "OHNOES!! Set the location settings and try again", SB_PROMPT
        end
      end
      
      def windowLooker( imagePath, 
                        cameraFOV, 
                        insetFactor, 
                        startTime, 
                        endTime, 
                        hourDivs, 
                        currentSelection, 
                        m,
                        model,
                        outputWidth)
        for hour in (startTime..endTime)
          for minutes in (0...hourDivs) # three dots ignores last value i.e. 0...3 ==> 0,1,2
            for i in (0...currentSelection.length)
              if (currentSelection[i].is_a?(Sketchup;;Face))
                # make the face local for the rest of this loop
                face = currentSelection[i] 
                if isFaceHorizontal(face, i) && isThisFourSided(face)
                  #set the time
                  #         Time.utc( year [, month, day, hour, min, sec, usec] )
                  timeNow = Time.utc( 2010,   "Jun", 21,  hour, m*minutes,  0)
                  model.shadow_info["ShadowTime"] = timeNow 
                  
                  #get information about the face
                  normal = face.normal
                  centroidPoint = face.bounds.center
                  height = (face.bounds.corner(4).distance face.bounds.corner(0)).to_mm
                  width  = (face.bounds.corner(0).distance face.bounds.corner(3)).to_mm  
      
                  #setup for the camera
                  camera = Sketchup;;Camera.new
                  camera.description  = "camera looking at window " + i.to_s()
                  aspectRatio = width/height
                  camera.aspect_ratio = aspectRatio
                  camera.fov = cameraFOV 
                  #this shrinks the view to account for prudence and window frames
                  width = width * (1-insetFactor) 
                  eyeDistance = calculateEyeDistance(camera, width)
      
                  normalPoint = []
                  normalPoint[0] = centroidPoint[0] + (normal[0]*eyeDistance)
                  normalPoint[1] = centroidPoint[1] + (normal[1]*eyeDistance)
                  normalPoint[2] = centroidPoint[2] + (normal[2]*eyeDistance)
                  
                  eye          = normalPoint
                  target       = centroidPoint
                  up           = [0,0,1]
                  camera.set eye, target, up  
      
                  #get the material and hold it, the face 
                  #must be white to avoid problems of translucency
                  holdMaterial = face.material
                  face.material = "white"
      
                  #change the view
                  view = model.active_view
                  status = view.camera = camera
                  #save the image
                  Dir.chdir( imagePath )
                  fileName = 'window' + ("%03d" %  i) + 
                                "_" + hour.to_s + "_" + 
                                (m*minutes).to_s + '.png'
                  view.write_image fileName, outputWidth, outputWidth*(1/aspectRatio), false
                  inspectImage(imagePath, fileName, aspectRatio)
      
                  #change the material back to what it was to begin with
                  face.material = holdMaterial
                  view.refresh
                  puts "face " + i.to_s() + " successful at " + hour.to_s + ";" + (m*minutes).to_s
                end
              end
            end
          end
        end
      end
      
      def isThisFourSided(aFace)
        if aFace.vertices.length == 4
          return true
        else
          puts "face " + i.to_s + " probably isn't a window"
          return false
        end
      end
      
      def isFaceHorizontal(aFace, identifierOfFace)
        if aFace.normal == [0,0,1] || aFace.normal == [0,0,-1]
          puts "face " + identifierOfFace.to_s + " is horizontal " + aFace.normal.to_s
          return false
        else
          return true
        end
      end
      
      def calculateEyeDistance(theCamera, faceWidth)
        eyeDistance = ((faceWidth/2)/Math.tan(theCamera.fov.degrees/2))
        eyeDistance = eyeDistance/25.4 #to fix the crazy inch bug
        if eyeDistance == 0 
          eyeDistance = 1
          puts "eye zero failure"
          puts "width was " + faceWidth.to_s
          puts "Math.tan(camera.fov) was " + Math.tan(radToDeg(theCamera.fov)/2).to_s 
        end
        return eyeDistance
      end
      
      def shadowInfoString
        #extract shadow information to show to the user
        shadowInfo  = Sketchup.active_model.shadow_info
        message = ""#declare an empty string outside the scope of the 'each'
        shadowInfo.each_pair {|key, value| message += "#{key} is #{value}\n" }
        return message
      end
      
      def cropImage(path, fileName, iar)
        i = QuickMagick;;Image.read(path+fileName).first
        cropW      = i.width.to_i
        cropH      = ((1/iar)*cropW).to_i
        offsetTop  = ((i.height/2)-(cropH/2)).to_i
        offsetleft = 0 
        puts "iar " + iar.to_s
        puts "cropW " + cropW.to_s
        puts "cropH " + cropH.to_s
        puts "offsetTop " + offsetTop.to_s
        puts "offsetleft " + offsetleft.to_s
        #geometry spec is in the format "widthxHeight!+leftOffset+topOffset"
        # the ! ignores the aspect ratio
        #more here http://www.imagemagick.org/script/command-line-processing.php#geometry
        geometrySpec = cropW.to_s + 
                "x" + 
                cropH.to_s + 
                "!+" + 
                offsetleft.to_s + 
                "+" + 
                offsetTop.to_s
        puts "geometrySpec " + geometrySpec
        i.crop geometrySpec
        i.save(path+fileName)
        i = QuickMagick;;Image.read(path+fileName).first
        numColours = i.colors
        #i.convert "QuickMagick;;Image.read(path+fileName).first -colorspace rgb -colors 10 -format \"%c\"  histogram;info;"
        i.draw_text(100, 100, "cropped " + geometrySpec + " colours " + numColours.to_s)
        i.save(path+fileName)
        sleep 0.5
      end
      
      def inspectImage(path, fileName, iar)
        i = QuickMagick;;Image.read(path+fileName).first
        numColours = i.colors
        #i.convert "QuickMagick;;Image.read(path+fileName).first -colorspace rgb -colors 10 -format \"%c\"  histogram;info;"
        i.draw_text(100, 100, " colours " + numColours.to_s)
        i.save(path+fileName)
        #sleep 0.5
      end
      
      start
      
      posted in Developers' Forum
      B
      ben.doherty
    • Taking pictures with cameras

      Hi,
      I've been working on this script to take pictures of windows to see if they are in shadow or not.
      Dan Rathbun has been helping me out over at the google group, but I though I'd post it here too.

      It's going pretty well, but I still have a few question, other than that I'd be very receptive to general comments.
      for now:
      The images that are saved are showing the correct aspect ratio, but within the shape of the window. (http://flic.kr/p/8gR74b - there's an example here.) Is there a way to export images that don't have the greyed out section?

      Thanks for taking a look
      Ben

      
      require 'sketchup.rb'
      #require 'RMagick.rb'
      
      def start
        puts "************************"
        ##changeable stuff
        imagePath   = 'C;\sk test images'
        cameraFOV   = 120 #in degrees (between 1 and 120)
        insetFactor =  0.03
        startTime   = 10  #hours in 24hr format
        endTime     = 15  #hours in 24hr format
        hourDivs    =  2  #how many chunks to chop the hour into i.e. 4 = 15 minutes
        ###########
        model = Sketchup.active_model
        #entities = model.active_entities
        #ent = model.entities
        currentSelection = model.selection
        model.shadow_info["DisplayShadows"]= true
        m = 60/hourDivs
        
        result = UI.messagebox shadowInfoString + "\n\nAre these details correct?", MB_YESNO
        if result == 6 # Yes
          #this writes a message to the status bar (SB_PROMPT means the left bit)
          Sketchup.set_status_text "great, lets get going", SB_PROMPT
          itWorked = windowLooker(imagePath, 
                                  cameraFOV, 
                                  insetFactor, 
                                  startTime, 
                                  endTime, 
                                  hourDivs, 
                                  currentSelection, 
                                  m, 
                                  model)
          if itWorked 
            puts "that all seemed to work out"
          end
        else
          Sketchup.set_status_text "OHNOES!! Set the location settings and try again", SB_PROMPT
        end
      end
      
      def windowLooker( imagePath, 
                        cameraFOV, 
                        insetFactor, 
                        startTime, 
                        endTime, 
                        hourDivs, 
                        currentSelection, 
                        m,
                        model)
        for hour in (startTime..endTime)
          for minutes in (0...hourDivs) # three dots ignores last value i.e. 0...3 ==> 0,1,2
            for i in (0...currentSelection.length)
              if (currentSelection[i].is_a?(Sketchup;;Face))
                # make the face local for the rest of this loop
                face = currentSelection[i] 
                if isFaceHorizontal(face, i) && isThisFourSided(face)
                  #set the time
                  #         Time.utc( year [, month, day, hour, min, sec, usec] )
                  timeNow = Time.utc( 2010,   "Jun", 21,  hour, m*minutes,  0)
                  model.shadow_info["ShadowTime"] = timeNow 
                  
                  #get information about the face
                  normal = face.normal
                  centroidPoint = face.bounds.center
                  height = (face.bounds.corner(4).distance face.bounds.corner(0)).to_mm
                  width  = (face.bounds.corner(0).distance face.bounds.corner(3)).to_mm  
      
                  #setup for the camera
                  camera = Sketchup;;Camera.new
                  camera.description  = "camera looking at window " + i.to_s()
                  camera.aspect_ratio = width/height
                  camera.fov = cameraFOV 
                  #this shrinks the view to account for prudence and window frames
                  width = width * (1-insetFactor) 
                  eyeDistance = calculateEyeDistance(camera, width)
      
                  normalPoint = []
                  normalPoint[0] = centroidPoint[0] + (normal[0]*eyeDistance)
                  normalPoint[1] = centroidPoint[1] + (normal[1]*eyeDistance)
                  normalPoint[2] = centroidPoint[2] + (normal[2]*eyeDistance)
                  
                  eye          = normalPoint
                  target       = centroidPoint
                  up           = [0,0,1]
                  camera.set eye, target, up  
      
                  #get the material and hold it, the face 
                  #must be white to avoid problems of translucency
                  holdMaterial = face.material
                  face.material = "white"
                  #model.entities.add_line(centroidPoint, normalPoint)
                  puts "face " + i.to_s() + " successful"
      
                  #change the view
                  view = model.active_view
                  status = view.camera = camera
                  #save the image
                  Dir.chdir( imagePath )
                  view.write_image 'window' + ("%03d" %  i) + 
                                "_" + hour.to_s + "_" + 
                                (m*minutes).to_s + '.png'
      
                  #change the material back to what it was to begin with
                  face.material = holdMaterial
                  view.refresh
                end
              end
            end
          end
        end
      end
      
      def isThisFourSided(aFace)
        if aFace.vertices.length == 4
          return true
        else
          puts "face " + i.to_s + " probably isn't a window"
          return false
        end
      end
      
      def isFaceHorizontal(aFace, identifierOfFace)
        if aFace.normal == [0,0,1] || aFace.normal == [0,0,-1]
          puts "face " + identifierOfFace.to_s + " is horizontal " + aFace.normal.to_s
          return false
        else
          return true
        end
      end
      
      def calculateEyeDistance(theCamera, faceWidth)
        eyeDistance = ((faceWidth/2)/Math.tan(radToDeg(theCamera.fov)/2))
        eyeDistance = eyeDistance/25.4 #to fix the crazy inch bug
        if eyeDistance == 0 
          eyeDistance = 1
          puts "eye zero failure"
          puts "width was " + faceWidth.to_s
          puts "Math.tan(camera.fov) was " + Math.tan(radToDeg(theCamera.fov)/2).to_s 
        end
        return eyeDistance
      end
      
      def shadowInfoString
        #extract shadow information to show to the user
        shadowInfo  = Sketchup.active_model.shadow_info
        message = ""#declare an empty string outside the scope of the 'each'
        shadowInfo.each_pair {|key, value| message += "#{key} is #{value}\n" }
        return message
      end
      
        def radToDeg (aNumberInRadians)
          aNumberInRadians * Math;;PI / 180 
        end
      
      
      start
      
      posted in Developers' Forum
      B
      ben.doherty
    • 1 / 1