1 /**
  2   This View is used by Greenhouse when application is in design mode
  3   This is a Drawing View:
  4   If you want to draw a new shape you can pass in the information:
  5   For a Line:
  6 
  7       {
  8         +shape: SC.LINE,
  9         +start: {x: 0, y: 0},
 10         +end: {x: 100, y: 100},
 11         ?style: {
 12           ?width: 5,
 13           ?color: 'orange' | '#FFA500' | 'rgb(255,165,0)' | 'rgba(255,165,0,1)'
 14           ?transparency: 0.2
 15         }
 16       }
 17 
 18   For a Rectangle:
 19 
 20       {
 21         +shape: SC.RECT,
 22         +start: {x: 0, y: 0},
 23         +size: {width: 100, height: 100},
 24         ?type: SC.FILL | SC.STROKE
 25         ?style: {
 26           ?width: 5,
 27           ?color: 'orange' | '#FFA500' | 'rgb(255,165,0)' | 'rgba(255,165,0,1)'
 28           ?transparency: 0.2
 29         }
 30       }
 31 
 32   For a Circle:
 33 
 34       {
 35         +shape: SC.CIRCLE,
 36         +center: {x: 0, y: 0},
 37         +radius: 20,
 38         ?type: SC.FILL | SC.STROKE
 39         ?style: {
 40           ?width: 5,
 41           ?color: 'orange' | '#FFA500' | 'rgb(255,165,0)' | 'rgba(255,165,0,1)'
 42           ?transparency: 0.2
 43         }
 44       }
 45 
 46   For a Polygon:
 47 
 48       {
 49         +shape: SC.POLY
 50         +path: [
 51           +{x: 0, y: 0},
 52           +{x: 10, y: 10},
 53           ?{x: 0, y: 50}
 54         ],
 55         ?type: SC.FILL | SC.STROKE
 56         ?style: {
 57           ?width: 5,
 58           ?color: 'orange' | '#FFA500' | 'rgb(255,165,0)' | 'rgba(255,165,0,1)'
 59           ?transparency: 0.2
 60         }
 61       }
 62 
 63   @author Evin Grano
 64   @extends SC.View
 65   @since SproutCore 1.0
 66 */
 67 SC.LINE = 'line';
 68 SC.RECT = 'rect';
 69 SC.CIRCLE = 'circle';
 70 SC.POLY = 'poly';
 71 
 72 SC.FILL = 'fill';
 73 SC.STROKE = 'stroke';
 74 
 75 
 76 SC.DrawingView = SC.View.extend({
 77 
 78   classNames: 'scui-drawing-view',
 79 
 80   shapes: [],
 81 
 82   _drawingManager: {},
 83 
 84   shapesDidChange: function(){
 85     this.set('layerNeedsUpdate', YES);
 86     this.updateLayerIfNeeded();
 87   }.observes('*shapes.[]'),
 88 
 89   init: function(){
 90     sc_super();
 91 
 92     // Register Basic Shapes
 93 
 94     // Drawing a Line
 95     this.registerShapeDrawing( SC.LINE, function(ctx, params){
 96       if (params.style){
 97         if (params.style.width) ctx.lineWidth = params.style.width;
 98         if (params.style.color) ctx.strokeStyle = params.style.color;
 99         if (params.style.transparency) ctx.globalAlpha = params.style.transparency;
100       }
101       ctx.beginPath();
102       ctx.moveTo(params.start.x, params.start.y);
103       ctx.lineTo(params.end.x, params.end.y);
104       ctx.stroke();
105     });
106 
107     // Drawing a Rectangle
108     this.registerShapeDrawing( SC.RECT, function(ctx, params){
109       if (params.style){
110         if (params.style.width) ctx.lineWidth = params.style.width;
111         if (params.style.color) ctx.fillStyle =  ctx.strokeStyle = params.style.color;
112         if (params.style.transparency) ctx.globalAlpha = params.style.transparency;
113       }
114       switch(params.type){
115         case SC.FILL:
116           ctx.fillRect(params.start.x, params.start.y, params.size.width, params.size.height);
117           break;
118         case SC.STROKE:
119           ctx.strokeRect(params.start.x, params.start.y, params.size.width, params.size.height);
120           break;
121         default:
122           ctx.clearRect(params.start.x, params.start.y, params.size.width, params.size.height);
123           break;
124       }
125     });
126 
127     // Drawing a Circle
128     this.registerShapeDrawing( SC.CIRCLE, function(ctx, params){
129       if (params.style){
130         if (params.style.width) ctx.lineWidth = params.style.width;
131         if (params.style.color) ctx.fillStyle =  ctx.strokeStyle = params.style.color;
132         if (params.style.transparency) ctx.globalAlpha = params.style.transparency;
133       }
134       ctx.beginPath();
135       ctx.arc(params.center.x,params.center.y,params.radius,0,Math.PI*2,true);
136       if (params.type === SC.FILL) ctx.fill();
137       else ctx.stroke();
138     });
139 
140     // Drawing a Polygon
141     this.registerShapeDrawing( SC.POLY, function(ctx, params){
142       if (params.style){
143         if (params.style.width) ctx.lineWidth = params.style.width;
144         if (params.style.color) ctx.fillStyle =  ctx.strokeStyle = params.style.color;
145         if (params.style.transparency) ctx.globalAlpha = params.style.transparency;
146       }
147       ctx.beginPath();
148       var len = params.path ? params.path.length : 0;
149       if (len < 2) return;
150 
151       var path = params.path, curr;
152       ctx.moveTo(path[0].x, path[0].y);
153       for(var i = 1; i < len; i++){
154         curr = path[i];
155         ctx.lineTo(curr.x, curr.y);
156       }
157       ctx.lineTo(path[0].x, path[0].y);
158       if (params.type === SC.FILL) ctx.fill();
159       else ctx.stroke();
160     });
161   },
162 
163   render: function(context, firstTime) {
164     //console.log('%@.render()'.fmt(this));
165     var frame = this.get('frame');
166     if (firstTime) {
167       if (!SC.browser.isIE) {
168         context.push('<canvas class="base-layer" width="%@" height="%@">You can\'t use canvas tags</canvas>'.fmt(frame.width, frame.height));
169       }
170     }
171     else {
172       var canvasElem = this.$('canvas.base-layer');
173       if (canvasElem) {
174         canvasElem.attr('width', frame.width);
175         canvasElem.attr('height', frame.height);
176         if (canvasElem.length > 0) {
177           var cntx = canvasElem[0].getContext('2d'); // Get the actual canvas object context
178           if (cntx) {
179             cntx.clearRect(0, 0, frame.width, frame.height);
180             this._drawShapes(cntx);
181           }
182           else {
183             SC.Logger.error("SC.DrawingView.render(): Canvas object context is not accessible.");
184           }
185         }
186         else {
187           SC.Logger.error("SC.DrawingView.render(): Canvas element array length is zero.");
188         }
189       }
190       else {
191         SC.Logger.error("SC.DrawingView.render(): Canvas element is not accessible.");
192       }
193     }
194 
195     return sc_super();
196   },
197 
198   registerShapeDrawing: function(name, drawingFunction){
199     if (!name) {
200       SC.Logger.error('Can\'t register this drawing paradigm because name is null');
201       return NO;
202     }
203 
204     // OK, create the drawing paradigm
205     this._drawingManager[name] = drawingFunction;
206     this.set('layerNeedsUpdate', YES);
207     this.updateLayerIfNeeded();
208     return YES;
209   },
210 
211   /**
212     @private
213 
214     Function for actually drawing the shapes that we have listed
215   */
216   _drawShapes: function(cntx){
217     var curr;
218     var shapes = this.get('shapes');
219     var drawingFunc;
220     for (var i=0,len=shapes.length;i<len;i++){
221       curr = shapes[i];
222       drawingFunc = this._drawingManager[curr.shape];
223       if (drawingFunc) drawingFunc(cntx, curr);
224     }
225   }
226 });
227