1 /**
  2  * The Render Engine
  3  * RenderContext2D
  4  *
  5  * @fileoverview The base 2D render context.  This context implements a number of
  6  *               methods which are then standard on all contexts which extend from
  7  *               it.
  8  *
  9  * @author: Brett Fattori (brettf@renderengine.com)
 10  * @author: $Author: bfattori $
 11  * @version: $Revision: 1555 $
 12  *
 13  * Copyright (c) 2011 Brett Fattori (brettf@renderengine.com)
 14  *
 15  * Permission is hereby granted, free of charge, to any person obtaining a copy
 16  * of this software and associated documentation files (the "Software"), to deal
 17  * in the Software without restriction, including without limitation the rights
 18  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 19  * copies of the Software, and to permit persons to whom the Software is
 20  * furnished to do so, subject to the following conditions:
 21  *
 22  * The above copyright notice and this permission notice shall be included in
 23  * all copies or substantial portions of the Software.
 24  *
 25  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 26  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 27  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 28  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 29  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 30  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 31  * THE SOFTWARE.
 32  *
 33  */
 34 
 35 // The class this file defines and its required classes
 36 R.Engine.define({
 37     "class":"R.rendercontexts.RenderContext2D",
 38     "requires":[
 39         "R.rendercontexts.AbstractRenderContext",
 40         "R.math.Math2D",
 41         "R.struct.Container"
 42     ]
 43 });
 44 
 45 /**
 46  * @class All 2D contexts should extend from this to inherit the
 47  * methods which abstract the drawing methods.
 48  * @extends R.rendercontexts.AbstractRenderContext
 49  * @constructor
 50  * @description Create a new instance of a 2d render context.
 51  * @param name {String} The name of the context
 52  * @param surface {HTMLElement} The element which represents the surface of the context
 53  */
 54 R.rendercontexts.RenderContext2D = function () {
 55     return R.rendercontexts.AbstractRenderContext.extend(/** @scope R.rendercontexts.RenderContext2D.prototype */{
 56 
 57         width:0,
 58         height:0,
 59         lineStyle:null,
 60         fillStyle:null,
 61         lineWidth:1,
 62         position:null,
 63         rotation:0,
 64         scaleX:1,
 65         scaleY:1,
 66         wPosition:null,
 67         wRotation:0,
 68         wScale:null,
 69         bBox:null,
 70         backgroundColor:null,
 71         font:null,
 72         fontWeight:null,
 73         fontSize:null,
 74         fontAlign:null,
 75         fontBaseline:null,
 76         fontStyle:null,
 77         zBins:null,
 78         postRenderList:null,
 79         _xformStack:null,
 80 
 81         /** @private */
 82         constructor:function (name, surface) {
 83             this.base(name || "RenderContext2D", surface);
 84             this.wPosition = R.math.Point2D.create(0, 0);
 85             this.wRotation = 0;
 86             this.wScale = 1;
 87             this.zBins = {
 88                 "0":{
 89                     all:R.struct.Container.create(),
 90                     vis:[]
 91                 }
 92             };
 93             this.zBins.activeBins = [0];
 94             this.postRenderList = [];
 95 
 96             // Default font settings
 97             this.font = "sans-serif";
 98             this.fontWeight = "normal";
 99             this.fontSize = "12px";
100             this.fontAlign = "left";
101             this.fontBaseline = "alphabetic";
102             this.fontStyle = "normal";
103             this._xformStack = [];
104             this.position = R.math.Point2D.create(0, 0);
105             this.rotation = 0;
106             this.scaleX = 1;
107             this.scaleY = 1;
108         },
109 
110         /**
111          * Releases the object back into the object pool.  See {@link R.engine.PooledObject#release}
112          * for more information.
113          */
114         release:function () {
115             this.base();
116             this.width = 0;
117             this.height = 0;
118             this.lineStyle = null;
119             this.fillStyle = null;
120             this.lineWidth = 1;
121             this.position = null;
122             this.rotation = 0;
123             this.scaleX = 1;
124             this.scaleY = 1;
125             this.bBox = null;
126             this.backgroundColor = null;
127             this.wPosition = null;
128             this.wRotation = 0;
129             this.wScale = null;
130             this.font = null;
131             this.fontWeight = null;
132             this.fontSize = null;
133             this.fontAlign = null;
134             this.fontBaseline = null;
135             this.fontStyle = null;
136             this.zBins = null;
137             this._xformStack = null;
138         },
139 
140         /**
141          * Clean up the render bins after cleaning up the contained objects.
142          */
143         cleanUp:function () {
144             this.base();
145             for (var b in this.zBins.activeBins) {
146                 this.zBins[this.zBins.activeBins[b]].all.destroy();
147                 this.zBins[this.zBins.activeBins[b]].all = null;
148                 this.zBins[this.zBins.activeBins[b]].vis = null;
149             }
150             this.zBins = {
151                 "0":{
152                     all:R.struct.Container.create(),
153                     vis:[]
154                 }
155             };
156             this.zBins.activeBins = [0];
157         },
158 
159         /**
160          * Sorts objects by their {@link R.objects.Object2D#getZIndex z-index}.  Objects
161          * that don't have a z-index are untouched.
162          */
163         sort:function () {
164             this.base(R.rendercontexts.RenderContext2D.sortFn);
165         },
166 
167         /**
168          * Add an object to the context.  Only objects
169          * within the context will be rendered.  If an object declared
170          * an <tt>afterAdd()</tt> method, it will be called after the object
171          * has been added to the context.
172          *
173          * @param obj {R.engine.BaseObject} The object to add to the render list
174          */
175         add:function (obj) {
176             this.base(obj);
177 
178             // Organize objects into bins by their zIndex so we can
179             // determine dirty rectangles
180             if (obj.getZIndex) {
181                 this.swapBins(obj, R.rendercontexts.RenderContext2D.NO_ZBIN, obj.getZIndex());
182             } else {
183                 // If they don't have a zIndex, put them in the zeroth bin
184                 this.swapBins(obj, R.rendercontexts.RenderContext2D.NO_ZBIN, 0);
185             }
186         },
187 
188         /**
189          * Remove an object from the render context.  The object is
190          * not destroyed when it is removed from the container.  The removal
191          * occurs after each update to avoid disrupting the flow of object
192          * traversal.
193          *
194          * @param obj {Object} The object to remove from the container.
195          * @return {Object} The object that was removed
196          */
197         remove:function (obj) {
198             this.base(obj);
199 
200             if (obj.getZIndex) {
201                 // Remove the object from the zBins
202                 var zBin = this.zBins[obj.getZIndex()];
203                 zBin.all.remove(obj);
204                 R.engine.Support.arrayRemove(zBin.vis, obj);
205             } else {
206                 this.zBins["0"].all.remove(obj);
207                 R.engine.Support.arrayRemove(this.zBins["0"].vis, obj);
208             }
209         },
210 
211         /**
212          * Swap the zBin that the object is contained within.
213          * @param obj {R.objects.Object2D} The object to swap
214          * @param oldBin {Number} The old bin number, or <tt>RenderContext2D.NO_ZBIN</tt> to just
215          *    insert into a new bin.
216          * @param newBin {Number} The new bin to put the object into
217          */
218         swapBins:function (obj, oldBin, newBin) {
219             var zBin;
220             if (oldBin != R.rendercontexts.RenderContext2D.NO_ZBIN) {
221                 // Remove the object from the old zBin
222                 zBin = this.zBins[oldBin];
223                 zBin.remove(obj);
224             }
225 
226             // We'll need to know the sorted order of bin numbers since there may be gaps
227             if (!this.zBins[newBin]) {
228                 this.zBins.activeBins.push(newBin);
229                 this.zBins.activeBins.sort();
230             }
231 
232             // Add to a bin
233             zBin = this.zBins[newBin];
234             if (!zBin) {
235                 this.zBins[newBin] = {
236                     all:R.struct.Container.create(), // List of all objects in the bin
237                     vis:[]                                   // Optimized list of only visible objects
238                 };
239 
240                 zBin = this.zBins[newBin];
241             }
242 
243             // Start objects out as "not visible"
244             this.getContextData(obj).isVisible = false;
245 
246             // Add the object to the "all objects" container
247             zBin.all.add(obj);
248         },
249 
250         /**
251          * Called to render all of the objects to the context.
252          *
253          * @param time {Number} The current render time in milliseconds from the engine.
254          * @param dt {Number} The delta between the world time and the last time the world was updated
255          *          in milliseconds.
256          */
257         render:function (time, dt) {
258             // Push the world transform
259             this.pushTransform();
260 
261             this.setupWorld(time, dt);
262 
263             // Run the objects in each bin
264             for (var zbin in this.zBins.activeBins) {
265                 var bin = this.zBins[this.zBins.activeBins[zbin]];
266 
267                 // Don't want to push the entire bin onto the stack
268                 this.processBin(this.zBins.activeBins[zbin]);
269                 R.Engine.rObjs += bin.vis.length;
270 
271                 var objs = bin.vis;
272                 this.renderBin(zbin, objs, time, dt);
273             }
274 
275             // Restore the world transform
276             this.popTransform();
277 
278             while (this.postRenderList.length > 0) {
279                 var fn = this.postRenderList.shift();
280                 fn.call(this);
281             }
282 
283             // Safely remove any objects that were removed from
284             // the context while it was rendering
285             if (this.safeRemoveList.length > 0) {
286                 this._safeRemove();
287             }
288         },
289 
290         /**
291          * A rendering function to perform in world coordinates.  After the world has
292          * been rendered, and all transformations have been reset to world coordinates,
293          * the list of post-render functions are executed.
294          * @param fn {Function} A function to execute
295          */
296         postRender:function (fn) {
297             this.postRenderList.push(fn);
298         },
299 
300         /**
301          * Process all objects in a bin, optimizing the list down to only those that are visible.
302          * TODO: Hoping to put this in a Worker thead at some point for speed
303          * @param binId {String} The bin Id
304          * @private
305          */
306         processBin:function (binId) {
307             var bin = this.zBins[binId];
308 
309             // Spin through "all" objects to determine visibility.
310             var itr = bin.all.iterator();
311             while (itr.hasNext()) {
312                 // Check if the object is visible, so it'll be processed.
313                 var obj = itr.next(), contextModel = this.getContextData(obj);
314                 if (!obj.getWorldBox || (obj.isKeepAlive && obj.isKeepAlive()) ||
315                     (this.getExpandedViewport().isIntersecting(obj.getWorldBox()))) {
316                     // If the object isn't visible, push it into the "visibility" list
317                     if (!contextModel.isVisible) {
318                         contextModel.isVisible = true;
319                         bin.vis.push(obj);
320                     }
321                 } else if (contextModel.isVisible) {
322                     // The object isn't in the viewport and is marked visible, unmark it and
323                     // remove from "visibility" list
324                     contextModel.isVisible = false;
325                     R.engine.Support.arrayRemove(bin.vis, obj);
326                 }
327             }
328             itr.destroy();
329         },
330 
331         /**
332          * Render all of the objects in a single bin, grouped by z-index.
333          * @param bin {Number} The bin number being rendered
334          * @param objs {Array} Array of objects
335          * @param time {Number} The current render time in milliseconds from the engine.
336          * @param dt {Number} The delta between the world time and the last time the world was updated
337          *          in milliseconds.
338          */
339         renderBin:function (bin, objs, time, dt) {
340             R.engine.Support.forEach(objs, function (e) {
341                 this.renderObject(e, time, dt);
342             }, this);
343         },
344 
345         //------------------------------------------------------------------------
346 
347         /**
348          * Set the background color of the context.
349          *
350          * @param color {String} An HTML color
351          */
352         setBackgroundColor:function (color) {
353             this.backgroundColor = color;
354         },
355 
356         /**
357          * Get the color assigned to the context background.
358          * @return {String}
359          */
360         getBackgroundColor:function () {
361             return this.backgroundColor;
362         },
363 
364         /**
365          * Set the width of the context drawing area.
366          *
367          * @param width {Number} The width in pixels
368          */
369         setWidth:function (width) {
370             this.width = width;
371         },
372 
373         /**
374          * Get the width of the context drawing area.
375          * @return {Number}
376          */
377         getWidth:function () {
378             return this.width;
379         },
380 
381         /**
382          * Set the height of the context drawing area
383          *
384          * @param height {Number} The height in pixels
385          */
386         setHeight:function (height) {
387             this.height = height;
388         },
389 
390         /**
391          * Get the height of the context drawing area.
392          * @render {Number}
393          */
394         getHeight:function () {
395             return this.height;
396         },
397 
398         /**
399          * Get the bounding box for the rendering context.
400          * @return {R.math.Rectangle2D}
401          */
402         getBoundingBox:function () {
403             if (!this.bBox) {
404                 this.bBox = R.math.Rectangle2D.create(0, 0, this.getWidth(), this.getHeight());
405             }
406             return this.bBox;
407         },
408 
409         /**
410          * Set the current transform position (translation) relative to the viewport.
411          *
412          * @param point {R.math.Point2D} The translation
413          */
414         setPosition:function (point) {
415             this.position.add(point);
416         },
417 
418         /**
419          * Get the current transform position (translation) relative to the viewport.
420          *
421          * @return {R.math.Point2D}
422          */
423         getPosition:function () {
424             return this.position;
425         },
426 
427         /**
428          * Set the rotation angle of the current transform.
429          *
430          * @param angle {Number} An angle in degrees
431          */
432         setRotation:function (angle) {
433             this.rotation = angle;
434         },
435 
436         /**
437          * Get the current transform rotation.
438          * @return {Number}
439          */
440         getRotation:function () {
441             return this.rotation;
442         },
443 
444         /**
445          * Set the scale of the current transform.  Specifying
446          * only the first parameter implies a uniform scale.
447          *
448          * @param scaleX {Number} The X scaling factor, with 1 being 100%
449          * @param scaleY {Number} The Y scaling factor
450          */
451         setScale:function (scaleX, scaleY) {
452             this.scaleX = scaleX;
453             this.scaleY = scaleY || scaleX;
454         },
455 
456         /**
457          * Get the X scaling factor of the current transform.
458          * @return {Number}
459          */
460         getScaleX:function () {
461             return this.scaleX;
462         },
463 
464         /**
465          * Get the Y scaling factor of the current transform.
466          * @return {Number}
467          */
468         getScaleY:function () {
469             return this.scaleY;
470         },
471 
472         /**
473          * Push the current transformation matrix.
474          */
475         pushTransform:function () {
476             // Translation
477             var p = R.clone(this.getWorldPosition()).add(this.getPosition());
478             var tMtx = $M([
479                 [1, 0, p.x],
480                 [0, 1, p.y],
481                 [0, 0, 1]
482             ]);
483 
484             // Rotation
485             var a = this.getWorldRotation() + this.getRotation();
486             var rMtx;
487             if (a != 0) {
488                 // Rotate
489                 rMtx = Matrix.Rotation(R.math.Math2D.degToRad(a), R.rendercontexts.RenderContext2D.ROTATION_AXIS);
490             }
491             else {
492                 // Set to identity
493                 rMtx = R.math.Math2D.identityMatrix();
494             }
495 
496             // Scale
497             var sX = this.getWorldScale() * this.getScaleX(), sY = this.getWorldScale() * this.getScaleY(),
498                 sMtx = $M([
499                     [sX, 0, 0],
500                     [0, sY, 0],
501                     [0, 0, 1]
502                 ]), txfmMtx = tMtx.multiply(rMtx).multiply(sMtx);
503 
504             rMtx = null;
505             sMtx = null;
506             this._xformStack.push(txfmMtx);
507 
508             this.base();
509         },
510 
511         /**
512          * Pop the current transformation matrix.
513          */
514         popTransform:function () {
515             // Restore the last position, (TODO: angle, and scale)
516             var xform = this._xformStack.pop().col(3);
517             this.position.set(xform.e(1), xform.e(2));
518 
519             this.base();
520         },
521 
522         /**
523          * Set the font to use when rendering text to the context.
524          * @param font {String} A font string similar to CSS
525          */
526         setFont:function (font) {
527             this.font = font;
528         },
529 
530         /**
531          * Get the font currently being used to render text
532          * @return {String}
533          */
534         getFont:function () {
535             return this.font;
536         },
537 
538         /**
539          * Get the normalized font string used to describe the style. The
540          * value includes style, weight, size, and font.
541          * @return {String}
542          */
543         getNormalizedFont:function () {
544             return this.getFontStyle() + " " + this.getFontWeight() + " " + this.getFontSize() + " " + this.getFont();
545         },
546 
547         /**
548          * Set the size of the font being used to render text
549          * @param size {String} The font size string
550          */
551         setFontSize:function (size) {
552             this.fontSize = size + "px";
553         },
554 
555         /**
556          * Get the font size
557          * @return {String}
558          */
559         getFontSize:function () {
560             return this.fontSize;
561         },
562 
563         /**
564          * Set the rendering weight of the font
565          * @param weight {String}
566          */
567         setFontWeight:function (weight) {
568             this.fontWeight = weight;
569         },
570 
571         /**
572          * Get the weight of the font to be rendered to the context
573          * @return {String}
574          */
575         getFontWeight:function () {
576             return this.fontWeight;
577         },
578 
579         /**
580          * Set the font alignment for the context
581          * @param align {String} The font alignment
582          */
583         setFontAlign:function (align) {
584             this.fontAlign = align;
585         },
586 
587         /**
588          * Get the alignment of the font
589          * @return {String}
590          */
591         getFontAlign:function () {
592             return this.fontAlign;
593         },
594 
595         /**
596          * Set the baseline of the renderable font
597          * @param baseline {String} The render baseline
598          */
599         setFontBaseline:function (baseline) {
600             this.fontBaseline = baseline;
601         },
602 
603         /**
604          * Get the font baseline
605          * @return {String}
606          */
607         getFontBaseline:function () {
608             return this.fontBaseline;
609         },
610 
611         /**
612          * Set the style of the renderable font
613          * @param style {String} The font style
614          */
615         setFontStyle:function (style) {
616             this.fontStyle = style;
617         },
618 
619         /**
620          * Get a rectangle that will approximately enclose the text drawn by the render context.
621          * @param text {String} The text to measure
622          * @return {R.math.Rectangle2D}
623          */
624         getTextMetrics:function (text) {
625             return R.math.Rectangle2D.create(0, 0, 1, 1);
626         },
627 
628         /**
629          * Get the renderable style of the font
630          * @return {String}
631          */
632         getFontStyle:function () {
633             return this.fontStyle;
634         },
635 
636         /**
637          * Set the current transformation using a matrix.  Replaces the
638          * current transformation at the top of the stack.
639          * @param matrix {Matrix} The transformation matrix
640          */
641         setTransform:function (matrix) {
642             this._xformStack[this._xformStack.length - 1] = matrix;
643         },
644 
645         /**
646          * Get the current transformation matrix.
647          * @return {Matrix}
648          */
649         getTransform:function () {
650             return this._xformStack[this._xformStack.length - 1];
651         },
652 
653         /**
654          * Set the transformation of the world.
655          *
656          * @param position {R.math.Point2D}
657          * @param rotation {Number}
658          * @param scale {Number}
659          */
660         setRenderTransform:function (mtx3) {
661         },
662 
663         /**
664          * Get the render position relative to the world
665          * @return {R.math.Point2D}
666          */
667         getRenderPosition:function () {
668             return R.math.Point2D.ZERO;
669         },
670 
671         /**
672          * Get the render rotation relative to the world
673          * @return {Number}
674          */
675         getRenderRotation:function () {
676             return 0;
677         },
678 
679         /**
680          * Get the render scale relative to the world
681          * @return {Number}
682          */
683         getRenderScale:function () {
684             return 1.0;
685         },
686 
687         /**
688          * Set the line style for the context.
689          *
690          * @param lineStyle {String} An HTML color or <tt>null</tt>
691          */
692         setLineStyle:function (lineStyle) {
693             this.lineStyle = lineStyle;
694         },
695 
696         /**
697          * Get the current line style for the context.  <tt>null</tt> if
698          * not set.
699          * @return {String}
700          */
701         getLineStyle:function () {
702             return this.lineStyle;
703         },
704 
705         /**
706          * Set the line width for drawing paths.
707          *
708          * @param [width=1] {Number} The width of lines in pixels
709          */
710         setLineWidth:function (width) {
711             this.lineWidth = width;
712         },
713 
714         /**
715          * Get the current line width for drawing paths.
716          * @return {Number}
717          */
718         getLineWidth:function () {
719             return this.lineWidth;
720         },
721 
722         /**
723          * Set the fill style of the context.
724          *
725          * @param fillStyle {String} An HTML color, or <tt>null</tt>.
726          */
727         setFillStyle:function (fillStyle) {
728             this.fillStyle = fillStyle;
729         },
730 
731         /**
732          * Get the current fill style of the context.
733          * @return {String}
734          */
735         getFillStyle:function () {
736             return this.fillStyle;
737         },
738 
739         /**
740          * Draw an un-filled rectangle on the context.
741          *
742          * @param rect {R.math.Rectangle2D} The rectangle to draw
743          * @param [ref] {R.engine.GameObject} A reference game object
744          */
745         drawRectangle:function (rect /*, ref */) {
746         },
747 
748         /**
749          * Draw a filled rectangle on the context.
750          *
751          * @param rect {R.math.Rectangle2D} The rectangle to draw
752          * @param [ref] {R.engine.GameObject} A reference game object
753          */
754         drawFilledRectangle:function (rect /*, ref */) {
755         },
756 
757         /**
758          * Draw an un-filled arc on the context.  Arcs are drawn in clockwise
759          * order.
760          *
761          * @param point {R.math.Point2D} The point around which the arc will be drawn
762          * @param radius {Number} The radius of the arc in pixels
763          * @param startAngle {Number} The starting angle of the arc in degrees
764          * @param endAngle {Number} The end angle of the arc in degrees
765          * @param [ref] {R.engine.GameObject} A reference game object
766          */
767         drawArc:function (point, radius, startAngle, endAngle /*, ref */) {
768         },
769 
770         /**
771          * Draw a filled arc on the context.  Arcs are drawn in clockwise
772          * order.
773          *
774          * @param point {R.math.Point2D} The point around which the arc will be drawn
775          * @param radius {Number} The radius of the arc in pixels
776          * @param startAngle {Number} The starting angle of the arc in degrees
777          * @param endAngle {Number} The end angle of the arc in degrees
778          * @param [ref] {R.engine.GameObject} A reference game object
779          */
780         drawFilledArc:function (point, radius, startAngle, endAngle /*, ref */) {
781         },
782 
783         /**
784          * Helper method to draw a circle by calling the {@link #drawArc} method
785          * with predefined start and end angle of zero and 6.28 radians.
786          *
787          * @param point {R.math.Point2D} The point around which the circle will be drawn
788          * @param radius {Number} The radius of the circle in pixels
789          * @param [ref] {R.engine.GameObject} A reference game object
790          */
791         drawCircle:function (point, radius /*, ref */) {
792             this.drawArc(point, radius, 0, R.math.Math2D.TWO_PI);
793         },
794 
795         /**
796          * Helper method to draw a filled circle by calling the {@link #drawFilledArc} method
797          * with predefined start and end angle of zero and 6.28 radians.
798          *
799          * @param point {R.math.Point2D} The point around which the circle will be drawn
800          * @param radius {Number} The radius of the circle in pixels
801          * @param [ref] {R.engine.GameObject} A reference game object
802          */
803         drawFilledCircle:function (point, radius /*, ref */) {
804             this.drawFilledArc(point, radius, 0, R.math.Math2D.TWO_PI);
805         },
806 
807         /**
808          * Draw a polygon or polyline using a Duff's device for
809          * efficiency and loop unrolling with inversion for speed.
810          *
811          * @param pointArray {Array} An array of <tt>R.math.Point2D</tt> objects
812          * @param closedLoop {Boolean} <tt>true</tt> to close the polygon
813          * @private
814          */
815         _poly:function (pointArray, closedLoop) {
816             this.startPath();
817             this.moveTo(pointArray[0]);
818             var p = 1;
819 
820             // Using Duff's device with loop inversion
821             switch ((pointArray.length - 1) & 0x3) {
822                 case 3:
823                     this.lineSeg(pointArray[p++]);
824                 case 2:
825                     this.lineSeg(pointArray[p++]);
826                 case 1:
827                     this.lineSeg(pointArray[p++]);
828             }
829 
830             if (p < pointArray.length) {
831                 do
832                 {
833                     this.lineSeg(pointArray[p++]);
834                     this.lineSeg(pointArray[p++]);
835                     this.lineSeg(pointArray[p++]);
836                     this.lineSeg(pointArray[p++]);
837                 } while (p < pointArray.length);
838             }
839 
840             if (closedLoop) {
841                 this.endPath();
842             }
843         },
844 
845         /**
846          * Draw an un-filled regular polygon with N sides.
847          *
848          * @param sides {Number} The number of sides, must be more than 2
849          * @param center {R.math.Point2D} The center of the polygon
850          * @param [radius] {Number} The radius of the polygon. Default: 100
851          */
852         drawRegularPolygon:function (sides, center, radius) {
853             var poly = R.math.Math2D.regularPolygon(sides, radius);
854             for (var p = 0; p < poly.length; p++) {
855                 poly[p].add(center);
856             }
857             this.drawPolygon(poly);
858         },
859 
860         /**
861          * Draw an un-filled polygon on the context.
862          *
863          * @param pointArray {Array} An array of {@link R.math.Point2D} objects
864          * @param [ref] {R.engine.GameObject} A reference game object
865          */
866         drawPolygon:function (pointArray /*, ref */) {
867             this._poly(pointArray, true);
868             this.strokePath();
869             this.lineSeg.moveTo = false;
870         },
871 
872         /**
873          * Draw a non-closed poly line on the context.
874          *
875          * @param pointArray {Array} An array of {@link Point2D} objects
876          * @param [ref] {R.engine.GameObject} A reference game object
877          */
878         drawPolyline:function (pointArray /*, ref */) {
879             this._poly(pointArray, false);
880             this.strokePath();
881             this.lineSeg.moveTo = false;
882         },
883 
884         /**
885          * Draw an filled polygon on the context.
886          *
887          * @param pointArray {Array} An array of {@link R.math.Point2D} objects
888          * @param [ref] {R.engine.GameObject} A reference game object
889          */
890         drawFilledPolygon:function (pointArray /*, ref */) {
891             this._poly(pointArray, true);
892             this.fillPath();
893             this.lineSeg.moveTo = false;
894         },
895 
896         /**
897          * Draw an un-filled regular polygon with N sides.
898          *
899          * @param sides {Number} The number of sides, must be more than 2
900          * @param center {R.math.Point2D} The center of the polygon
901          * @param [radius] {Number} The radius of the polygon. Default: 100
902          */
903         drawFilledRegularPolygon:function (sides, center, radius) {
904             var poly = R.math.Math2D.regularPolygon(sides, radius);
905             for (var p = 0; p < poly.length; p++) {
906                 poly[p].add(center);
907             }
908             this.drawFilledPolygon(poly);
909         },
910 
911         /**
912          * Draw a line on the context.
913          *
914          * @param point1 {R.math.Point2D} The start of the line
915          * @param point2 {R.math.Point2D} The end of the line
916          * @param [ref] {R.engine.GameObject} A reference game object
917          */
918         drawLine:function (point1, point2 /*, ref */) {
919         },
920 
921         /**
922          * Draw a point on the context.
923          *
924          * @param point {R.math.Point2D} The position to draw the point
925          * @param [ref] {R.engine.GameObject} A reference game object
926          */
927         drawPoint:function (point /*, ref */) {
928         },
929 
930         /**
931          * Draw a sprite on the context.
932          *
933          * @param sprite {R.resources.types.Sprite} The sprite to draw
934          * @param time {Number} The current world time
935          * @param dt {Number} The delta between the world time and the last time the world was updated
936          *          in milliseconds.
937          * @param [ref] {R.engine.GameObject} A reference game object
938          */
939         drawSprite:function (sprite, time, dt /*, ref */) {
940         },
941 
942         /**
943          * Draw an image on the context.
944          *
945          * @param rect {R.math.Rectangle2D} The rectangle that specifies the position and
946          *             dimensions of the image rectangle.
947          * @param image {Object} The image to draw onto the context
948          * @param [srcRect] {R.math.Rectangle2D} <i>[optional]</i> The source rectangle within the image, if
949          *                <tt>null</tt> the entire image is used
950          * @param [ref] {R.engine.GameObject} A reference game object
951          */
952         drawImage:function (rect, image, srcRect /*, ref */) {
953         },
954 
955         /**
956          * Capture an image from the context.
957          *
958          * @param rect {R.math.Rectangle2D} The area to capture
959          * @return {ImageData} Image data capture
960          */
961         getImage:function (rect) {
962         },
963 
964         /**
965          * Draw an image, captured with {@link #getImage}, to
966          * the context.
967          *
968          * @param imageData {ImageData} Image data captured
969          * @param point {R.math.Point2D} The poisition at which to draw the image
970          * @param [ref] {R.engine.GameObject} A reference game object
971          */
972         putImage:function (imageData, point /*, ref */) {
973         },
974 
975         /**
976          * Draw text on the context.
977          *
978          * @param point {R.math.Point2D} The top-left position to draw the image.
979          * @param text {String} The text to draw
980          * @param [ref] {R.engine.GameObject} A reference game object
981          */
982         drawText:function (point, text /*, ref */) {
983         },
984 
985         /**
986          * Start a path.
987          */
988         startPath:function () {
989         },
990 
991         /**
992          * End a path.
993          */
994         endPath:function () {
995         },
996 
997         /**
998          * Stroke a path using the current line style and width.
999          */
1000         strokePath:function () {
1001         },
1002 
1003         /**
1004          * Fill a path using the current fill style.
1005          */
1006         fillPath:function () {
1007         },
1008 
1009         /**
1010          * Move the current path to the point sepcified.
1011          *
1012          * @param point {R.math.Point2D} The point to move to
1013          */
1014         moveTo:function (point) {
1015         },
1016 
1017         /**
1018          * Draw a line from the current point to the point specified.
1019          *
1020          * @param point {R.math.Point2D} The point to draw a line to
1021          */
1022         lineTo:function (point) {
1023         },
1024 
1025         /**
1026          * Used to draw line segments for polylines.  If <tt>point</tt>
1027          * is <tt>null</tt>, the context will move to the next point.  Otherwise,
1028          * it will draw a line to the point.
1029          *
1030          * @param point {R.math.Point2D} The point to draw a line to, or null.
1031          */
1032         lineSeg:function (point) {
1033             if (point == null) {
1034                 this.lineSeg.moveTo = true;
1035                 return;
1036             }
1037 
1038             if (this.lineSeg.moveTo) {
1039                 // Cannot have two subsequent nulls
1040                 Assert((point != null), "LineSeg repeated null!", this);
1041                 this.moveTo(point);
1042                 this.lineSeg.moveTo = false;
1043             }
1044             else {
1045                 this.lineTo(point);
1046             }
1047         },
1048 
1049         /**
1050          * Draw a quadratic curve from the current point to the specified point.
1051          *
1052          * @param cPoint {R.math.Point2D} The control point
1053          * @param point {R.math.Point2D} The point to draw to
1054          */
1055         quadraticCurveTo:function (cPoint, point) {
1056         },
1057 
1058         /**
1059          * Draw a bezier curve from the current point to the specified point.
1060          *
1061          * @param cPoint1 {R.math.Point2D} Control point 1
1062          * @param cPoint2 {R.math.Point2D} Control point 2
1063          * @param point {R.math.Point2D} The point to draw to
1064          */
1065         bezierCurveTo:function (cPoint1, cPoint2, point) {
1066         },
1067 
1068         /**
1069          * Draw an arc from the current point to the specified point.
1070          *
1071          * @param point1 {R.math.Point2D} Arc point 1
1072          * @param point2 {R.math.Point2D} Arc point 2
1073          * @param radius {Number} The radius of the arc
1074          */
1075         arcTo:function (point1, point2, radius) {
1076         },
1077 
1078         /**
1079          * Draw an element on the context
1080          * @param ref {R.engine.GameObject} A reference game object
1081          */
1082         drawElement:function (ref) {
1083         }
1084 
1085     }, /** @scope R.rendercontexts.RenderContext2D.prototype */{
1086         /**
1087          * Get the class name of this object
1088          *
1089          * @return {String} "R.rendercontexts.RenderContext2D"
1090          */
1091         getClassName:function () {
1092             return "R.rendercontexts.RenderContext2D";
1093         },
1094 
1095         /**
1096          * Sort the objects to draw from objects with the lowest
1097          * z-index to the highest z-index.
1098          * @static
1099          */
1100         sortFn:function (obj1, obj2) {
1101             if (obj1.getZIndex && obj2.getZIndex) {
1102                 return obj1.getZIndex() - obj2.getZIndex();
1103             }
1104             return 0
1105         },
1106 
1107         /**
1108          * Bold text weight
1109          * @type {String}
1110          */
1111         FONT_WEIGHT_BOLD:"bold",
1112 
1113         /**
1114          * Normal text weight
1115          * @type {String}
1116          */
1117         FONT_WEIGHT_NORMAL:"normal",
1118 
1119         /**
1120          * Light text weight
1121          * @type {String}
1122          */
1123         FONT_WEIGHT_LIGHT:"light",
1124 
1125         /**
1126          * Text align left
1127          * @type {String}
1128          */
1129         FONT_ALIGN_LEFT:"left",
1130 
1131         /**
1132          * Text align right
1133          * @type {String}
1134          */
1135         FONT_ALIGN_RIGHT:"right",
1136 
1137         /**
1138          * Text align center
1139          * @type {String}
1140          */
1141         FONT_ALIGN_CENTER:"center",
1142 
1143         /**
1144          * Text align start of stroke
1145          * @type {String}
1146          */
1147         FONT_ALIGN_START:"start",
1148 
1149         /**
1150          * Text align end of stroke
1151          * @type {String}
1152          */
1153         FONT_ALIGN_END:"end",
1154 
1155         /**
1156          * Text baseline alphabetic
1157          * @type {String}
1158          */
1159         FONT_BASELINE_ALPHABETIC:"alphabetic",
1160 
1161         /**
1162          * Text baseline top of em box
1163          * @type {String}
1164          */
1165         FONT_BASELINE_TOP:"top",
1166 
1167         /**
1168          * Text baseline hanging ideograph
1169          * @type {String}
1170          */
1171         FONT_BASELINE_HANGING:"hanging",
1172 
1173         /**
1174          * Text baseline middle of em square
1175          * @type {String}
1176          */
1177         FONT_BASELINE_MIDDLE:"middle",
1178 
1179         /**
1180          * Text baseline ideographic bottom
1181          * @type {String}
1182          */
1183         FONT_BASELINE_IDEOGRAPHIC:"ideographic",
1184 
1185         /**
1186          * Text baseline bottom of em square
1187          * @type {String}
1188          */
1189         FONT_BASELINE_BOTTOM:"bottom",
1190 
1191         /**
1192          * Text style italic
1193          * @type {String}
1194          */
1195         FONT_STYLE_ITALIC:"italic",
1196 
1197         /**
1198          * Text style normal
1199          * @type {String}
1200          */
1201         FONT_STYLE_NORMAL:"normal",
1202 
1203         /**
1204          * Text style oblique
1205          * @type {String}
1206          */
1207         FONT_STYLE_OBLIQUE:"oblique",
1208 
1209         /**
1210          * @private
1211          */
1212         NO_ZBIN:0xDEADBEEF,
1213 
1214         /**
1215          * @private
1216          */
1217         ROTATION_AXIS:$V([0, 0, 1])
1218     });
1219 
1220 };
1221