|
the same gradient for each bar |
Posted by Pieter Hauffe on May-19-2004 23:46 |
|
Hello,
I was trying to create a single layer bar chart where every bar's background color is a gradient. I know how to apply individual gradient to every data-element of the layer, but of course I have to specify the coordinates, where to start the gradient and where to end. This is of course dependent on the position of the bar, if I want to have every bar looking the exactly the same (for example having the gradient started on the left upper corner of the bar, and ending on the lower right corner). As I dont know these coordinates on the Draw Area when applying the gradient:
Is there a more simple solution for this scenario?
By the way: its the same problem with a pattern.
Thanks for your help... Pieter |
Re: the same gradient for each bar |
Posted by Peter Kwan on May-22-2004 01:49 |
|
Hi Pieter,
Sorry for the late reply.
If the gradient is a "horizonal gradient" (that is, the gradient runs from left to right across the bar and is independent on the vertical position, then the solution is easy. However, from your description, I believe you want to have a diagonal gradient. In this case, you may need to determine the pixel coordinates of the bars (using Layer.getXCoor and Layer.getYCoor) and set the gradient accordingly.
For example, in VBScript/ASP, the code will be something like:
'Create a multicolor bar layer
Set layer = c.addBarLayer3(myData)
........ do other things (set up x-axis, etc.) as usual .......
'Finalize the axis scale (perform auto-scaling)
Call c.layout()
'Now create the gradients from &H800000 to &Hffff00, starting from the bottom left corner and ends at
'the top right corner
For i = 0 to Ubound(data)
color = c.gradientColor(layer.getXCoor(i - 0.4), layer.getYCoor(0), layer.getXCoor(i + 0.4), _
layer.getYCoor(data(i)), &H800000, &Hffff00)
Call layer.getDataSet(i).setDataColor(color)
Next
The same method applies to pattern too.
Regards
Peter Kwan |
Re: the same gradient for each bar |
Posted by Pieter Hauffe on May-24-2004 17:24 |
|
Thanks. Thats the solution I thought of too.
You said, that a more simple solution from left to right gradients would be possible. Could you explain that too, please?
Thanks, Pieter |
Re: the same gradient for each bar |
Posted by Peter Kwan on May-25-2004 05:25 |
|
Hi Pieter,
Assuming you are drawing a vertical bar chart. If the gradients are from left to right, then the gradients are independent of the data values (the data values only determine the heights of the bar, not not their widths). So it is because easy to determine the gradient starting and ending points.
For example, the following gradient color may work sufficient well (provided you do not have too many bars):
color = c.gradientColor(plotAreaLeft, 0, plotAreaLeft + plotAreaWidth / noOfBars, 0, startColor, endColor)
Call c.addBarLayer(myData, color)
A more accurate gradient color that will work even if you have many bars is:
Call c.addBarLayer3(myData)
For i = 0 to Ubound(data)
barLeft = plotAreaWidth * (i + 0.1) / noOfBars + plotAreaLeft
barRigth = plotAreaWidth * (i + 0.9) / noOfBars + plotAreaLeft
color = c.gradientColor(barLeft, 0, barRight, 0, startColor, endColor)
Call layer.getDataSet(i).setDataColor(color)
Next
Regards
Peter Kwan |
Re: the same gradient for each bar |
Posted by Michael Klobe on Oct-05-2005 13:16 |
|
These algorithms work great for a single-bar graph; I want to use a different gradient for each bar in a multi-bar chart. I'd also prefer not to use magic numbers (e.g., .1 and .9) if I can avoid them. Any thoughts?
Thanks! |
Re: the same gradient for each bar |
Posted by Peter Kwan on Oct-05-2005 18:18 |
|
Hi Michael,
There are many reasons for using gradient colors for bars. ChartDirector handles the most common reasons:
1. To handle lighting conditions
A light is a "global" to the chart. Thus the lighting field should be apply to the entire chart and not "repeating" for each bar. The standard ChartDirector gradient color achieves this effect. I have attach a bar chart showing lighting conditions.
2. To handle different reflections due to the "shape" of the bar.
The most common shape people like to emulate is a "cylinder" shape. ChartDirector also has built-in support for cylinder shape gradients. An example 2D and also a 3D cylinder chart are shown in our chart gallery page http://www.advsofteng.com/gallery_bar.html . The source code for those charts are included in the ChartDirector documentation.
My personal opinion is that the above two types of graidents are more "natural" types of gradients.
However, if you want some other kind of gradients, the most flexible way is to apply your own gradient patterns on each bar with code. For any bar in a multi-bar chart, you can use simple geometry to determine bar coordinates. For a vertical bar chart with a horizontal graident, the bar left and right coordinates are (without using magic numbers):
barLeft = plotAreaLeft + plotAreaWidth / noOfBars * (barGap / 2 + dataSetNo * (1 - barGap) / (noOfBars - subBarGap))
barRight = plotAreaLeft + plotAreaWidth / noOfBars * (barGap / 2 + (dataSetNo + 1 - subBarGap) * (1 - barGap) / (noOfBars - subBarGap))
By default, the barGap = 0.25 and subBarGap = 0.2. You may configure them explicitly using the BarLayer.setBarGap API.
Once you get the bar coordinates, you can set the gradient using the loop similar to my previous message.
Hope this can help.
Regards
Peter Kwan
|
Re: the same gradient for each bar |
Posted by John on Oct-27-2011 07:07 |
|
Hi,
I've been trying to do something similar with colouring each bar in a stack chart with a gradient colour (colour A to colour B, not using lighting). Unfortuantely i haven't been able to get it to work properly using the methods described in previous posts:
1.) hardcoded params to work out bar area (i.e. 0.1 or 0.9)
2.) dynamic params calculate bar area (i.e. plotAreaLeft + plotAreaWidth / noOfBars * (barGap / 2 + dataSetNo * (1 - barGap) / (noOfBars - subBarGap));)
with my graphs they need to be able to support varying numbers of bars and also varying orientation (vertical / horizontal), image attached for my progress so far.
The hardcoded vertical bar is close but i don't want to rely on the 0.1/0.9 as this seems messy and may cause isssues if the number or gaps are changed later on.
What is the best way to calculate the area of a bar (minus the gaps) using a calculation for vertical and horizontal bars?
Any help would be great
Thanks,
JC
|
Re: the same gradient for each bar |
Posted by John on Oct-27-2011 07:23 |
|
to give some more detail my dyanmic forumula so far is:
int itemArea = (IsHorizontal ? plotAreaHeight : plotAreaWidth);// / noOfBars;
double offSet = (IsHorizontal ? plotAreaY : plotAreaX);
barLeft = ((itemArea / noOfBars * i) - (1 - barGap / 2)) + offSet;
barRight = ((itemArea / noOfBars * i) - barGap) + offSet;
this is ok on vertical graphs now but horizontal is doesn't work because it seems squashes the bargap? |
Re: the same gradient for each bar |
Posted by John on Oct-27-2011 08:19 |
|
I've got a bit further with the following calculation:
// Declarations
double barGap = 0.25;
int noOfBars = data0.Length;
int plotOrientedArea = (IsHorizontal ? plotAreaHeight : plotAreaWidth);
double offSet = (IsHorizontal ? plotAreaY : plotAreaX);
layer.setBarGap(barGap);
// Calculation
for (int i = 0; i < data0.Length; ++i)
{
int barArea = plotOrientedArea / noOfBars;
double calBarGap = barArea * barGap;
barLeft = ((barArea * (i+1)) - (barArea - (calBarGap / 2))) + offSet;
barRight = ((barArea * (i+1)) - calBarGap) + offSet;
)......
The problem i've found is that the gap between bars is inconsistent...it alternatives between 6 & 7 pixels so as you progress through the bars the gradient is off by a pixel which by the end of a large chart is a big offset
any ideas? |
Re: the same gradient for each bar |
Posted by John on Oct-27-2011 13:02 |
|
I've managed to get the Vertical Chart working after some further troubleshooting, code below:
double barGap = 0.25;
layer.setBarGap(barGap);
int noOfBars = data0.Length;
int plotOrientedArea = (IsHorizontal ? plotAreaHeight : plotAreaWidth);
int offSet = (IsHorizontal ? plotAreaY : plotAreaX);
//color bar segments one by one
c.layout();
for (int i = 0; i < data0.Length; ++i)
{
int barLeft = 0;
int barRight = 0;
decimal barArea = (decimal) plotOrientedArea / noOfBars;
decimal calBarGap = (int) Math.Ceiling(barArea * Convert.ToDecimal(barGap));
barLeft = (int)((barArea * (i + 1)) - (barArea - (calBarGap / 2))) + offSet;
barRight = (int)((barArea * (i + 1)) - (calBarGap / 2)) + offSet;
I've had to round up the BarGap to a whole integer so that when i divide it by 2 i get a bit of padding around the Bar area otherwise with the inconsistent Gaps of 6-7pixels I work get missing gradient.
Unfortunately it doesn't work on the Vertical chart even thou logically i would have thought it should work. It seems that the X&Y values for the Vertical Bar are out...i.e. the first bar should start at:
50 (Offset of PlotArea) + BarGap (1-2pixels) = 51-52
but if you look at the picture its not working, if i set it to 50 then it will which is strange as the plotArea is offset by 50 and then there is a gap?!
Any help would be great
|
Re: the same gradient for each bar |
Posted by Peter Kwan on Oct-28-2011 03:03 |
|
Hi John,
The original code in this thread is written in 2005 and is for a "Multi-Color Bar Chart", in which each bar can has a different color. It is something like the first chart in http://www.advsofteng.com/gallery_bar2.html. The code computes the left and right edge of each bar, and then set the gradient color of each bar.
Since 2006, ChartDirector has several built-in bar shading methods. The charts in the link above shows bars with soft lighting effect, glass lighting effect, etc., so explicitly creating gradient colors for the bars are rarely necessarily.
For your case, I see that each bar segment is of one basic color, and the gradient is only for making the color brighter or darker. It is very similar to the built-in "soft lighting effect". So I suggest you to simply use the built-in "soft lighting effect". This only needs one line or code:
myBarLayer.setBorderColor(Chart.Transparent, Chart.softLighting(IsHorizontal ? Chart.Top : Chart.Left));
If you really would like to set a custom gradient for each bar segment yourself, the code in this thread is not applicable to a stacked bar chart (a BarLayer configured as Chart.Stack). It is because a stacked bar layer does not allow you to set a different color for each bar. (The API will not accept an array of colors.) It only allows you to set a different color for each data set. Computing a gradient using the coordinates of a single bar segment is not useful in this case, because the coordinates is unlikely to be applicable to other bar segments within the data set. Another issue in your code is that the method to compute the bar border may be incorrect, because for horizontal bars, the bars are in bottom/up order with the first bar at the bottom (your code seems to assume the reverse).
For your case, one method I can think of is to make each data set containing just one bar segment, so we don't need to worry about the gradient color not applicable to other bar segments within the data set. It is like (for horizontal stacked bars):
BarLayer layer = c.addBarLayer2(Chart.Stack);
double[] temp = new double[data0.Length];
for (int i = 0; i < data0.Length; ++i)
{
int barBottom = (int)(c.getPlotArea().getTopY() + c.getPlotArea().getHeight() * (data0.Length - i - 0.1) / data0.Length);
int barTop = (int)(c.getPlotArea().getTopY() + c.getPlotArea().getHeight() * (data0.Length - i - 1 + 0.1) / data0.Length);
temp[i] = data0[i];
layer.addDataSet(temp, c.linearGradientColor(0, barTop, 0, barBottom, 0xffffff, 0xff8080), i == 0 ? "Server # 1" : "");
temp[i] = data1[i];
layer.addDataSet(temp, c.linearGradientColor(0, barTop, 0, barBottom, 0xffffff, 0x80ff80), i == 0 ? "Server # 2" : "");
temp[i] = data2[i];
layer.addDataSet(temp, c.linearGradientColor(0, barTop, 0, barBottom, 0xffffff, 0x8080ff), i == 0 ? "Server # 3" : "");
temp[i] = 0;
}
(Note: The +/- 0.1 in the above code is to adjust for the bar gap, which is assumed to be 20% of the "slot".)
Hope this can help.
Regards
Peter Kwan |
Re: the same gradient for each bar |
Posted by Ron on Oct-28-2011 05:29 |
|
Thanks Peter.. it works like a charm... |
Re: the same gradient for each bar |
Posted by John on Oct-28-2011 07:08 |
|
Hi Peter,
Thanks for your help that works well for the horiztonal chart.
I have a quick question thou so i can under the logic. From what you've said the X&Y start at the top left for a Horizontal Charts? so when it adds the gradients from the bottom up the values are the offset + the height and then it increments down to draw the others (which is actually up visually).
This behaviour is different to a Vertical Chart where it starts at the bottom left for the X&Y?
Thanks for your help
John |
Re: the same gradient for each bar |
Posted by Peter Kwan on Oct-28-2011 16:28 |
|
Hi John,
linearGradientColor is defined using "pixel coordinates". In "pixel coordinates", the top-left corner of the entire image is (0, 0).
When you iterate the data using "for (int i = 0; i < data0.Length; ++i)", it is using data coordinates. In data coordinates, the origin is at the bottom-left corner of the plot area. For example, you can see the value "0" at the bottom-left corner of the plot area. If you add labels to the axis, the first label is at the bottom-left corner. It is the same for horizontal or vertical bars.
For vertical bars, the first bar must be near to the left of the plot area, and subsequent bars should flow right. In pixel coordinates, flowing right means to add to the x-pixel coordinate. So the left edge of the bar should be like:
barLeft = leftEdge + barSlotWidth * i
For horizontal bars, the first bar must be near the bottom of the plot area, with subsequent bars flowing up. In pixel coordinates, flowing up means to subtract to the y-pixel coordinate. So the bottom edge of the bar should be:
barBottom = bottomEdge - barSlotHeight * i
In summary, the bars always start at the bottom-left corner of the plot area, no matter it is vertical or horizontal. However, the formulas are different because for vertical bars, we are flowing horizontally from left to right, while for horizontal bars, we are flowing vertically from bottom to top.
Hope this can help.
Regards
Peter Kwan |
|