Team:Evry/arbor/src/graphics/graphics.js

From 2012.igem.org

Revision as of 18:00, 18 September 2012 by TriCer (Talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

// // graphics.js // // Created by Christian Swinehart on 2010-12-07. // Copyright (c) 2011 Samizdat Drafting Co. All rights reserved. //

var Graphics = function(canvas){

 var dom = $(canvas)
 var ctx = $(dom).get(0).getContext('2d')
 var _bounds = null
 var _colorMode = "rgb" // vs hsb
 var _coordMode = "origin" // vs "center"
 var _drawLibrary = {}
 var _drawStyle = {background:null, 
                   fill:null, 
                   stroke:null,
                   width:0}
 var _fontLibrary = {}
 var _fontStyle = {font:"sans-serif",
                  size:12, 
                  align:"left",
                  color:Colors.decode("black"),
                  alpha:1,
                  baseline:"ideographic"}
 var _lineBuffer = [] // calls to .lines sit here until flushed by .drawlines
 
 ///MACRO:primitives-start
 var primitives = Primitives(ctx, _drawStyle, _fontStyle)
 var _Oval = primitives._Oval
 var _Rect = primitives._Rect
 var _Color = primitives._Color
 var _Path = primitives._Path
 ///MACRO:primitives-end    


 // drawStyle({background:"color" or {r,g,b,a}, 
 //            fill:"color" or {r,g,b,a}, 
 //            stroke:"color" or {r,g,b,a}, 
 //            alpha:<number>, 
 //            weight:<number>})



 var that = {
   init:function(){
     if (!ctx) return null
     return that
   },
   // canvas-wide settings
   size:function(width,height){
     if (!isNaN(width) && !isNaN(height)){
       dom.attr({width:width,height:height})
       
       // if (_drawStyle.fill!==null) that.fill(_drawStyle.fill)
       // if (_drawStyle.stroke!==null) that.stroke(_drawStyle.stroke)
       // that.textStyle(_fontStyle)
       
       // trace(_drawStyle,_fontStyle)
     }
     return {width:dom.attr('width'), height:dom.attr('height')}
   },
   clear:function(x,y,w,h){
     if (arguments.length<4){
       x=0; y=0
       w=dom.attr('width')
       h=dom.attr('height')
     }
     
     ctx.clearRect(x,y,w,h)
     if (_drawStyle.background!==null){
       ctx.save()
       ctx.fillStyle = Colors.encode(_drawStyle.background)
       ctx.fillRect(x,y,w,h)
       ctx.restore()
     }
   },
   background:function(a,b,c,d){
     if (a==null){
       _drawStyle.background = null
       return null
     }
     
     var fillColor = Colors.decode(a,b,c,d)
     if (fillColor){
       _drawStyle.background = fillColor
       that.clear()
     }
   },


   // drawing to screen
   noFill:function(){
     _drawStyle.fill = null
   },
   fill:function(a,b,c,d){
     if (arguments.length==0){
       return _drawStyle.fill
     }else if (arguments.length>0){
       var fillColor = Colors.decode(a,b,c,d)
       _drawStyle.fill = fillColor
       ctx.fillStyle = Colors.encode(fillColor)
     }
   },
   
   noStroke:function(){
     _drawStyle.stroke = null
     ctx.strokeStyle = null
   },
   stroke:function(a,b,c,d){
     if (arguments.length==0 && _drawStyle.stroke!==null){
       return _drawStyle.stroke
     }else if (arguments.length>0){
       var strokeColor = Colors.decode(a,b,c,d)
       _drawStyle.stroke = strokeColor
       ctx.strokeStyle = Colors.encode(strokeColor)
     }
   },
   strokeWidth:function(ptsize){
     if (ptsize===undefined) return ctx.lineWidth
     ctx.lineWidth = _drawStyle.width = ptsize
   },
   
   
   
   Color:function(clr){
     return new _Color(clr)
   },


   // Font:function(fontName, pointSize){
   //   return new _Font(fontName, pointSize)
   // },
   // font:function(fontName, pointSize){
   //   if (fontName!==undefined) _fontStyle.font = fontName
   //   if (pointSize!==undefined) _fontStyle.size = pointSize
   //   ctx.font = nano("{size}px {font}", _fontStyle)
   // },


   drawStyle:function(style){
     // without arguments, show the current state
     if (arguments.length==0) return objcopy(_drawStyle)
     
     // if this is a ("stylename", {style}) invocation, don't change the current
     // state but add it to the library
     if (arguments.length==2){
       var styleName = arguments[0]
       var styleDef = arguments[1]
       if (typeof styleName=='string' && typeof styleDef=='object'){
         var newStyle = {}
         if (styleDef.color!==undefined){
           var textColor = Colors.decode(styleDef.color)
           if (textColor) newStyle.color = textColor
         }
         $.each('background fill stroke width'.split(' '), function(i, param){
           if (styleDef[param]!==undefined) newStyle[param] = styleDef[param]
         })
         if (!$.isEmptyObject(newStyle)) _drawLibrary[styleName] = newStyle
       }
       return
     }
     
     // if a ("stylename") invocation, load up the selected style
     if (arguments.length==1 && _drawLibrary[arguments[0]]!==undefined){
       style = _drawLibrary[arguments[0]]
     }
           
     // for each of the properties specified, update the canvas state
     if (style.width!==undefined) _drawStyle.width = style.width
     ctx.lineWidth = _drawStyle.width
     
     $.each('background fill stroke',function(i, color){
       if (style[color]!==undefined){
         if (style[color]===null) _drawStyle[color] = null
         else{
           var useColor = Colors.decode(style[color])
           if (useColor) _drawStyle[color] = useColor
         }
       }
     })
     ctx.fillStyle = _drawStyle.fill
     ctx.strokeStyle = _drawStyle.stroke
   },
   textStyle:function(style){
     // without arguments, show the current state
     if (arguments.length==0) return objcopy(_fontStyle)
     
     // if this is a ("name", {style}) invocation, don't change the current
     // state but add it to the library
     if (arguments.length==2){
       var styleName = arguments[0]
       var styleDef = arguments[1]
       if (typeof styleName=='string' && typeof styleDef=='object'){
         var newStyle = {}
         if (styleDef.color!==undefined){
           var textColor = Colors.decode(styleDef.color)
           if (textColor) newStyle.color = textColor
         }
         $.each('font size align baseline alpha'.split(' '), function(i, param){
           if (styleDef[param]!==undefined) newStyle[param] = styleDef[param]
         })
         if (!$.isEmptyObject(newStyle)) _fontLibrary[styleName] = newStyle
       }
       return
     }
     
     if (arguments.length==1 && _fontLibrary[arguments[0]]!==undefined){
       style = _fontLibrary[arguments[0]]
     }
           
     if (style.font!==undefined) _fontStyle.font = style.font
     if (style.size!==undefined) _fontStyle.size = style.size
     ctx.font = nano("{size}px {font}", _fontStyle)
     if (style.align!==undefined){
        ctx.textAlign = _fontStyle.align = style.align
     }
     if (style.baseline!==undefined){
        ctx.textBaseline = _fontStyle.baseline = style.baseline
     }
     if (style.alpha!==undefined) _fontStyle.alpha = style.alpha
     if (style.color!==undefined){
       var textColor = Colors.decode(style.color)
       if (textColor) _fontStyle.color = textColor
     }
     if (_fontStyle.color){
       var textColor = Colors.blend(_fontStyle.color, _fontStyle.alpha)
       if (textColor) ctx.fillStyle = textColor
     }
     // trace(_fontStyle,opts)
   },
   text:function(textStr, x, y, opts){ // opts: x,y, color, font, align, baseline, width
     if (arguments.length>=3 && !isNaN(x)){
       opts = opts || {}
       opts.x = x
       opts.y = y
     }else if (arguments.length==2 && typeof(x)=='object'){
       opts = x
     }else{
       opts = opts || {}
     }
     var style = objmerge(_fontStyle, opts)
     ctx.save()
       if (style.align!==undefined) ctx.textAlign = style.align
       if (style.baseline!==undefined) ctx.textBaseline = style.baseline
       if (style.font!==undefined && !isNaN(style.size)){
         ctx.font = nano("{size}px {font}", style)
       }
       var alpha = (style.alpha!==undefined) ? style.alpha : _fontStyle.alpha
       var color = (style.color!==undefined) ? style.color : _fontStyle.color
       ctx.fillStyle = Colors.blend(color, alpha)
       
       // if (alpha>0) ctx.fillText(textStr, style.x, style.y);        
       if (alpha>0) ctx.fillText(textStr, Math.round(style.x), style.y);        
     ctx.restore()
   },
   textWidth:function(textStr, style){ // style: x,y, color, font, align, baseline, width
     style = objmerge(_fontStyle, style||{})
     ctx.save()
       ctx.font = nano("{size}px {font}", style)
       var width = ctx.measureText(textStr).width			  
     ctx.restore()
     return width
   },
   
   // hasFont:function(fontName){
   //   var testTxt = 'H h H a H m H b H u H r H g H e H r H f H o H n H s H t H i H v H'
   //   ctx.save()
   //   ctx.font = '10px sans-serif'
   //   var defaultWidth = ctx.measureText(testTxt).width
   // 
   //   ctx.font = '10px "'+fontName+'"'
   //   var putativeWidth = ctx.measureText(testTxt).width
   //   ctx.restore()
   //   
   //   // var defaultWidth = that.textWidth(testTxt, {font:"Times New Roman", size:120})
   //   // var putativeWidth = that.textWidth(testTxt, {font:fontName, size:120})
   //   trace(defaultWidth,putativeWidth,ctx.font)
   //   // return (putativeWidth!=defaultWidth || fontName=="Times New Roman")
   //   return putativeWidth!=defaultWidth
   // },
   
   
   // shape primitives.
   // classes will return an {x,y,w,h, fill(), stroke()} object without drawing
   // functions will draw the shape based on current stroke/fill state
   Rect:function(x,y,w,h,r,style){
     return new _Rect(x,y,w,h,r,style)
   },
   rect:function(x, y, w, h, r, style){
     _Rect.prototype._draw(x,y,w,h,r,style)
   },
   
   Oval:function(x, y, w, h, style) {
     return new _Oval(x,y,w,h, style)
   },
   oval:function(x, y, w, h, style) {
     style = style || {}
     _Oval.prototype._draw(x,y,w,h, style)
   },
   
   // draw a line immediately
   line:function(x1, y1, x2, y2, style){
     var p = new _Path(x1,y1,x2,y2)
     p.draw(style)
   },
   
   // queue up a line segment to be drawn in a batch by .drawLines
   lines:function(x1, y1, x2, y2){
     if (typeof y2=='number'){
       // Æ’( x1, y1, x2, y2)
       _lineBuffer.push( [ {x:x1,y:y1}, {x:x2,y:y2} ] )
     }else{
       // Æ’( {x:1, y:1}, {x:2, y:2} )
       _lineBuffer.push( [ x1,y1 ] )
     }
   },
   
   // flush the buffered .lines to screen
   drawLines:function(style){
     var p = new _Path(_lineBuffer)
     p.draw(style)
     _lineBuffer = []
   }
   
 }
 
 return that.init()    

}


// // helpers for figuring out where to draw arrows // var intersect_line_line = function(p1, p2, p3, p4) // { // var denom = ((p4.y - p3.y)*(p2.x - p1.x) - (p4.x - p3.x)*(p2.y - p1.y)); // // // lines are parallel // if (denom === 0) { // return false; // } // // var ua = ((p4.x - p3.x)*(p1.y - p3.y) - (p4.y - p3.y)*(p1.x - p3.x)) / denom; // var ub = ((p2.x - p1.x)*(p1.y - p3.y) - (p2.y - p1.y)*(p1.x - p3.x)) / denom; // // if (ua < 0 || ua > 1 || ub < 0 || ub > 1) { // return false; // } // // return arbor.Point(p1.x + ua * (p2.x - p1.x), p1.y + ua * (p2.y - p1.y)); // } // // var intersect_line_box = function(p1, p2, p3, w, h) // { // var tl = {x: p3.x, y: p3.y}; // var tr = {x: p3.x + w, y: p3.y}; // var bl = {x: p3.x, y: p3.y + h}; // var br = {x: p3.x + w, y: p3.y + h}; // // var result; // if (result = intersect_line_line(p1, p2, tl, tr)) { return result; } // top // if (result = intersect_line_line(p1, p2, tr, br)) { return result; } // right // if (result = intersect_line_line(p1, p2, br, bl)) { return result; } // bottom // if (result = intersect_line_line(p1, p2, bl, tl)) { return result; } // left // // return false; // }