code: package org.sunflow.core;
  
  import org.sunflow.image.Color;
  import org.sunflow.math.Vector3;
  
  /**
   * Represents a sample taken from a light source that faces a point being
   * shaded.
   */
  public class LightSample {
      private Ray shadowRay; // ray to be used to evaluate if the point is in
      // shadow
      private Color ldiff;
      private Color lspec;
      LightSample next; // pointer to next item in a linked list of samples
  
      /**
       * Creates a new light sample object (invalid by default).
       */
      public LightSample() {
          ldiff = lspec = null;
          shadowRay = null;
          next = null;
      }
  
      boolean isValid() {
          return ldiff != null && lspec != null && shadowRay != null;
      }
  
      /**
       * Set the current shadow ray. The ray's direction is used as the sample's
       * orientation.
       * 
       * @param shadowRay shadow ray from the point being shaded towards the light
       */
      public void setShadowRay(Ray shadowRay) {
          this.shadowRay = shadowRay;
      }
  
      /**
       * Trace the shadow ray, attenuating the sample's color by the opacity of
       * intersected objects.
       * 
       * @param state shading state representing the point to be shaded
       */
      public final void traceShadow(ShadingState state) {
          Color opacity = state.traceShadow(shadowRay);
          Color.blend(ldiff, Color.BLACK, opacity, ldiff);
          Color.blend(lspec, Color.BLACK, opacity, lspec);
      }
  
      /**
       * Get the sample's shadow ray.
       * 
       * @return shadow ray
       */
      public Ray getShadowRay() {
          return shadowRay;
      }
  
      /**
       * Get diffuse radiance.
       * 
       * @return diffuse radiance
       */
      public Color getDiffuseRadiance() {
          return ldiff;
      }
  
      /**
       * Get specular radiance.
       * 
       * @return specular radiance
       */
      public Color getSpecularRadiance() {
          return lspec;
      }
  
      /**
       * Set the diffuse and specular radiance emitted by the current light
       * source. These should usually be the same, but are distinguished to allow
       * for non-physical light setups or light source types which compute diffuse
       * and specular responses seperately.
       * 
       * @param d diffuse radiance
       * @param s specular radiance
       */
      public void setRadiance(Color d, Color s) {
          ldiff = d.copy();
          lspec = s.copy();
      }
  
      /**
       * Compute a dot product between the current shadow ray direction and the
       * specified vector.
       * 
       * @param v direction vector
       * @return dot product of the vector with the shadow ray direction
       */
      public float dot(Vector3 v) {
          return shadowRay.dot(v);
      }
  }