Based on my experience with Zedgraph I could not set both of X and Y axes scale same to have a correct scatter graph! Assume we have a square grid of 10x10m cells in which each cell is a square shape 1x1m. when we try to draw points of such data, the output is not acceptable because each axis scaled to different scale. It is worse when we try to call Zoomall, then we find all points are fitted to chart area regardless their equal spacing!
I hope someone can help me to find a solution. Although Zedgraph is flexible library but this is a big fault!
perfectly aware, this q. is 9 years old, but still..
Have just encountered and solved the issue of presenting the graph in a square pane.
(It seems that was the OP question)
It's a bit "brute" and invokes redundant redraws, but gets the job done.
//"GraphWinFormsHost" is my ZGraph container
GraphWinFormsHost.SizeChanged += (sender, args) =>
{
//"IsEqualScale" is my property, indicating a square is needed
if(_ChartData == null || !_ChartData.IsEqualScale)
return;
_ZedGraphControl.GraphPane.Chart.IsRectAuto = true;
_ZedGraphControl.Refresh();
//here, the graph pane is redrawn according to available space
float x = _ZedGraphControl.GraphPane.Chart.Rect.X;
float y = _ZedGraphControl.GraphPane.Chart.Rect.Y;
float h = _ZedGraphControl.GraphPane.Chart.Rect.Height;
float w = _ZedGraphControl.GraphPane.Chart.Rect.Width;
float min = Math.Min(h, w);
_ZedGraphControl.GraphPane.Chart.Rect = new RectangleF(x, y, min, min);
};
Is Scale.IsAnyOrdinal true for any Axis.Scale's ?
ZedGraph appears to position nodes based on index offset rather than node value when the Scale.Type is set to AxisType.Text, Ordinal, DateAsOrdinal, or LinearAsOrdinal.
I recently had to solve the same problem. This is what has worked for me:
zg1.AxisChange();
if (myPane.XAxis.Scale.Max > myPane.YAxis.Scale.Max) {
myPane.YAxis.Scale.Max = myPane.XAxis.Scale.Max;
myPane.YAxis.Scale.Min = myPane.XAxis.Scale.Min;
myPane.YAxis.Scale.MajorStep = myPane.XAxis.Scale.MajorStep;
} else {
myPane.XAxis.Scale.Max = myPane.YAxis.Scale.Max;
myPane.XAxis.Scale.Min = myPane.YAxis.Scale.Min;
myPane.XAxis.Scale.MajorStep = myPane.YAxis.Scale.MajorStep;
}
zg1.AxisChange();
With the first call to AxisChange the control automatically calculate the correct values for my data. Then I copy the relevant parameters from one scale to the other one and apply the change.
Related
I'm using Embarcadero RAD Studio C++ builder XE7.
For a drawing function using the Windows GDI, I need to add a clip region to the device context of a canvas.
By testing my code, I noticed that sometimes the clipping region was smaller than the expected size. I searched why and I found a strange behavior of the OffsetRgn() function which lets me a little puzzled.
To apply the clip region, I use a code similar to the following:
std::unique_ptr<TBitmap> pBitmap(new TBitmap());
pBitmap->PixelFormat = pf32bit;
pBitmap->AlphaFormat = afDefined;
pBitmap->SetSize(60, 7);
TCanvas* pCanvas = pBitmap->Canvas;
::SelectClipRgn(pCanvas->Handle, NULL);
const TRect sourceRect = pCanvas->ClipRect;
HRGN pClipRegion = ::CreateRectRgn(50, -2, 60, 8);
::SelectClipRgn(pCanvas->Handle, pClipRegion);
const TRect intermediateRect = pCanvas->ClipRect;
const int deltaX = pCanvas->ClipRect.Left - 50;
const int deltaY = pCanvas->ClipRect.Top - (-2);
::OffsetRgn(pClipRegion, -deltaX, -deltaY);
::SelectClipRgn(pCanvas->Handle, pClipRegion);
const TRect finalRect = pCanvas->ClipRect;
NOTE written like this and out of his context, the above code does not really make sense, and I know it's illogical. Please do not judge its quality, this is not the purpose of my question. I gathered several excerpts that I grouped into an executable code putting the problem forward.
The hardcoded values are an example of values I get in my application when the problem occurs. If I execute the above code, I measure:
left = 0, top = 0, right = 60, bottom = 7 in sourceRect value
left = 50, top = 0, right = 60, bottom = 7 in intermediateRect value
left = 50, top = 0, right = 60, bottom = 6 in finalRect
I however expected that the bottom value should also be equals to 7 in finalRect, which is the canvas limit, as I only moved the region and nothing else. So why it's value become suddenly smaller than expected?
So I finally found the substance of the case. Based on this post:
Why does calling GetRgnBox on the result of GetClipRgn return a very different rect than GetClipRect?
The clip region is applied in logical units relatively to the canvas origin, whereas the clipping rectangle I tried to apply was measured in pixels from a [0, 0] origin.
As I incorrectly thought in my code that the origin was always [0, 0] for the both systems, the resulting region could be incorrect in several special cases, causing this strange shifting I sometimes noticed between the clipping really applied and which I expected.
Measuring the canvas origin with the GetWindowOrgEx() function highlighted the issue.
However for the above shown case, issue came because the clip region was moved by an offset of -2, taking so the value of -4 on top and 6 on bottom, which is then clipped to fit the canvas bounds while the clip region is applied, resulting to a clipping with value of 0 on top and 6 on bottom.
I am trying to achieve a gauge graph that will have a gradient based on the percentage it is full, similar to the image below. The closest I could manage is showing all the gradient colors regardless of the value - Like here.
var x = 80; // Change x to see effects.
Did anyone managed to get a gradient based on value?
You can use yAxis stops and make small trick adding gradient to your chart. You can for example add points to your gauge (inside your load event callback function) with values decreasing from your normal y value to 0. It will give you a chance to have something similar to gradient with multiple points.
function(chart) {
var y = this.series[0].data[0].y;
for (var i = y; i >= 0; i = i - 1) {
chart.addSeries({
data: [i],
stickyTracking: false,
enableMouseTracking: false
}, false)
}
chart.redraw();
}
Here you can see an example how it can work: http://jsfiddle.net/64mfcg3v/6/
Kind regards,
I'm trying to update my axis with custom labels because I want my users to be able to use different datasets. The current array of label names i load in have 17 elements in it.
When loading the page the graph only shows 11 elements as it can be seen here: http://servers.binf.ku.dk/hemaexplorerbeta/
How do I update my xScale to be able to all of the elements at once?
Here's the code for my scale:
var xScale = d3.scale.linear()
.domain([-25, genWidth])
.range([0, width-margin.right]);
And for my update function:
function updateAxisX(arr) {
var formatAxis = function(d) {
return arr[d % arr.length];
}
xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom")
.tickFormat(formatAxis);
SVG.select(".x.axis")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("transform", function(d) {
return "rotate(-65)"
});
}
I also have a zoom function where you can drag the graph around as well. As I mentioned the X Axis has 17 element in total, although on my graph they are repeated over and over. Is there any way I can prevent the zoom from letting the user drag the graph out of my "maximum" frame? In other words I'd like to be able to drag ONLY IF there has been zoomed in on the graph and ONLY until you reach the "edges" of the graph and no more.
Here's the code for my zoom function:
var zoom = d3.behavior.zoom()
.x(xScale)
.y(yScale)
.scaleExtent([1, 10])
.on("zoom", zoomTargets);
function zoomTargets() {
SVG.select(".x.axis").call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("transform", function(d) {
return "rotate(-65)"
});
SVG.select(".y.axis").call(yAxis);
SVG.selectAll("circle").attr("cx",function(d){return xScale(d)}).attr("cy",function(d){return yScale(d)});
}
Also don't mind the huge red circles, they are just randomly made for testing something.
I should also mention that my graph is supposed be a scatter plot by the time I get everything done.
One quick question as well. Since I'm not using numbers, but custom labels where I've inserted strings. How do I insert the my circles accordingly to each spot on the x axis?
Thank you very much for your help in advance!
I trying to do some Joint Tracking with kinect (just put a ellipse inside my right hand) everything works fine for a default 640x480 Image, i based myself in this channel9 video.
My code, updated to use the new CoordinateMapper classe is here
...
CoordinateMapper cm = new CoordinateMapper(this.KinectSensorManager.KinectSensor);
ColorImagePoint handColorPoint = cm.MapSkeletonPointToColorPoint(atualSkeleton.Joints[JointType.HandRight].Position, ColorImageFormat.RgbResolution640x480Fps30);
Canvas.SetLeft(elipseHead, (handColorPoint.X) - (elipseHead.Width / 2)); // center of the ellipse in center of the joint
Canvas.SetTop(elipseHead, (handColorPoint.Y) - (elipseHead.Height / 2));
This works. The question is:
How to do joint tracking in a scaled image, 540x380 for example?
The solution for this is pretty simple, i fugured it out.
What a need to do is find some factor to apply to the position.
This factor can be found takin the atual ColorImageFormat of the Kinect and dividing by the desired size, example:
Lets say i am working with the RgbResolution640x480Fps30 format and my Image (ColorViewer) have 220x240. So, lets find the factor for X:
double factorX = (640 / 220); // the factor is 2.90909090...
And the factor for y:
double factorY = (480/ 240); // the factor is 2...
Now, i adjust the position of the ellipse using this factor.
Canvas.SetLeft(elipseHead, (handColorPoint.X / (2.909090)) - (elipseHead.Width / 2));
Canvas.SetTop(elipseHead, (handColorPoint.Y / (2)) - (elipseHead.Height / 2));
I've not used the CoordinateMapper yet, and am not in front on my Kinect at the moment, so I'll toss out this first. I'll see about an update when I get working with the Kinect again.
The Coding4Fun Kinect Toolkit has a ScaleTo extension as part of the library. This adds the ability to take a joint and scale it to any display resolution.
The scaling function looks like this:
private static float Scale(int maxPixel, float maxSkeleton, float position)
{
float value = ((((maxPixel / maxSkeleton) / 2) * position) + (maxPixel/2));
if(value > maxPixel)
return maxPixel;
if(value < 0)
return 0;
return value;
}
maxPixel = the width or height, depending on which coordinate your scaling.
maxSkeleton = set this to 1.
position = the X or Y coordinate of the joint you want to scale.
If you were to just include the above function you could call it like so:
Canvas.SetLeft(e, Scale(640, 1, joint.Position.X));
Canvas.SetTop(e, Scale(480, 1, -joint.Position.Y));
... replacing your 640 & 480 with a different scale.
If you include the Coding4Fun Kinect Toolkit, instead of re-writing code, you could just call it like so:
scaledJoin = rawJoint.ScaleTo(640, 480);
... then plug in what you need.
I have a stacked column chart like so:
I'm using a text annotation to display the $2495 in the rightmost stacked column. I've determined the proper y position experimentally - obviously that won't work for dynamically generated content.
Does anyone know how I can determine the height of the data points which compose the column? I presumed it would be something like:
Chart1.Series[0][0].Height + Chart1.Series[1][0].Height + Chart1.Series[3][0].Height + Chart1.Series[4][0].Height
But, alas, it is apparently not that simple. Any thoughts or insight would be greatly appreciate.
Check out this link
http://support2.dundas.com/OnlineDocumentation/WebChart2005/Custom_Drawing_Using_the_Paint_Event.html
See the examples at the bottom.
It turns out that it's all much simpler than I thought...the annotations use chart coordinates. This means that all you have to do is sum the actual values and use that as a y coordinate. I ended up doing this to calculate the heights of the respective series:
private int CalculateHeight(int i, ChartGraphics graphics)
{
var height = 0.0;
// find the respective heights of series i, add them together
for (var x = 0; x < this.Chart1.Series.Count(); x++)
{
height += this.Chart1.Series[x].Points[i].YValues[0];
}
return (int)height;
}
I then call that function like so:
for (var i = 0; i < chart.Series[0].Points.Count(); i++ )
{
var height = this.CalculateHeight(i, e.ChartGraphics);
this.Chart1.Annotations[i].Y = height + verticalPadding;
}
Much simpler than I thought.