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.

 
AAPL
$100.96
Apple Inc.
-0.83
MSFT
$47.52
Microsoft Corpora
+0.84
GOOG
$596.08
Google Inc.
+6.81

MacTech Search:
Community Search:

Software Updates via MacUpdate

Chromium 37.0.2062.122 - Fast and stable...
Chromium is an open-source browser project that aims to build a safer, faster, and more stable way for all Internet users to experience the web. FreeSMUG-Free OpenSource Mac User Group build is... Read more
Attachment Tamer 3.1.14b9 - Take control...
Attachment Tamer gives you control over attachment handling in Apple Mail. It fixes the most annoying Apple Mail flaws, ensures compatibility with other email software, and allows you to set up how... Read more
Duplicate Annihilator 5.0 - Find and del...
Duplicate Annihilator takes on the time-consuming task of comparing the images in your iPhoto library using effective algorithms to make sure that no duplicate escapes. Duplicate Annihilator detects... Read more
jAlbum Pro 12.2 - Organize your digital...
jAlbum Pro has all the features you love in jAlbum, but comes with a commercial license. With jAlbum, you can create gorgeous custom photo galleries for the Web without writing a line of code!... Read more
jAlbum 12.2 - Create custom photo galler...
With jAlbum, you can create gorgeous custom photo galleries for the Web without writing a line of code! Beginner-friendly, with pro results Simply drag and drop photos into groups, choose a design... Read more
Quicken 2015 2.0.4 - Complete personal f...
Quicken 2015 helps you manage all your personal finances in one place, so you can see where you're spending and where you can save. Quicken automatically categorizes your financial transactions,... Read more
iMazing 1.0 - Complete iOS device manage...
iMazing (formerly DiskAid) is the ultimate iOS device manager with capabilities far beyond what iTunes offers. With iMazing and your iOS device (iPhone, iPad, or iPod), you can: Copy music to and... Read more
Xcode 6.0.1 - Integrated development env...
Apple Xcode is Apple Computer's integrated development environment (IDE) for OS X. The full Xcode package is free to ADC members and includes all the tools you need to create, debug, and optimize... Read more
Apple Safari 7.1 - Apple's Web brow...
Apple Safari in OS X Mavericks brings you all-new ways to find and enjoy the best of the web. It works with iCloud to give you a seamless browsing experience across all your devices. It looks out for... Read more
Delivery Status 6.1.2 - Check delivery s...
Delivery Status displays delivery status of packages for a variety of shipment services. Can't wait for your packages to arrive? Don't waste your time checking the site constantly, just open this all... Read more

Latest Forum Discussions

See All

View Source – HTML, JavaScript and CSS...
View Source – HTML, JavaScript and CSS 1.0 Device: iOS Universal Category: Utilities Price: $.99, Version: 1.0 (iTunes) Description: View Source is an app plus an iOS 8 Safari extension that makes it easy to do one key web developer... | Read more »
Avenged Sevenfold’s Hail To The King: De...
Avenged Sevenfold’s Hail To The King: Deathbat is Coming to iOS on October 16th Posted by Jessica Fisher on September 19th, 2014 [ permalink ] Just in time for Halloween, on October 16 Avenged Sevenfold will be launching | Read more »
Talisman Has Gone Universal – Can Now be...
Talisman Has Gone Universal – Can Now be Played on the iPhone Posted by Jessica Fisher on September 19th, 2014 [ permalink ] | Read more »
Tap Army Review
Tap Army Review By Jennifer Allen on September 19th, 2014 Our Rating: :: SHOOT EM ALLUniversal App - Designed for iPhone and iPad Mindless but fun, Tap Army is a lane-based shooter that should help you relieve some stress.   | Read more »
Monsters! Volcanoes! Loot! Epic Island f...
Monsters! Volcanoes! Loot! | Read more »
Plunder Pirates: Tips, Tricks, Strategie...
Ahoy There, Seadogs: Interested in knowing our thoughts on all this plundering and pirating? Check out our Plunder Pirates Review! Have you just downloaded the rather enjoyable pirate-em-up Plunder Pirates and are in need of some assistance? Never... | Read more »
Goat Simulator Review
Goat Simulator Review By Lee Hamlet on September 19th, 2014 Our Rating: :: THE GRUFFEST OF BILLY GOATSUniversal App - Designed for iPhone and iPad Unleash chaos as a grumpy goat in this humorous but short-lived casual game.   | Read more »
A New and Improved Wunderlist is Here fo...
A New and Improved Wunderlist is Here for iOS 8 Posted by Jessica Fisher on September 19th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Evernote Update for iOS 8 Adds Web Clipp...
Evernote Update for iOS 8 Adds Web Clipping, Quick Notes, and More Posted by Ellis Spice on September 19th, 2014 [ permalink ] | Read more »
Apple Names Ultimate Productivity Bundl...
Apple Names Ultimate Productivity Bundle by Readdle as the Essential Bundle on the App Store Posted by Jessica Fisher on September 19th, 2014 [ permalink | Read more »

Price Scanner via MacPrices.net

Mac Pros available for up to $260 off MSRP
Adorama has Mac Pros on sale for up to $260 off MSRP. Shipping is free, and Adorama charges sales tax in NY & NJ only: - 4-core Mac Pro: $2839.99, $160 off MSRP - 6-core Mac Pro: $3739.99, $260... Read more
13-inch 2.6GHz/256GB Retina MacBook Pros avai...
B&H Photo has the 13″ 2.6GHz/256GB Retina MacBook Pro on sale for $1379 including free shipping plus NY sales tax only. Their price is $120 off MSRP. Read more
Previous-generation 15-inch 2.0GHz Retina Mac...
B&H Photo has leftover previous-generation 15″ 2.0GHz Retina MacBook Pros now available for $1599 including free shipping plus NY sales tax only. Their price is $400 off original MSRP. B&H... Read more
21″ 2.7GHz iMac available for $1179, save $12...
Adorama has 21″ 2.7GHz Hawell iMacs on sale for $1179.99 including free shipping. Their price is $120 off MSRP. NY and NJ sales tax only. Read more
iOS 8 Adoption Rate Slower than iOS 7, 6, Hit...
Apple began pushing out iOS 8 updates to eligible devices around 1pm ET on September 17, 2014. However, unlike with iOS 7, which boasted a wide variety of differences from its predecessor iOS 6, in... Read more
LIkely Final Definitive OS X 10.9.5 Mavericks...
Apple has released what will almost certainly be the last incremental version number update of OS X 10.9 Mavericks (save for futire security updates) before OS X 10.10 Yosemite is released next month... Read more
Fingerprints, Apple Pay and Identity Theft Wa...
On Sep 9th, CEO Tim Cook unveiled Apple Pay, along with the new iPhone 6 and iWatch. Apple Pay is a newly developed technology that utilizes a near field communication (NFC) to enable customer... Read more
Amazon Introduces Two All-New Kindles
Amazon on Thursday introduced the 7th generation of its Kindle dedicated e-reader device: Kindle Voyage, its top-of-the-line e-reader, and the new $79 Kindle, with a 20% faster processor, twice the... Read more
Save up to $300 on the price of a new Mac wit...
Purchase a new Mac or iPad at The Apple Store for Education and take up to $300 off MSRP. All teachers, students, and staff of any educational institution qualify for the discount. Shipping is free,... Read more
13-inch 2.8GHz Retina MacBook Pro available f...
B&H Photo has the new 2014 13″ 2.8GHz Retina MacBook Pro on sale for $1699.99 including free shipping plus NY sales tax only. They’ll also include free copies of Parallels Desktop and LoJack for... Read more

Jobs Board

Project Manager, *Apple* Financial Services...
**Job Summary** Apple Financial Services (AFS) offers consumers, businesses and educational institutions ways to finance Apple purchases. We work with national and Read more
*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.