PDA

View Full Version : Vector Graphics



marmstrong
6 Feb 2007, 2:41 PM
I have been working with vector graphics for a mapping app and thought it might be useful to include some of the functionality in Ext. I'm also a big fan how clean Ext is so I threw together some base objects and a rough line graph. I tried to follow the patterns and approach that Jack uses.

The current implementation of the base objects targets vml but svg support could be added relatively easily. (ie always seems to be a corporate standard...)

Here are the base classes:


/**
* @class YAHOO.ext.Line
* @extends YAHOO.ext.util.Observable
* Simple Vector Line class
* @cfg {Function} handler A function called when the Line is hovered
* @cfg {Object} scope The scope of the handler
* @cfg {String} Id optional to identify the line
* @cfg {Coord} from beginning point of line
* @cfg (Coord} to end point of line
* @cfg {Number} strokeweight width of line
* @cfg {String} strokecolor color of line
* @constructor
* Create a new line
* @param {String/HTMLElement/Element} renderTo The element to append the button to
* @param {Object} config The config object
*/
YAHOO.ext.Vector.Line = function(renderTo,config){
this.id = YAHOO.util.Dom.generateId();
YAHOO.ext.util.Config.apply(this,config);
this.events = {
};
if(renderTo){
this.render(renderTo);
}
}
YAHOO.extendX(YAHOO.ext.Vector.Line, YAHOO.ext.util.Observable, {
render : function(renderTo){
this.lineTemplate = new YAHOO.ext.DomHelper.Template('<v:line id="' + this.id + '" ' +
'stroke=true strokecolor="' + this.strokecolor + '" strokeweight="' + this.strokeweight + '"' +
' style="position:relative;left:0px;top0px" ' + 'coordorigin="0,0"' +
' to="' + this.to.x + ' ' + this.to.y + '" from="' + this.from.x + ' ' + this.from.y + '"></v:line>');
this.el = this.lineTemplate.append(getEl(renderTo).dom, [''], true);
},
/**
* Returns the line element
* @return {YAHOO.ext.Element}
*/
getEl : function(){
return this.el;
}
});
/**
* @class YAHOO.ext.Polyline
* @extends YAHOO.ext.util.Observable
* Simple Vector Line class
* @cfg {Function} handler A function called when the Line is hovered
* @cfg {Object} scope The scope of the handler
* @cfg {Number} strokeweight width of line
* @cfg {Array} pts x and y pairs
* @cfg {String} strokecolor color of line
* @constructor
* Create a new line
* @param {String/HTMLElement/Element} renderTo The element to append the button to
* @param {Object} config The config object
*/
YAHOO.ext.Vector.Polyline = function(renderTo,config){
YAHOO.ext.util.Config.apply(this,config);
this.events = {
/**
* @event hover
* Fires when line is hovered over
* @param {Line} this
* @param {EventObject} e The hover event
*/
'hover' : true
};
if(renderTo){
this.render(renderTo);
}
}
YAHOO.extendX(YAHOO.ext.Vector.Polyline, YAHOO.ext.util.Observable, {
render : function(renderTo){
var s = "";
for(var i=0; i<this.pts.length; i++) {
s += this.pts[i].x + ' ' + this.pts[i].y + ' ';
}
this.polyTemplate = new YAHOO.ext.DomHelper.Template('<v:polyline id="' + YAHOO.util.Dom.generateId() + '" ' +
'filled=false stroke=true strokecolor="' + this.strokecolor + '" strokeweight="' + this.strokeweight +
'" Points="' + s + '"></v:polyline>');

this.el = this.polyTemplate.append(getEl(renderTo).dom, [''], true);
},
/**
* Returns the polyline element
* @return {YAHOO.ext.Element}
*/
getEl : function(){
return this.el;
}
});
/**
* @class YAHOO.ext.Box
* @extends YAHOO.ext.util.Observable
* Simple Vector Rectangle class
* @cfg {Number} strokeweight width of line
* @cfg {String} strokecolor color of line
* @constructor
* Create a new line
* @param {String/HTMLElement/Element} renderTo The element to append the button to
* @param {Object} config The config object
*/
YAHOO.ext.Vector.Box = function(renderTo,config){
this.shadow = 'true';
this.strokecolor = 'black';
this.strokweight = '1';
this.cls ='graph-box';
YAHOO.ext.util.Config.apply(this,config);
this.events = {
};
if(renderTo){
this.render(renderTo);
}
}
YAHOO.extendX(YAHOO.ext.Vector.Box, YAHOO.ext.util.Observable, {
render : function(renderTo){
this.boxTemplate = new YAHOO.ext.DomHelper.Template('<v:rect id="' +
YAHOO.util.Dom.generateId() + '" ' +
' class=' + this.cls + ' style="position:relative;width:' + this.width + ';height:' + this.height + ';' +
'left:0px;top:0px;"' +
' strokecolor=' + this.strokecolor + ' strokeweight=' + this.strokeweight +
'><v:shadow on=' + this.shadow + ' />' +
'</v:rect>');
this.el = this.boxTemplate.append(getEl(renderTo).dom, [''], true);
},
/**
* Returns the line element
* @return {YAHOO.ext.Element}
*/
getEl : function(){
return this.el;
}
});

YAHOO.ext.Vector.Group = function(renderTo, config){
YAHOO.ext.util.Config.apply(this,config);
this.groupTemplate = new YAHOO.ext.DomHelper.Template('<v:group ' +
'style="position:relative;left:0px;top:0px;width:' + this.width +
'; height:' + this.height + ';" ' +
'coordsize="' + this.width + ',' + this.height + '" coordorigin="0,0">');
this.el = this.groupTemplate.append(getEl(renderTo).dom, [''], true);
this.getEl = function(){
return this.el;
}
}



The line graph class:


YAHOO.namespace('ext.Graph');
/**
* @class YAHOO.ext.Graph.LineGraph
* @extends YAHOO.ext.util.Observable
* Simple Vector Line Graph class
* @cfg {Object} graphData array of array object
* Example: [[{x:0,y:5},{x:1,y:10}],[{x:0,y:8},{x:1,y:13}]]
* @constructor
* Create a new line
* @param {Object} config The config object
*/
YAHOO.ext.Graph.LineGraph = function(renderTo,config){
YAHOO.ext.util.Config.apply(this,config);
if(renderTo){
this.render(renderTo);
}
}
YAHOO.extendX(YAHOO.ext.Graph.LineGraph, YAHOO.ext.util.Observable, {
render : function(renderTo){
this.graphTemplate = new YAHOO.ext.DomHelper.Template('<div></div>');
this.container = getEl(renderTo);
this.width = this.container.getWidth();
this.height = this.container.getHeight();
this.el = this.graphTemplate.append(this.container.dom, [''], true);
this.el.addClass('graph-main');
this.el.setHeight(this.height);
this.el.setWidth(this.width);
this.mainX = 25;
this.mainY = this.height -25;
this.lengthY = this.height - this.mainY;
this.lengthX = this.width - this.mainX;
var x = this.getMaxX(this.graphData);
var y = this.getMaxY(this.graphData);
this.scaleX = parseFloat(x/(this.lengthX-this.mainX));
this.scaleY = parseFloat(y/(this.mainY-this.lengthY));
Log.log('max x: ' + x + ' max y: ' + y);
this.rescaleData();
this.adjustData();
this.group = YAHOO.ext.Vector.VectorManager.getGroup(this.el,
{
height: this.height,
width: this.width
});
this.graphBox = YAHOO.ext.Vector.VectorManager.getBox(this.group.getEl(),
{
strokecolor: 'gray',
height: this.height,
width: this.width
});
this.xAxis = YAHOO.ext.Vector.VectorManager.getLine(this.group.getEl(),
{
id: 'xLine',
strokecolor: 'steelblue',
strokeweight: '1px',
from:{x:this.mainX,y:this.mainY},
to:{x:this.lengthX,y:this.mainY}
});
this.yAxis = YAHOO.ext.Vector.VectorManager.getLine(this.group.getEl(),
{
id: 'yLine',
strokecolor: 'steelblue',
strokeweight: '1px',
from:{x:this.mainX,y:this.mainY},
to:{x:this.mainX,y:this.lengthY}
});
for(var i=0; i<this.graphData.length; i++) {
Log.log('graph line: ' + i);
YAHOO.ext.Vector.VectorManager.getPoly(this.group.getEl(),
{
strokecolor: 'blue',
strokeweight: '1px',
pts: this.graphData[i]
});
}
// Log.log(getEl('yLine').dom.from + ' and to: ' + getEl('yLine').dom.to);
},
rescaleData : function(){
for(var i=0; i<this.graphData.length; i++) {
for(var j=0; j<this.graphData[i].length; j++) {
this.graphData[i][j].x = this.graphData[i][j].x / this.scaleX;
this.graphData[i][j].y = this.graphData[i][j].y / this.scaleY;
}
}
},
adjustData : function(){
for(var i=0; i<this.graphData.length; i++) {
for(var j=0; j<this.graphData.length; j++) {
this.graphData[i][j].x = this.graphData[i][j].x + this.mainX;
this.graphData[i][j].y = this.mainY - this.graphData[i][j].y;
}
}
},
getMaxX : function(obj){
var m = 0;
for(var i=0; i<obj.length; i++) {
for(var j=0; j<obj[i].length; j++) {
(m<obj[i][j].x)?m=obj[i][j].x:'';
}
}
this.maxX = m;
return m;
},
getMaxY : function(obj){
var m = 0;
for(var i=0; i<obj.length; i++) {
for(var j=0; j<obj[i].length; j++) {
(m<obj[i][j].y)?m=obj[i][j].y:'';
}
}
this.maxY = m;
return m;
},
/**
* Returns the line element
* @return {YAHOO.ext.Element}
*/
getEl : function(){
return this.el;
}
});


I almost forgot, there is also a vector manager class. My thought is that this class would handle vml/svg decision. Kind of like a factory pattern.



YAHOO.namespace('ext.Vector');
/**
* @class YAHOO.ext.Vector.VectorManager
* This class is a singleton that manages vector objects
*/
YAHOO.ext.Vector.VectorManager = new function(){
var brows = YAHOO.ext.util.Browser;
var vectorType = 'vml'; //vml or svg
/**
* Sets up required vector graphics options per browser
*/
this.setupVector = function(){
if(brows.isIE||brows.isIE7){
var baseHtml = document.getElementsByTagName('html')[0];
baseHtml.setAttribute("xmlns:v","urn:schemas-microsoft-com:vml");
var h = YAHOO.ext.Element.get(baseHtml, true);

vectorType = 'vml';
}else{
vectorType = 'svg';
}
}
this.getLine = function(renderTo, config){
if(vectorType == 'vml'){
return new YAHOO.ext.Vector.Line(renderTo, config);
}
}
this.getPoly = function(renderTo, config){
if(vectorType == 'vml'){
return new YAHOO.ext.Vector.Polyline(renderTo, config);
}
}
this.getBox = function(renderTo, config){
if(vectorType == 'vml'){
return new YAHOO.ext.Vector.Box(renderTo, config);
}
}
this.getGroup = function(renderTo, config){
if(vectorType == 'vml'){
return new YAHOO.ext.Vector.Group(renderTo, config);
}
}
}
YAHOO.ext.EventManager.onDocumentReady(YAHOO.ext.Vector.VectorManager.setupVector, YAHOO.ext.Vector.VectorManager, true);


The line of code to create a graph:


function createNewLineGraph(){
var g = new YAHOO.ext.Graph.LineGraph('vmltest', {stroke: 'true',
graphData: [
[{x:0,y:0},
{x:1,y:75},
{x:2,y:100},
{x:3,y:75}],
[{x:0,y:10},
{x:1,y:65},
{x:2,y:120},
{x:3,y:100}]
]
});
}


I welcome feedback.

Webnet
6 Feb 2007, 7:31 PM
mmmm graph support :)

Animal
7 Feb 2007, 12:59 AM
Great work. Do you have a version up anywhere that we can see?

rodiniz
7 Feb 2007, 1:49 AM
Its giveing an error at this.group is undefined.
You also have to comment some Log calls that are not commented.
If you let me I can post a sample in my website.

Condor70
7 Feb 2007, 2:44 AM
YAHOO.extendX(YAHOO.ext.Vector.Line, YAHOO.ext.util.Observable, {
render : function(renderTo){
this.lineTemplate = new YAHOO.ext.DomHelper.Template('<v:line id="' + this.id + '" ' +
'stroke=true strokecolor="' + this.strokecolor + '" strokeweight="' + this.strokeweight + '"' +
' style="position:relative;left:0px;top0px" ' + 'coordorigin="0,0"' +
' to="' + this.to.x + ' ' + this.to.y + '" from="' + this.from.x + ' ' + this.from.y + '"></v:line>');
this.el = this.lineTemplate.append(getEl(renderTo).dom, [''], true);
},
I welcome feedback.

I think you are missing the point of the Template class. You should create the template once, compile it and then reuse it with parameters, e.g.


YAHOO.ext.Vector.Line.prototype.lineTemplate = new YAHOO.ext.DomHelper.Template('<v:line id={id} ' +
'stroke=true strokecolor={strokecolor} strokeweight={strokeweight} ' +
'style="position:relative;left:0px;top:0px" coordorigin="0,0" ' +
'to="{tox},{toy}" from="{fromx},{fromy}" />');
YAHOO.ext.Vector.Line.prototype.lineTemplate.compile();

YAHOO.extendX(YAHOO.ext.Vector.Line, YAHOO.ext.util.Observable, {
render : function(renderTo){
this.el = this.lineTemplate.append(getEl(renderTo).dom, this, true);
},

ps. The only disadvantage is that instead of to.x, to.y, from.x and from.y you will need to use tox, toy, fromx and fromy.

Animal
7 Feb 2007, 2:55 AM
or if Template.compile could be changed to return "this":



YAHOO.extendX(YAHOO.ext.Vector.Line, YAHOO.ext.util.Observable, {

lineTemplate: new YAHOO.ext.DomHelper.Template('<v:line id={id} ' +
'stroke=true strokecolor={strokecolor} strokeweight={strokeweight} ' +
'style="position:relative;left:0px;top:0px" coordorigin="0,0" ' +
'to="{tox},{toy}" from="{fromx},{fromy}" />').compile(),

render : function(renderTo){
this.el = this.lineTemplate.append(getEl(renderTo).dom, this, true);
},

...
});

marmstrong
7 Feb 2007, 5:41 AM
Rodiniz, feel free to post an example.

I will post changes based on suggestions a little later today.

rodiniz
7 Feb 2007, 6:04 AM
Rodiniz, feel free to post an example.

I will post changes based on suggestions a little later today.
The sample is on the url belowbut its giving an error.
http://www.rodrigodiniz.qsh.eu/LineChart.aspx

marmstrong
7 Feb 2007, 11:34 AM
I changed the base vector classes based on suggestions. Thanks!

RoundBox class now extends the Box class.

Any suggestions on the polyline class? The number of points that can be included is not finite so I couldn't follow the suggested use of Template.





/**
* @class YAHOO.ext.Line
* @extends YAHOO.ext.util.Observable
* Simple Vector Line class
* @cfg {String} Id optional to identify the line
* @cfg fromx fromy beginning point of line
* @cfg tox toy end point of line
* @cfg {Number} strokeweight width of line
* @cfg {String} strokecolor color of line
* @constructor
* Create a new line
* @param {String/HTMLElement/Element} renderTo The element to append the button to
* @param {Object} config The config object
*/
YAHOO.ext.Vector.Line = function(renderTo,config){
this.id = YAHOO.util.Dom.generateId();
this.strokeweight = '1px';
this.strokecolor = 'black';
YAHOO.ext.util.Config.apply(this,config);
if(renderTo){
this.render(renderTo);
}
}
YAHOO.extendX(YAHOO.ext.Vector.Line, YAHOO.ext.util.Observable, {
lineTemplate: new YAHOO.ext.DomHelper.Template('<v:line id={id} ' +
'stroke=true strokecolor={strokecolor} strokeweight={strokeweight} ' +
'style="position:relative;left:0px;top:0px" coordorigin="0,0" ' +
'to="{tox},{toy}" from="{fromx},{fromy}"><v:line>'), /*.compile(),*/
render : function(renderTo){
this.el = this.lineTemplate.append(getEl(renderTo).dom, this, true);
return this.el;
},
getEl : function(){
return this.el;
}
});
/**
* @class YAHOO.ext.Polyline
* @extends YAHOO.ext.util.Observable
* Simple Vector PolyLine class
* @cfg {Number} strokeweight width of line
* @cfg {Array} pts x and y pairs
* @cfg {String} strokecolor color of line
* @constructor
* Create a new line
* @param {String/HTMLElement/Element} renderTo The element to append the button to
* @param {Object} config The config object
*/
YAHOO.ext.Vector.Polyline = function(renderTo,config){
YAHOO.ext.util.Config.apply(this,config);
if(renderTo){
this.render(renderTo);
}
}
YAHOO.extendX(YAHOO.ext.Vector.Polyline, YAHOO.ext.util.Observable, {
render : function(renderTo){
var s = "";
for(var i=0; i<this.pts.length; i++) {
s += this.pts[i].x + ' ' + this.pts[i].y + ' ';
}
this.polyTemplate = new YAHOO.ext.DomHelper.Template('<v:polyline id="' + YAHOO.util.Dom.generateId() + '" ' +
'filled=false stroke=true strokecolor="' + this.strokecolor + '" strokeweight="' + this.strokeweight +
'" Points="' + s + '"></v:polyline>');

this.el = this.polyTemplate.append(getEl(renderTo).dom, [''], true);
},
/**
* Returns the polyline element
* @return {YAHOO.ext.Element}
*/
getEl : function(){
return this.el;
}
});
/**
* @class YAHOO.ext.Box
* @extends YAHOO.ext.util.Observable
* Simple Vector Rectangle class
* @cfg {Number} strokeweight width of line
* @cfg {String} strokecolor color of line
* @constructor
* @param {String/HTMLElement/Element} renderTo The element to append the button to
* @param {Object} config The config object
*/
YAHOO.ext.Vector.Box = function(renderTo,config){
this.shadow = 'true';
this.strokecolor = 'black';
this.strokweight = '1';
this.cls ='graph-box';
this.left = 0;
this.top = 0;
this.text = '';
this.id = YAHOO.util.Dom.generateId();
YAHOO.ext.util.Config.apply(this,config);
this.events = {
};
if(renderTo){
this.render(renderTo);
}
}
YAHOO.extendX(YAHOO.ext.Vector.Box, YAHOO.ext.util.Observable, {
boxTemplate : new YAHOO.ext.DomHelper.Template('<v:rect id="{id}" ' +
' class={cls} style="position:relative;width:{width};height:{height};' +
'left:{left};top:{top};" strokecolor={strokecolor} strokeweight={strokeweight} >' +
'<v:shadow on={shadow} />' +
'<v:textbox v-text-anchor=middle style="height:20px;margin:0px 0px 0px 0px;font:8pt arial">{text}</v:textbox>' +
'</v:rect>'),
render : function(renderTo){
this.el = this.boxTemplate.append(getEl(renderTo).dom, this, true);
return this.el;
},
getEl : function(){
return this.el;
}
});
/**
* @class YAHOO.ext.Vector.RoundBox
* @extends YAHOO.ext.Vector.Box
* @constructor
* @param {String/HTMLElement/Element} renderTo The element to append the button to
* @param {Object} config The config object
*/
YAHOO.ext.Vector.RoundBox = function(renderTo,config){
YAHOO.ext.Vector.RoundBox.superclass.constructor.call(this, renderTo,config);
}
YAHOO.extendX(YAHOO.ext.Vector.RoundBox, YAHOO.ext.Vector.Box, {
boxTemplate : new YAHOO.ext.DomHelper.Template('<v:roundrect id="{id}" ' +
' class={cls} style="position:relative;width:{width};height:{height};' +
'left:{left};top:{top};" strokecolor={strokecolor} strokeweight={strokeweight} >' +
'<v:shadow on={shadow} />' +
'<v:textbox v-text-anchor=middle style="height:20px;margin:0px 0px 0px 0px;font:8pt arial">{text}</v:textbox>' +
'</v:roundrect>')
});

YAHOO.ext.Vector.Group = function(renderTo, config){
this.left = 0;
this.top = 0;
YAHOO.ext.util.Config.apply(this,config);
this.groupTemplate = new YAHOO.ext.DomHelper.Template('<v:group ' +
'style="position:relative;left:{left};top:{top};width:{width};height:{height};" ' +
'coordsize="{width},{height}" coordorigin="0,0">');
this.el = this.groupTemplate.append(getEl(renderTo).dom, this, true);
this.getEl = function(){
return this.el;
}
}

rodiniz
8 Feb 2007, 2:05 AM
The sample is working only in IE...
http://www.rodrigodiniz.qsh.eu/linechart.aspx
Someone still has to do an svg template...

Condor70
8 Feb 2007, 8:14 AM
or if Template.compile could be changed to return "this"

The 0.40 version of Template.compile already returns this.

ps. I posted a change to Template to allow for objects (like from.x), arrays and functions in a template (see http://www.yui-ext.com/forum/viewtopic.php?t=2580).

bhangale.parag
20 Jul 2009, 3:11 AM
Hi,
I have one problem that how can i use it in my application
can u please give me some sort of idea???
Reply appreciated.........
thanks in advance