PDA

View Full Version : Move points on a line chart



tbstockton
21 Jun 2010, 10:16 AM
I'd like to be able to drag a point on a line chart to a new position. Looks like I should be able to use itemdragend to do this but it seems I need to know the x,y's of the other points to scale the the data store values by the x,y returned from itemdragend. I can't figure out how to get the other x,y's of the points in the chart series (the points not dragged), is there a way to do this? Maybe there is a function that translates data store values to component x,y's and vise versa?



var graphStore = new Ext.data.JsonStore({
fields: ['value', 'level'],
data: [
{value: 0.0, level: 5},
{value: 0.3, level: 100},
{value: 0.7, level: 1000},
{value: 1.0, level: 10000}
]
});

new Ext.Panel({
width: 600,
height: 400,
layout: 'fit',
title: 'Value Function',
items: {
xtype: 'linechart',
store: graphStore,
xField: 'level',
yField: 'value',
yAxis: new Ext.chart.NumericAxis({
displayName: 'Values',
labelRenderer : Ext.util.Format.numberRenderer('0,0.0')
}),
xAxis: new Ext.chart.NumericAxis({
displayName: 'Levels'
}),
listeners: {
itemdragend: function(o){
var rec = graphStore.getAt(o.index);
alert(o.y);
}
},
}
}, config));
}
});

Thanks

Tom

darthwes
21 Jun 2010, 6:20 PM
My first question is have you searched the forums? My next one is why do you want to scale the other data points? You realize it's going to move the chart (not the axes) if you change them all? You want to scroll around it maybe? You could get the container elements and do sizing based on that, but you lose accuracy. Of course it's all moot if you don't know your axes' min/max'es.

The x, y positions you're getting on that event are the x,y positions of the mouse on the screen, not the x,y positions of the point that was clicked.

You grab your store (o.component.store) and you use it's method "each" to access them all. Good luck!

tbstockton
22 Jun 2010, 5:57 AM
darthwes thanks for responding. I've searched the forums and various extjs pages and haven't found what I need.

If I drag one of the points on the linechart with a itemdragend listener than I can get the x,y positions of the mouse on the screen, as you said. I then need to translate that x,y pair to the scale of the data store so I can update the data store and refresh the linegraph. I was thinking I could do this if I knew what the x,y coordinates of the of the other data store points where, ie, I need to know the relationship between the x,y screen positions and the scale of the linegraph.
http://zeus.neptuneinc.org/PPriM/content/linechart.png

darthwes
22 Jun 2010, 3:23 PM
It's not built for that.
To do it you'd have to use rough estimation, because:


alert(".1 + .2 = " + (.1 + .2) + ", and it does " + ((.1+.2) === (.3) ? "": "not") + " equal .3");

JavaScript doesn't do floating point perfectly.

Therefore, converting pixels to graph units will be a rough estimate at best. Wanna know how I know?

Behold:



var graphStore = new Ext.data.JsonStore({
fields: ['value', 'level'],
data: [
{value: 0.0, level: 5},
{value: 0.3, level: 100},
{value: 0.7, level: 1000},
{value: 1.0, level: 10000}
]
});

var np = new Ext.Panel({
width: 600,
height: 400,
layout: 'fit',
title: 'Value Function',
items: {
xtype: 'linechart',
store: graphStore,
xField: 'level',
yField: 'value',
yAxis: new Ext.chart.NumericAxis({
displayName: 'Values',
labelRenderer: Ext.util.Format.numberRenderer('0,0.0'),
minimum: 0,
maximum: 3
}),
xAxis: new Ext.chart.NumericAxis({
displayName: 'Levels',
minimum: 0,
maximum: 4000
}),
listeners: {
itemdragstart: function (o) {
this.lastStart = o;
},
itemdragend: function (o) {
//hack, never copy this code, it burns.
if (Ext.isDefined(this.lastStart)) {
var s = this.lastStart;
var diffX = s.x - o.x;
var diffY = s.y - o.y;
var parent = o.component.ownerCt;
var w = parent.el.dom.offsetWidth;
var h = parent.el.dom.offsetHeight;

//pull padding from self
w = w - parent.el.dom.offsetLeft;
h = h - parent.el.dom.offsetTop;

//I need to know the lengths of the axes.
//Solve this with Ext.NumericAxis settings.
var xMin = 0,
xMax = 4000,
yMin = 0,
yMax = 3;

//ticks / pixel
var xScale = ( (xMax-xMin) / w);
var yScale = ( (yMax-yMin) / h);

//num container pixels moved
var dXComputed = xScale * diffX;
var dYComputed = yScale * diffY;

var affixData = function(dX,dY) {
return function(rec) {
rec.data[this.xField] = rec.data[this.xField] - dX;
rec.data[this.yField] = rec.data[this.yField] + dY;
rec.commit(); //there has to be a way to do this different, I don't know it.
};
}(dXComputed, dYComputed);

o.component.store.each(affixData, this);

//remember this line, b's and g's:
delete(this.laststart);
}

//code block to show that you can move data points around
//on a chart.
/*
var rec = graphStore.getAt(o.index);
var nData = [
{value: 0.0, level: 50},
{value: 0.3, level: 100},
{value: 0.7, level: 200},
{value: 1.0, level: 300}
];
graphStore.loadData(nData, false);
*/
}
}
}
});

//insert complex overnesting:
var win = new Ext.Window({
items: np
});
win.show(Ext.getBody());

tbstockton
23 Jun 2010, 4:39 AM
Wow! Thanks, this seems to provide a close enough estimate to work.

I've tried to modify affixData() to move only the point dragged and to ensure that the result is monotonic. A first hack at doing this exhibits odd behavior but as I learn more I imagine I can work it out.



var affixData = function(dX,dY,x,y,oindex) {
return function(rec,index) {
if(index == oindex && index!=0 && index!=3){
var newX = rec.data[this.xField] - dX;
var newY = rec.data[this.yField] + dY;

if((newX > x[index-1]) && (newX < x[index+1])){
rec.data[this.xField] = newX;
}else{
if(Math.abs(newX - x[index-1]) < Math.abs(newX - x[index+1])){
rec.data[this.xField] = x[index-1];
}else{
rec.data[this.xField] = x[index+1];
}
}

if((newY > y[index-1]) && (newY < y[index+1])){
rec.data[this.yField] = newY;
}else{
if(Math.abs(newY - y[index-1]) < Math.abs(newY - y[index+1])){
rec.data[this.yField] = y[index-1]+0.001;
}else{
rec.data[this.yField] = y[index+1]-0.001;
}
}
rec.commit(); //there has to be a way to do this different, I don't know it.
}
};
}(dXComputed, dYComputed,x,y,o.index);