Check point for shadow at given day/ime

shadows

#1

Im hoping to be able to somehow programmatically check a certain point on a model to see if there is a shadow cast on it at a given time of day, Ive looked into the ruby class ‘shadowInfo’ but that doesnt seem to be the solution. Any thoughts?


#2

First you should set your location, date and time in ShadowInfo. It should be sufficient to set Latitude, Longitude and TZOffset but you might want to change also city name and country. Then you set the time in Sketchup.active_model.shadow_info["ShadowTime"]. Remember to define a UTC time to assign the correct time independently by the time zone. Of course you can do this also from the UI: Model/info and Window/Shadows.

Sketchup.active_model.shadow_info["SunDirection"] will give you the Vector3d representing the direction of the sun at the current time and location.

You can then use Model.raytest with your point and the sun direction. The test will return the first object intersected by the ray. If it returns nil you see the sun from the point, if it returns something else the point is in shade.


#3

It is a bit more complicated. Both the face and it’s parent context must have their casts_shadows? property set true. If not, a loop must be repeated firing a new ray from the strike point up the sunward vector, and the test repeated.

Try this:

module TestPoint

  def self.in_shadow?( point, verbose=false )
    #
    # point   : an array of 3 Numerics || a Geom::Point3d object.
    # verbose : true || false
    #
    #
    mod  = Sketchup::active_model
    info = mod.shadow_info
    direction_towards_sun = info["SunDirection"]
    ray  = [ point, direction_towards_sun ]
    ignore_hidden_geometry = false # default for raytest() is true
    #
    retval = false
    result = true
    #
    until result.nil? || result == false ||
    (result.is_a?(Array) && result.empty?)
      #
      if verbose
        puts "...\nFiring sunward ray..."
      end
      result = mod.raytest( ray, ignore_hidden_geometry )
      #
      if !result || !result.is_a?(Array)
      (result.is_a?(Array) && result.empty?)
        retval = false
        result = nil
        if verbose
          puts "... NO HIT"
        end
        break # outta da' loop
      else
        #
        strike_point, stricken_objects = result
        #
        obj = stricken_objects.last
        if obj.is_a?(Sketchup::Face) && obj.parent != mod &&
        stricken_objects.size > 1
          parent = stricken_objects[-2]
        else
          parent = nil
        end
        #
        if verbose
          puts "... OBJECT STRUCK. \n..."
          puts "  <#{obj.class.name}> casts_shadows? (#{obj.casts_shadows?})"
          puts "  Parent <#{parent.class.name}> casts_shadows? (#{parent.casts_shadows?})" if !parent.nil?
        end
        if obj.casts_shadows? && parent.nil? ||
        obj.casts_shadows? && parent.casts_shadows?
          retval = true
          result = nil
          if verbose
            puts "  Point (#{ray[0].x},#{ray[0].y},#{ray[0].z}) in shadow? (TRUE)"
          end
          break
        else
          if verbose
            puts "  Point (#{ray[0].x},#{ray[0].y},#{ray[0].z}) in shadow? (unknown)"
          end
          ray = [ strike_point, direction_towards_sun ]
          # We must loop & retest from strike point ...
        end
        #
      end
      #
    end # until loop
    #
    if verbose
      puts "...\nCONCLUSION: Point (#{point.x},#{point.y},#{point.z}) in shadow? (#{retval.to_s.upcase})"
    end
    return retval
    #
  end ###

end

Here is a test model with a couple of test points on the floor. One is in the shadow of the wall. The other is in the sunlight cast through the window (whose faces and group container are set NOT to cast shadows.)
ShadowCasting.skp (40.6 KB) (SKPv14)


#4

Just as a note, in the sample code it tests for striking a face. It may be possible to strike an edge that is set to cast shadows. So you could modify the code:

if (obj.is_a?(Sketchup::Face) || obj.is_a?(Sketchup::Edge)) && obj.parent != mod &&