Source: Drawing.js

/**
 * @constructor
 */
hui.ui.Drawing = function(options) {
  this.options = hui.override({width:200,height:200},options);
  this.element = hui.get(options.element);
  hui.log({width:options.width,height:options.height});
  this.svg = hui.ui.Drawing._build({tag:'svg',parent:this.element,attributes:{width:options.width,height:options.height}});
  this.element.appendChild(this.svg);
  this.name = options.name;
  hui.ui.extend(this);
};

hui.ui.Drawing.create = function(options) {
  options = options || {};
  var e = options.element = hui.build('div',{'class':'hui_drawing',style:'position: relative; overflow: hidden;'});
  if (options.height) {
    e.style.height = options.height+'px';
  }
  if (options.width) {
    e.style.width = options.width+'px';
  }
  if (options.parent) {
    hui.get(options.parent).appendChild(e);
  }
  return new hui.ui.Drawing(options);
};

hui.ui.Drawing.prototype = {
  setSize : function(width,height) {
    this.svg.setAttribute('width',width);
    this.svg.setAttribute('height',height);
    this.svg.style.width = width+'px';
    this.svg.style.height = height+'px';
    this.element.style.width = width+'px';
    this.element.style.height = height+'px';
  },
  clear : function() {
    hui.dom.clear(this.svg);
  },
  addLine : function(options) {
    options.parent = this.svg;
    return hui.ui.Drawing.Line.create(options);
  },
  addRect : function(options) {
    options.parent = this.svg;
    return hui.ui.Drawing.Rect.create(options);
  },
  addCircle : function(options) {
    options.parent = this.svg;
    return hui.ui.Drawing.Circle.create(options);
  },
  addArc : function(options) {
    options.parent = this.svg;
    return hui.ui.Drawing.Arc.create(options);
  },
  addElement : function(options) {
    var node = hui.build('div',{style:'position:absolute;left:0;top:0;',parent:this.element,html:options.html}),
      element = new hui.ui.Drawing.Element(node);
    if (options.movable) {
      hui.drag.register({
        element : node,
        onBeforeMove : function(e) {
          this.fire('shapeWillMove',{shape:element,event:e});
        }.bind(this),
        onMove : function(e) {
          node.style.left = e.getLeft()+'px';
          node.style.top = e.getTop()+'px';
          this.fire('shapeMoved',{shape:element,event:e});
        }.bind(this),
        onAfterMove : function(e) {
          this.fire('shapeWasMoved',{shape:element,event:e});
        }.bind(this)
      });
    }
    return element;
  }
};

hui.ui.Drawing._build = function(options) {
  var node = document.createElementNS('http://www.w3.org/2000/svg',options.tag);
  if (options.attributes) {
    for (var att in options.attributes) {
      node.setAttribute(att,options.attributes[att]);
    }
  }
  if (options.parent) {
    options.parent.appendChild(node);
  }
  return node;
};


// Line

hui.ui.Drawing.Line = function(options) {
  this.node = options.node;
  this.endNode = options.endNode;
  this.from = options.from;
  this.to = options.to;
  this._updateEnds();
};

hui.ui.Drawing.Line.create = function(options) {
  if (!options.from) {
    options.from = {x:options.x1,y:options.y1};
  }
  if (!options.to) {
    options.to = {x:options.x2,y:options.y2};
  }

  var attributes = {
    x1 : options.from.x.toFixed(10),
    y1 : options.from.y.toFixed(10),
    x2 : options.to.x.toFixed(10),
    y2 : options.to.y.toFixed(10),
    style : 'stroke:'+(options.color || '#000')+';stroke-width:'+(options.width || 1)
  };

  options.node = hui.ui.Drawing._build({
    tag : 'line',
    parent : options.parent,
    attributes : attributes
  });
  if (options.end) {
    options.endNode = hui.ui.Drawing._build({
      tag : 'path',
      parent : options.parent,
      attributes : {d:'M 0 -1 L 5 10 L -5 10',fill:options.color || '#000'}
    });
  }
  return new hui.ui.Drawing.Line(options);
};

hui.ui.Drawing.Line.prototype = {
  setFrom : function(point) {
    this.from = point;
    this.node.setAttribute('x1',point.x.toFixed(10));
    this.node.setAttribute('y1',point.y.toFixed(10));
    this._updateEnds();
  },
  getFrom : function() {
    return this.from;
  },
  setTo : function(point) {
    this.to = point;
    this.node.setAttribute('x2',point.x.toFixed(10));
    this.node.setAttribute('y2',point.y.toFixed(10));
    this._updateEnds();
  },
  getTo : function() {
    return this.to;
  },
  _updateEnds : function() {
    //var deg = Math.atan((this.from.y-this.to.y) / (this.from.x-this.to.x)) * 180/Math.PI;
    if (this.endNode) {
      var deg = -90 + Math.atan2(this.from.y - this.to.y, this.from.x - this.to.x) * 180 / Math.PI;
      this.endNode.setAttribute('transform','translate('+ ( this.to.x.toFixed(10)) + ',' + ( this.to.y.toFixed(10) ) + ') rotate(' + (deg) + ')');

    }
  },
  getDegree : function() {
    return Math.atan((this.from.y - this.to.y) / (this.from.x - this.to.x)) * 180 / Math.PI;
  }
};



// Circle

hui.ui.Drawing.Circle = function(options) {
  this.node = options.node;
  this.properties = {};
};

hui.ui.Drawing.Circle.create = function(options) {
  var css = [];
  if (options.stroke) {
    if (options.stroke.color) {
      css.push('stroke:' + options.stroke.color);
    }
    if (options.stroke.width) {
      css.push('stroke-width:' + options.stroke.width);
    }
  }
  if (options.fill) {
    css.push('fill:' + options.fill);
  }
  options.node = hui.ui.Drawing._build({
    tag : 'circle',
    parent : options.parent,
    attributes : {
      cx : options.cx,
      cy : options.cy,
      r : options.r,
      style : css.join(';')
    }
  });
  return new hui.ui.Drawing.Circle(options);
};

hui.ui.Drawing.Circle.prototype = {
  setRadius : function(radius) {
    this.node.setAttribute("r", radius);
  },

  setCenter : function(point) {
    this.node.setAttribute('cx', point.x);
    this.node.setAttribute('cy', point.y);
  }
};



// Rect

hui.ui.Drawing.Rect = function(options) {
  this.node = options.node;
};

hui.ui.Drawing.Rect.create = function(options) {
  var css = [];
  if (options.stroke) {
    if (options.stroke.color) {
      css.push('stroke:'+options.stroke.color);
    }
    if (options.stroke.width) {
      css.push('stroke-width:'+options.stroke.width);
    }
  }
  if (options.fill) {
    css.push('fill:'+options.fill);
  }
  options.node = hui.ui.Drawing._build({
    tag : 'rect',
    parent : options.parent,
    attributes : {
      x : options.x,
      y : options.y,
      width : options.width,
      height : options.height,
      style : css.join(';')
    }
  });
  return new hui.ui.Drawing.Circle(options);
};

hui.ui.Drawing.Rect.prototype = {
  setPosition : function(point) {
    this.node.setAttribute('x',point.x);
    this.node.setAttribute('y',point.y);
  }
};


// Arc
hui.ui.Drawing.Arc = function(options) {
  this.node = options.node;
  this.options = hui.override({
    center : {x:100,y:100},
    startDegrees : 0,
    endDegrees : 0,
    innerRadius : 0,
    outerRadius : 0,
    skew: 0,
    fill : '#eee'
  },options);
  this._redraw();
};

hui.ui.Drawing.Arc.create = function(options) {
  var css = [];
  if (options.stroke) {
    if (options.stroke.color) {
      css.push('stroke:'+options.stroke.color);
    }
    if (options.stroke.width) {
      css.push('stroke-width:'+options.stroke.width);
    }
  }
  options.node = hui.ui.Drawing._build({ tag : 'path' ,parent : options.parent, attributes : {fill : options.fill || '#000', style:css.join(';')}});
  var arc = new hui.ui.Drawing.Arc(options);
  return arc;
};

hui.ui.Drawing.Arc.prototype = {

  update : function(options) {
    this.options = hui.override(this.options,options);
    this._redraw();
  },
  _redraw : function() {
    var o = this.options,
      cx = o.center.x,
      cy = o.center.y,
      startRadians = (o.startDegrees || 0) * Math.PI/180,
      closeRadians = (o.endDegrees   || 0) * Math.PI/180,
      r1 = o.innerRadius,
      r2 = o.outerRadius;
    var points = [
      [
        cx + r2 * Math.cos(startRadians),
        cy + r2 * Math.sin(startRadians)
      ],
      [
        cx + r2 * Math.cos(closeRadians),
        cy + r2 * Math.sin(closeRadians)
      ],
      [
        cx + r1 * Math.cos(closeRadians - o.skew),
        cy + r1 * Math.sin(closeRadians - o.skew)
      ],
      [
        cx + r1 * Math.cos(startRadians + o.skew),
        cy + r1 * Math.sin(startRadians + o.skew)
      ]
    ];

    var angleDiff = closeRadians - startRadians;
    var largeArc = (angleDiff % (Math.PI*2)) > Math.PI ? 1 : 0;
    var cmds = [
      "M"+points[0].join(),                 // Move to P0
      "A"+[r2,r2,0,largeArc,1,points[1]].join(),        // Arc to  P1
      "L"+points[2].join(),                 // Line to P2
      "A"+[r1,r1,0,largeArc,0,points[3]].join(),        // Arc to  P3
      "z"                                           // Close path (Line to P0)
    ];
    this.node.setAttribute('d',cmds.join(' '));
  }
};



// Element

hui.ui.Drawing.Element = function(node) {
  this.node = node;
};

hui.ui.Drawing.Element.prototype = {
  setPosition : function(point) {
    this.node.style.left = point.x+'px';
    this.node.style.top = point.y+'px';
  },
  setCenter : function(point) {
    this.node.style.left = (point.x - this.node.clientWidth/2)+'px';
    this.node.style.top = (point.y - this.node.clientHeight/2)+'px';
  }
};


hui.geometry = {
  intersectLineLine : function(a1, a2, b1, b2) {

    var ua_t = (b2.x - b1.x) * (a1.y - b1.y) - (b2.y - b1.y) * (a1.x - b1.x);
    var ub_t = (a2.x - a1.x) * (a1.y - b1.y) - (a2.y - a1.y) * (a1.x - b1.x);
    var u_b  = (b2.y - b1.y) * (a2.x - a1.x) - (b2.x - b1.x) * (a2.y - a1.y);

    if ( u_b !== 0 ) {
      var ua = ua_t / u_b;
      var ub = ub_t / u_b;

      if ( 0 <= ua && ua <= 1 && 0 <= ub && ub <= 1 ) {
        return {
          x : a1.x + ua * (a2.x - a1.x),
          y : a1.y + ua * (a2.y - a1.y)
        };
      }
    }
    return null;
  },
  intersectLineRectangle : function(a1, a2, r1, r2) {
    var min        = {x : Math.min(r1.x,r2.x),y : Math.min(r1.y,r2.y)};
    var max        = {x : Math.max(r1.x,r2.x),y : Math.max(r1.y,r2.y)};
    var topRight   = {x: max.x, y: min.y };
    var bottomLeft = {x: min.x, y: max.y };

    var inter1 = hui.geometry.intersectLineLine(min, topRight, a1, a2);
    var inter2 = hui.geometry.intersectLineLine(topRight, max, a1, a2);
    var inter3 = hui.geometry.intersectLineLine(max, bottomLeft, a1, a2);
    var inter4 = hui.geometry.intersectLineLine(bottomLeft, min, a1, a2);

    var result = [];

    if (inter1 !== null) result.push(inter1);
    if (inter2 !== null) result.push(inter2);
    if (inter3 !== null) result.push(inter3);
    if (inter4 !== null) result.push(inter4);
    return result;
  },
  distance : function( point1, point2 ) {
    var xs = point2.x - point1.x;

    var ys = point2.y - point1.y;

    return Math.sqrt( xs * xs + ys * ys );
  }
};