TweetFollow Us on Twitter

Jul 97 Challenge

Volume Number: 13 (1997)
Issue Number: 7
Column Tag: Programmer's Challenge

Jul 97 - Programmer's Challenge

by Bob Boonstra, Westford, MA

Disambiguator

The Challenge this month is to write a string completion routine loosely patterned after the keyword lookup facility in the QuickView utility. QuickView will suggest a completion of the keyword as you begin to type it, and update that suggested completion as you continue to type. In the Toolbox Assistant, for example, if you are looking for documentation on InitGraf and type "i", the suggested completion is "iconIDToRgn". As you continue by typing "n", the suggestion becomes "index2Color". Adding "i" yields "initAllPacks"; adding "t" leaves the suggestion intact; adding "g" changes it to "initGDevice". Finally, typing "r" gives the desired "initgraf".

For our disambiguator, you will be given an unsorted list of words and an opportunity to preprocess them. Then you will be given a string to match and asked to return a list of words matching findString. To make the problem more interesting, the match string can contain wild card characters, as described below.

The prototype for the code you should write is:

typedef unsigned long ulong;

void InitDisambiguator(
   const char *const wordList[],   /* words to match against */
   ulong numWords,                 /* number of words in wordList */
   void *privStorage,              /* private storage preinitialized to zero */
   ulong storageSize               /* number of bytes of privStorage */
);

ulong /*numMatch*/ Disambiguator(
   const char *const wordList[],   /* words to match against */
   ulong numWords,                 /* number of words in wordList */
   void *privStorage,              /* private storage */
   ulong storageSize,              /* number of bytes of privStorage */
   char *findString,               /* string to match, includes wild cards */
   char *matchList[]               /* return matched words here */
);

Your InitDisambiguator routine will be called with an unsorted list wordList of numWords null-terminated words to match. The wordList words will include alphanumeric characters, spaces, and underscores. You will also be provided with a pointer privStorage to storageSize bytes of preallocated memory initialized to zero. The amount of storage provided will be at least 20 bytes for each word in wordList, plus one byte for each character in the wordList (including the null byte, and rounded up to a multiple of 4). In other words, storageSize will be no smaller than minStorage, calculated as:

for (minStorage=0,i=0; i<numWords; i++)
   minStorage += 20 + 4*(1+strlen(wordList[i])/4);

InitDisambiguator is not allowed to modify the wordList, but you may store a sorted version of wordList, or pointers to the words in sorted order, in privStorage. The first four parameters provided to Disambiguator will be identical as those provided to InitDisambiguator. In addition, you will be provided with the null-terminated findString and a preallocated array matchList with numWords entries where you are to store pointers to the words that match findString. Your string matches should be case insensitive (i.e., "initgr" matches "InitGraf". The matchList should be returned with the strings ordered in case-insensitive ASCII order (i.e., space < [0..9] < [A-Za-z] < underscore).

The findString may also contain zero or more of the wildcard characters '?', '*', and '+'. The wildcard '?' matches any single character, '*' matches zero or more characters, and '+' matches one or more characters. So, for example, "*graf" matches any string ending in the (case-insensitive) string "graf", while "+1Ind+" matches any string containing "1Ind" between the first and last characters of a word.

For each call to InitDisambiguator, your Disambiguator routine will be called an average of 100 to 1000 times. The winner will be the solution that finds the correct matchList in the minimum amount of time, including the time taken by the initialization routine.

This will be a native PowerPC Challenge, using the latest CodeWarrior environment. Solutions may be coded in C, C++, or Pascal. The problem is based on a suggestion by Charles Kefauver, who pointed me to an April, 1995, AppleDirections article discussing the user interface for a disambiguator. Charles wins 2 Challenge points for his suggestion.

Three Months Ago Winner

Congratulations to ACC Murphy (Perth, Australia), for submitting the faster (and smaller) of the two entries I received for the Projection Challenge. This problem required contestants to calculate the image of a set of input polygons, including the shadows cast by one polygon on another, given an observation viewpoint and an illumination point.

Both of the submitted solutions used a ray-tracing technique. The winning solution calculates, for each point on the projection plane, the nearest polygon to the viewpoint among those intersecting the ray from the plane to the viewpoint. It then does another ray-trace to determine if there are any other polygons between the illumination point and the projected polygon, identifying the point as being in shadow if an intervening polygon is found.

I ran three test cases, moving the polygons 10 times for a given viewpoint in each case, using a GWorld bounds rectangle slightly smaller than my 1024x768 monitor. As you can see from the execution times, considerable refinement would be needed before this code could be used for animation.

A good discussion of the projection and hidden surface removal algorithms applicable to this problem can be found in the Black Art of Macintosh Game Programming, by Kevin Tieskoetter. In addition to discussing the z-buffer ray-tracing algorithm, it describes another technique for hidden surface removal called the Painter's algorithm. This approach breaks the polygons to be displayed into pieces such that each piece is entirely in front of or entirely behind any other piece, as seen from the viewpoint. The polygons can then be sorted and displayed without looking at each pixel in the image. For our application, two polygon decompositions would be required, one for the image, and one for the shadows.

The table below lists, for each entry, the execution time for each case and the code size. The number in parentheses after the entrant's name is the total number of Challenge points earned in all Challenges to date prior to this one.

                      Case 1   Case 2   Case 3    Total       Code
Name                   Time     Time     Time   Time (secs)   Size
A.C.C. Murphy (10)    29.02    23.64    81.61     134.27      4196
Ernst Munter (232)    20.87    58.11    89.76     168.74      7192

Top 20 Contestants

Here are the Top Contestants for the Programmer's Challenge. The numbers below include points awarded over the 24 most recent contests, including points earned by this month's entrants.

Rank    Name             Points   Rank     Name             Points
   1.   Munter, Ernst      194       11.   Beith, Gary         24
   2.   Gregg, Xan         114       12.   Cutts, Kevin        21
   3.   Cooper, Greg        54       13.   Nicolle, Ludovic    21
   4.   Larsson, Gustav     47       14.   Picao, Miguel Cruz  21
   5.   Lengyel, Eric       40       15.   Brown, Jorg         20
   6.   Boring, Randy       37       16.   Gundrum, Eric       20
   7.   Mallett, Jeff       37       17.   Higgins, Charles    20
   8.   Lewis, Peter        32       18.   Kasparian, Raffi    20
   9.   Murphy, ACC         30       19.   Slezak, Ken         20
   10.  Antoniewicz, Andy   24       20.   Studer, Thomas      20

There are three ways to earn points: (1) scoring in the top 5 of any Challenge, (2) being the first person to find a bug in a published winning solution or, (3) being the first person to suggest a Challenge that I use. The points you can win are:

1st place   20 points             5th place   2 points
2nd place   10 points           finding bug   2 points
3rd place   7 points   suggesting Challenge   2 points
4th place   4 points

Here is A.C.C. Murphy's winning solution:
Challenge.p
A.C.C. Murphy

unit Challenge;

(*

Assumptions:
   Storage space must be big enough for 13 floats per polygon
   All points must be significantly smaller in magnitude than BIG_FLOAT = 
      1000000.0
   Polygons are translucent (their colour based uplon lighting is independent 
      of the side of the polygon that is lit)
   50% attenuation of colour is used
   50% attenuation of black is black
      
Method:
   InitProjection is not used
   
   First we precalculate a small bounding sphere for the polygon points.
   Next we get the information about the GWorld to allow direct pixel access.
   Then for each point on the GWorld, we trace the ray from the point to the 
      eye, intersecting it with each polygon and finding the one closes to 
      the eye (furthest forward, since the eye is infront of all polygons).  
      That determines the colour.  We then trace the ray from that intersection 
      point to the light source to determine whether the point is in shadow, 
      and if so we halve the intensity. We set the colour of the pixel and 
      move on.
   
   Optimizations:
      Direct pixel access to the GWorld (known to be 32 bit)
      Bounding sphere used to optimize the ray/polygon intersection test.
      Time is approximately 2 microseconds per pixel per polygon on an 8500.
*)

interface

   uses
      Types, Quickdraw, QDOffscreen;
      
   const
      kMAXPOINTS = 10;

   const
      BIG_FLOAT = 1000000.0;
         
   type
      float = real;
      
   type
      My2DPoint = record (* point in z==0 plane*)
         x2D: float; (* x coordinate*)
         y2D: float; (* y coordinate*)
      end;
      My3DPoint = record
         x3D: float;                 (* x coordinate*)
         y3D: float;                 (* y coordinate*)
         z3D: float;                 (* z coordinate*)
      end;
      My3DDirection = record
         thetaX:float;              (* angle in radians*)
         thetaY:float;              (* angle in radians*)
         thetaZ:float;              (* angle in radians*)
      end;
      MyPlane = record
         planeNormal: My3DDirection; (* normal vector to plane*)
         planeOrigin: My3DPoint;     (* origin of plane in 3D space*)
      end;
      MyPolygon = record
         numPoints: longint;      (* number of points in polygon*)
         thePoint: array[0..kMAXPOINTS-1] of My2DPoint;
                                  (* polygon in z==0 plane*)
         polyPlane: MyPlane;      (* rotate/translate z==0 plane to this plane*)
         polyColor: RGBColor;     (* the color to draw this polygon*)
      end;
      MyPolygonArray = array[0..0] of MyPolygon;
      
         
   procedure InitProjection(
      const viewPoint: My3DPoint;(* viewpoint from which to project*)
      const illumPoint:My3DPoint;(* viewpoint from which to draw shadow*)
      storage: univ Ptr;         (* auxiliary storage preallocated for your use*)
      storageSize: longint       (* number of bytes of storage*)
   );

   procedure CalcProjection(
      offScreen: GWorldPtr;          (* GWorld to draw projection *)
      const thePolys: MyPolygonArray;(* polygons to project *)
      numPolys: longint;             (* number of polygons to project *)
      const viewPoint: My3DPoint;    (* viewpoint from which to project *)
      const illumPoint: My3DPoint;
                               (* illumination point from which to draw shadow *)
      storage: univ Ptr;       (* auxiliary storage preallocated for your use*)
      storageSize: longint     (* number of bytes of storage*)
   );

implementation

   type
      Ray3D = record
         origin: My3DPoint;
         direction: My3DPoint;
      end;
      PolygonExtra = record
         normal, rotX, rotY, center: My3DPoint;
         radius2: float;
      end;
      PolygonExtraArray = array[0..0] of PolygonExtra;
      StorageRecord = record
         poly_extra: PolygonExtraArray;
                  { must be at the end, since it's an extensible array }
      end;
      StorageRecordPtr = ^StorageRecord;
      
   function DotProduct(const src1, src2 : My3DPoint) : float;
   begin
      DotProduct := src1.x3D*src2.x3D +  
                    src1.y3D*src2.y3D +  
                    src1.z3D*src2.z3D;
   end;
   
CrossProduct
   procedure CrossProduct(src1, src2 : My3DPoint; 
                    var dst : My3DPoint);
   begin
      dst.x3D := src1.y3D*src2.z3D - src1.z3D*src2.y3D;
      dst.y3D := src1.z3D*src2.x3D - src1.x3D*src2.z3D;
      dst.z3D := src1.x3D*src2.y3D - src1.y3D*src2.x3D;
   end;
   
AddVectors
   procedure AddVectors(const src1, src2 : My3DPoint; 
                     var dst : My3DPoint);
   begin
      dst.x3D := src1.x3D + src2.x3D;
      dst.y3D := src1.y3D + src2.y3D;
      dst.z3D := src1.z3D + src2.z3D;
   end;
      
SubtractVectors
   procedure SubtractVectors(const src1, src2 : My3DPoint; 
                      var dst : My3DPoint);
   begin
      dst.x3D := src1.x3D - src2.x3D;
      dst.y3D := src1.y3D - src2.y3D;
      dst.z3D := src1.z3D - src2.z3D;
   end;
   
MidPoint
   procedure MidPoint( const src1, src2 : My3DPoint; 
                      var dst : My3DPoint);
   begin
      dst.x3D := (src1.x3D + src2.x3D) / 2;
      dst.y3D := (src1.y3D + src2.y3D) / 2;
      dst.z3D := (src1.z3D + src2.z3D) / 2;
   end;
   
Distance2
   function Distance2( const src1, src2 : My3DPoint) : float;
   begin
      Distance2 := sqr(src1.x3D - src2.x3D) + 
                      sqr(src1.y3D - src2.y3D) + 
                      sqr(src1.z3D - src2.z3D);
   end;
   
ScaleVector
   procedure ScaleVector(const src : My3DPoint; scale : float; 
                      var dst : My3DPoint);
   begin
      dst.x3D := src.x3D * scale;
      dst.y3D := src.y3D * scale;
      dst.z3D := src.z3D * scale;
   end;
      
NormalizeVector
   procedure NormalizeVector(const src : My3DPoint;
                      var dst : My3DPoint);
      var
         length : float;
   begin
      length := sqrt(DotProduct(src,src));   
      dst.x3D := src.x3D / length;
      dst.y3D := src.y3D / length;
      dst.z3D := src.z3D / length;
   end;
   
MakeViewRay
   procedure MakeViewRay(const eye : My3DPoint;
                      x, y, z: float; var ray : Ray3D);
   begin
      ray.origin.x3D := x;
      ray.origin.y3D := y;
      ray.origin.z3D := z;
      ray.direction.x3D := eye.x3D - x;
      ray.direction.y3D := eye.y3D - y;
      ray.direction.z3D := eye.z3D - z;
      NormalizeVector(ray.direction, ray.direction);
   end;
   
RotateX
   procedure RotateX(src : My3DPoint; sinA, cosA : float; 
                      var dst : My3DPoint);
   begin
      dst.x3D := src.x3D;
      dst.y3D := cosA*src.y3D - sinA*src.z3D;
      dst.z3D := sinA*src.y3D + cosA*src.z3D;
   end;
   
RotateY
   procedure RotateY( src : My3DPoint; sinA, cosA : float; 
                      var dst : My3DPoint);
   begin
      dst.x3D := cosA*src.x3D + sinA*src.z3D;
      dst.y3D := src.y3D;
      dst.z3D := -sinA*src.x3D + cosA*src.z3D;
   end;
   
RotateZ
   procedure RotateZ( src : My3DPoint; sinA, cosA : float; 
                      var dst : My3DPoint);
   begin
      dst.x3D := cosA*src.x3D - sinA*src.y3D;
      dst.y3D := sinA*src.x3D + cosA*src.y3D;
      dst.z3D := src.z3D;
   end;
   
PointInPlaneInPolygon
   function PointInPlaneInPolygon( const pt: My2DPoint; const 
               poly: MyPolygon ): boolean;
      function Quadrant( const pt: My2DPoint; x, y: float ): 
                      longint;
      begin
         if pt.x2D > x then begin
            if pt.y2D > y then begin
               Quadrant := 0;
            end else begin
               Quadrant := 3;
            end;
         end else begin
            if pt.y2D > y then begin
               Quadrant := 1;
            end else begin
               Quadrant := 2;
            end;
         end;
      end;
      
      function x_intercept( const pt1, pt2: My2DPoint;
                      yy: float ): 
                      float;
      begin
         x_intercept := pt2.x2D - 
                     ( (pt2.y2D - yy) * 
                        ((pt1.x2D - pt2.x2D)/(pt1.y2D - pt2.y2D)) );
      end;
      
      var
         i, angle, quad, next_quad, delta: longint;
         last_vertex, next_vertex: My2DPoint;
   begin
      angle := 0;
      last_vertex := poly.thePoint[poly.numPoints-1];
      quad := Quadrant( last_vertex, pt.x2D, pt.y2D );
      for i := 1 to poly.numPoints do begin
         next_vertex := poly.thePoint[i-1];
         next_quad := Quadrant( next_vertex, pt.x2D, pt.y2D );
         delta := next_quad - quad;
         case delta of
            3: delta := -1;
            -3: delta := 1;
            2, -2: begin
               if x_intercept( last_vertex, next_vertex, pt.y2D ) > 
                           pt.x2D then begin
                  delta := -delta;
               end;
            end;
            otherwise begin
            end;
         end;
         angle := angle + delta;
         quad := next_quad;
         last_vertex := next_vertex;
      end;
      PointInPlaneInPolygon := (angle = 4) | (angle = -4);
   end;
   
Intersect
   function Intersect(const ray: Ray3D; const poly: MyPolygon; 
         const poly_extra: PolygonExtra; var distance : float; 
         var intersectionPt: My3DPoint) : boolean;
      var
         tempVector : My3DPoint;
         temp1, temp2 : float;
         intersectionPoint : My3DPoint;
         intersection2D : My2DPoint;
         Ib, Ic, Id: float;
   begin
      Intersect := false;

      { intersect ray with sphere }
      SubtractVectors( ray.origin, poly_extra.center,
                            tempVector );
      Ib := 2 * DotProduct( ray.direction, tempVector );
      Ic := DotProduct( tempVector, tempVector ) - 
                            poly_extra.radius2;
      Id := sqr(Ib) - 4.0*Ic;
      if Id >= 0 then begin { yes, ray intersects sphere }
         temp1 := DotProduct( poly.polyPlane.planeOrigin, 
                            poly_extra.normal ) - 
                     DotProduct( poly_extra.normal, ray.origin );
         temp2 := DotProduct(ray.direction, poly_extra.normal);
         if temp2 <> 0 then begin
            distance := temp1 / temp2;
            if distance > 0 then begin
               ScaleVector(ray.direction, distance, intersectionPoint);
               AddVectors(intersectionPoint, ray.origin, 
                           intersectionPoint);
               
               if Distance2(intersectionPoint, poly_extra.center)
                  <= 
                                          poly_extra.radius2 then begin 
                  { intersection point is whithin sphere.  
                     Find out if it is actually in the polygon }
                  intersectionPt := intersectionPoint;
                  { First translate back to the origin }
                  SubtractVectors(intersectionPoint, 
                     poly.polyPlane.planeOrigin,intersectionPoint);
                  intersection2D.x2D := DotProduct(
                     intersectionPoint,
                        poly_extra.rotX );
                  intersection2D.y2D := DotProduct( 
                     intersectionPoint, 
                        poly_extra.rotY );
                  { Then check if it is whithin the polygon }
                  Intersect := PointInPlaneInPolygon
                                                      (intersection2D,poly);
               end;
            end;
         end;
      end;
   end;

InitProjection
   procedure InitProjection(
      const viewPoint: My3DPoint;(* viewpoint from which to project *)
      const illumPoint:My3DPoint;
                                 (* viewpoint from which to draw shadow *)
      storage: univ Ptr;         (* auxiliary storage preallocated for your use *)
      storageSize: longint       (* number of bytes of storage *)
   );
   begin
{$unused( viewPoint, illumPoint, storage, storageSize )}
   end;

PreparsePolygons
   procedure PreparsePolygons( my_storage: StorageRecordPtr;
   numPolys: longint; const thePolys: MyPolygonArray );
      var
         i, j: longint;
         pt: My3DPoint;
         pts: array[1..kMAXPOINTS] of My3DPoint;
         min_x, min_y, min_z, max_x, max_y, max_z: My3DPoint;
         dist_x, dist_y, dist_z, new_radius2: float;
         radius, new_radius, old_to_new: float;
         sinX, cosX, sinY, cosY, sinZ, cosZ: float;
   begin
      for i := 0 to numPolys-1 do begin
         with my_storage^.poly_extra[i], thePolys[i],
         polyPlane.planeNormal do begin
            sinX := sin(thetaX);
            cosX := cos(thetaX);
            sinY := sin(thetaY);
            cosY := cos(thetaY);
            sinZ := sin(thetaZ);
            cosZ := cos(thetaZ);
            normal.x3d := sinY*cosX;
            normal.y3d := -sinX;
            normal.z3d := cosY*cosX;
            rotX.x3D := 1;
            rotX.y3D := 0;
            rotX.z3D := 0;
            RotateZ(rotX, sinZ, cosZ, rotX);
            RotateX(rotX, sinX, cosX, rotX);
            RotateY(rotX, sinY, cosY, rotX);
            rotY.x3D := 0;
            rotY.y3D := 1;
            rotY.z3D := 0;
            RotateZ(rotY, sinZ, cosZ, rotY);
            RotateX(rotY, sinX, cosX, rotY);
            RotateY(rotY, sinY, cosY, rotY);
            
            for j := 1 to numPoints do begin
               pt.x3D := thePoint[j-1].x2D;
               pt.y3D := thePoint[j-1].y2D;
               pt.z3D := 0;
               RotateZ(pt, sinZ, cosZ, pt);
               RotateX(pt, sinX, cosX, pt);
               RotateY(pt, sinY, cosY, pt);
               pts[j] := pt;
               if j = 1 then begin
                  min_x := pt; min_y := pt; min_z := pt;
                  max_x := pt; max_y := pt; max_z := pt;
               end else begin
                  if pt.x3D < min_x.x3D then begin
                     min_x := pt;
                  end;
                  if pt.y3D < min_y.y3D then begin
                     min_y := pt;
                  end;
                  if pt.z3D < min_z.z3D then begin
                     min_z := pt;
                  end;
                  if pt.x3D > max_x.x3D then begin
                     max_x := pt;
                  end;
                  if pt.y3D > max_y.y3D then begin
                     max_y := pt;
                  end;
                  if pt.z3D > max_z.z3D then begin
                     max_z := pt;
                  end;
               end;
            end;
            
            dist_x := Distance2( min_x, max_x );
            dist_y := Distance2( min_y, max_y );
            dist_z := Distance2( min_z, max_z );
            if dist_x > dist_y then begin
               if dist_x > dist_z then begin
                  radius2 := dist_x/4;
                  MidPoint( min_x, max_x, center );
               end else begin
                  radius2 := dist_z/4;
                  MidPoint( min_z, max_z, center );
               end;
            end else begin
               if dist_y > dist_z then begin
                  radius2 := dist_y/4;
                  MidPoint( min_y, max_y, center );
               end else begin
                  radius2 := dist_z/4;
                  MidPoint( min_z, max_z, center );
               end;
            end;
            
            for j := 1 to numPoints do begin
               new_radius2 := Distance2( center, pts[j] );
               if new_radius2 > radius2 then begin
                  radius := sqrt(radius2);
                  new_radius := sqrt(new_radius2);
                  radius2 := (radius + new_radius)/2;
                  old_to_new := radius2 - radius;
                  center.x3D := (radius2*center.x3D + 
                              old_to_new*pts[j].x3D)/radius;
                  center.y3D := (radius2*center.y3D + 
                              old_to_new*pts[j].y3D)/radius;
                  center.z3D := (radius2*center.z3D + 
                              old_to_new*pts[j].z3D)/radius;
                  radius2 := sqr(radius2);
               end;
            end;
         
            AddVectors( polyPlane.planeOrigin, center, center );

         end;
      end;
   end;
   
CalcProjection
   procedure CalcProjection(
      offScreen: GWorldPtr;          (* GWorld to draw projection *)
      const thePolys: MyPolygonArray;(* polygons to project *)
      numPolys: longint;             (* number of polygons to project *)
      const viewPoint: My3DPoint;    (* viewpoint from which to project *)
      const illumPoint: My3DPoint;   (* illumination point from which to draw shadow *)
      storage: univ Ptr;         (* auxiliary storage preallocated for your use *)
      storageSize: longint      (* number of bytes of storage *)
   );
      var
         bounds: Rect;
         x, y : integer;
         colour : RGBColor;
         viewRay : Ray3D;
         lightRay : Ray3D;
         i : integer;
         closestDistance : float;
         closestIntersectionPt: My3DPoint;
         thisDistance : float;
         intersectionPt: My3DPoint;
         intersect_polygon: longint;
         pm: PixMapHandle;
         junk_boolean: boolean;
         pixels: Ptr;
         rowbytes_add: longint;
         my_storage: StorageRecordPtr;
   begin
{$unused( storage, storageSize )}
      my_storage := StorageRecordPtr(storage);

      PreparsePolygons( my_storage, numPolys, thePolys );

      SetGWorld( offScreen, nil );
      bounds := offScreen^.PortRect;
      pm := GetGWorldPixMap( offScreen );
      junk_boolean := LockPixels( pm );
      pixels := GetPixBaseAddr( pm );
      rowbytes_add := band( pm^^.rowBytes, $3FFF ) - 
                                    4 * (bounds.right - bounds.left);

      for y := bounds.top to bounds.bottom-1 do begin
         for x := bounds.left to bounds.right-1 do begin
            MakeViewRay(viewPoint, x, y, 0, viewRay);
            closestDistance := 0.0;
            intersect_polygon := -1;
            for i:= 1 to numPolys do begin
               if Intersect(viewRay, thePolys[i-1], 
                           my_storage^.poly_extra[i-1], thisDistance, 
                           intersectionPt) then begin
                  if (thisDistance > closestDistance) then begin
                     intersect_polygon := i;
                     closestDistance := thisDistance;
                     closestIntersectionPt := intersectionPt;
                  end;
               end
            end;
            if intersect_polygon > 0 then begin
               colour := thePolys[intersect_polygon-1].polyColor;

               MakeViewRay(illumPoint, closestIntersectionPt.x3D, 
                                 closestIntersectionPt.y3D, 
                                 closestIntersectionPt.z3D, lightRay);

               for i:= 1 to numPolys do begin
                  if (intersect_polygon <> i) & 
                     Intersect(lightRay, thePolys[i-1], 
                     my_storage^.poly_extra[i-1], 
                     thisDistance, intersectionPt) then begin
               colour.red := band(colour.red, $0FFFF) div 2;
               colour.green := band(colour.green, $0FFFF) div 2;
               colour.blue := band(colour.blue, $0FFFF) div 2;
                     leave;
                  end
               end;
               
      LongintPtr(pixels)^ := bsl( band(colour.red, $0FF00), 8 ) 
                     + band(colour.green, $0FF00) + 
                        bsr( band(colour.blue, $0FF00), 8 );
            end else begin
               LongintPtr(pixels)^ := 0;
            end;
            longint(pixels) := longint(pixels) + 4;
         end;
         longint(pixels) := longint(pixels) + rowbytes_add;
      end;
   end;

end.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Opera 44.0.2510.1449 - High-performance...
Opera is a fast and secure browser trusted by millions of users. With the intuitive interface, Speed Dial and visual bookmarks for organizing favorite sites, news feature with fresh, relevant content... Read more
Skim 1.4.29 - PDF reader and note-taker...
Skim is a PDF reader and note-taker for OS X. It is designed to help you read and annotate scientific papers in PDF, but is also great for viewing any PDF file. Skim includes many features and has a... Read more
FontExplorer X Pro 6.0.2 - Font manageme...
FontExplorer X Pro is optimized for professional use; it's the solution that gives you the power you need to manage all your fonts. Now you can more easily manage, activate and organize your... Read more
1Password 6.7.1 - Powerful password mana...
1Password is a password manager that uniquely brings you both security and convenience. It is the only program that provides anti-phishing protection and goes beyond password management by adding Web... Read more
Vivaldi 1.9.818.44 - An advanced browser...
Vivaldi is a browser for our friends. In 1994, two programmers started working on a web browser. Our idea was to make a really fast browser, capable of running on limited hardware, keeping in mind... Read more
Vivaldi 1.9.818.44 - An advanced browser...
Vivaldi is a browser for our friends. In 1994, two programmers started working on a web browser. Our idea was to make a really fast browser, capable of running on limited hardware, keeping in mind... Read more
Skim 1.4.29 - PDF reader and note-taker...
Skim is a PDF reader and note-taker for OS X. It is designed to help you read and annotate scientific papers in PDF, but is also great for viewing any PDF file. Skim includes many features and has a... Read more
1Password 6.7.1 - Powerful password mana...
1Password is a password manager that uniquely brings you both security and convenience. It is the only program that provides anti-phishing protection and goes beyond password management by adding Web... Read more
FontExplorer X Pro 6.0.2 - Font manageme...
FontExplorer X Pro is optimized for professional use; it's the solution that gives you the power you need to manage all your fonts. Now you can more easily manage, activate and organize your... Read more
VOX 2.8.24 - Music player that supports...
VOX just sounds better! The beauty is in its simplicity, yet behind the minimal exterior lies a powerful music player with a ton of features and support for all audio formats you should ever need.... Read more

Latest Forum Discussions

See All

Fire Emblem Heroes event announces new m...
As reported yesterday, Nintendo was gearing up a live press event for their popular mobile game,Fire Emblem Heroes. While the stream revealed a lot of new things, the event was entirely in Japanese. Luckily we have a rundown of what was announced... | Read more »
Best games we played this week
Another week, another slate of new mobile games. Although there weren't as many big name releases as last week, there were plenty of unique video game titles that came out that's sure to keep you interested over the weekend. Everything from classic... | Read more »
Olli by Tinrocket (Photography)
Olli by Tinrocket 1.0 Device: iOS iPhone Category: Photography Price: $2.99, Version: 1.0 (iTunes) Description: Get drawn in with Olli by TinrocketOlli instantly turns your everyday moments into hand-drawn art and animations. • Watch... | Read more »
Penarium (Games)
Penarium 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: | Read more »
Fire Emblem Heroes is way more profitabl...
Profits for Nintendo's mobile game Fire Emblem Heroes are apparently impressive enough to beat out other Nintendo titles likeSuper Mario Run, despite having 10 times fewer downloads. [Read more] | Read more »
Classic series Robot Unicorn Attack 3 no...
The classic Adult Swim browser game, Robot Unicorn Attack, branched off into a series of popular mobile games. Now, the latest entry into the series, Robot Unicorn Attack 3, is available for iOS and Android mobile devices. [Read more] | Read more »
Sudoku Sweeper (Games)
Sudoku Sweeper 1.0 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0 (iTunes) Description: A minimalist mashup of Minesweeper and Sudoku. Logic puzzle perfection. Every row, column and zone contains a bomb and one of... | Read more »
Under Leaves (Games)
Under Leaves 1.0.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0.0 (iTunes) Description: Journey into the forest, the jungle or the depths of the deep blue sea. Find chestnuts for the pigs, a caterpillar for the... | Read more »
Ninja Pizza Girl (Games)
Ninja Pizza Girl 1.0 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0 (iTunes) Description: In the not-so-distant future, rampart traffic congestion has resulted in only one way to deliver pizzas across town in thirty... | Read more »
SCRAP (Games)
SCRAP 1.0 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0 (iTunes) Description: That day, for no apparent reason, SCRAP decided to wake up and run. He had to, because his activation was a mistake the "Factory" could... | Read more »

Price Scanner via MacPrices.net

15-inch 2.7GHz Space Gray Touch Bar MacBook P...
B&H Photo has the 15″ 2.7GHz Space Gray Touch Bar MacBook Pro in stock today and on sale for $2599…$200 off MSRP. Shipping is free, and B&H charges NY & NJ sales tax only: - 15″ 2.7GHz... Read more
13-inch 2.9GHz/256GB Space Gray Touch Bar Mac...
B&H Photo has the 13″ 2.9GHz/256GB Space Gray Touch Bar MacBook Pro in stock today and on sale for $150 off MSRP including free shipping plus NY & NJ sales tax only: - 13″ 2.9GHz/256GB Touch... Read more
21-inch iMacs on sale for up to $151 off MSRP
B&H Photo has 21″ iMacs on sale for up to $151 off MSRP, each including free shipping plus NY sales tax only: - 21″ 3.1GHz iMac 4K: $1348 $151 off MSRP - 21″ 2.8GHz iMac: $1199.99 $100 off MSRP... Read more
Weekend deal: Up to $420 off new MacBook Pros...
Apple has Certified Refurbished 2016 15″ and 13″ MacBook Pros available for $230 to $420 off original MSRP. An Apple one-year warranty is included with each model, and shipping is free: - 15″ 2.6GHz... Read more
Price drop: 15-inch 2.2GHz Retina MacBook Pro...
Amazon has dropped their price on 15″ 2.2GHz Retina MacBook Pros (MJLQ2LL/A) to $1709.99 including free shipping. Their price is $290 off MSRP for this model. Note that stock may sell out quickly at... Read more
2.8GHz Mac mini on sale for $899, save $100
B&H Photo has the 2.8GHz Mac mini (model number MGEQ2LL/A) on sale for $899 including free shipping plus NY & NJ sales tax only. Their price is $100 off MSRP. Read more
Check Apple prices on any device with the iTr...
MacPrices is proud to offer readers a free iOS app (iPhones, iPads, & iPod touch) and Android app (Google Play and Amazon App Store) called iTracx, which allows you to glance at today’s lowest... Read more
New System Clock for macOS by B-Eng Now Avail...
Fehraltorf, Switzerland based B-Eng has announced the release and immediate availability of System Clock, the company’s new system monitor and information app developed exclusively for macOS. System... Read more
DEVONtechnologies Celebrates 15th Anniversary...
DEVONtechnologies celebrates its 15th company anniversary with a 30% discount on all its software products from May 1st through 5th, 2017. In spring 2002, DEVONtechnologies opened its website and... Read more
WaterField Designs Invites Customers to Help...
San Francisco based WaterField Designs invites customers and air travelers to participate in developing the next generation in-flight travel case, the Air Porter. Frustrated with limited legroom,... Read more

Jobs Board

Best Buy *Apple* Computing Master - Best Bu...
**501846BR** **Job Title:** Best Buy Apple Computing Master **Location Number:** 001126-South Bay Center-Store **Job Description:** **What does a Best Buy Apple Read more
Consultant or Sr. Consultant, *Apple* Allia...
…improve our business and your clients will be heard.Project Manager, Apple AllianceLocation:San Francisco preferred, open to other locationsLevel:Consultant or Sr. Read more
*Apple* Mac Computer Technician - GeekHampto...
…complex computer issues over the phone and in person? GeekHampton, Long Island's Apple Premium Service Provider, is looking for you! Come work with our crew Read more
*Apple* Mobile Master - Best Buy (United Sta...
**501505BR** **Job Title:** Apple Mobile Master **Location Number:** 000849-Gurnee-Store **Job Description:** **What does a Best Buy Apple Mobile Master do?** At Read more
Best Buy *Apple* Computing Master - Best Bu...
**498428BR** **Job Title:** Best Buy Apple Computing Master **Location Number:** 000293-Rockville-Store **Job Description:** **What does a Best Buy Apple Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.