Page 1 of 2

Request for a new Series Style

Posted: Tue Sep 11, 2007 9:06 pm
by 8739068
On some of our 2D charts, we need to fill whole regions with a semi-transparent color (indicating invalid regions). We've tried doing it by filling the area with adjacent points... and adjusting the size of the points to accomplish a fill. However, that ends up being a LOT of points and your drawing becomes very slow.

What we really need seems to be a simple extension of what you have:

A 2D series style that takes:
= X, Y, X1, Y1 Add(double x, double y, double x1, double y1)
and then draws a rectangle with one corner at X,Y and the opposite corner at X1,Y1.
Need to be able to set the transparency, fill color, border color, visibility, all the standard features in the base series class.

Alternatively, it could take:
= X, Y, width, height Add(double x, double y, double width, double height)
and then draw a rectangle with one corner at X,Y and the opposite corner at X+width,Y+height.

The look would be similar to a tower series in 2D but each rectangle could be located anywhere in the drawing area of the chart instead of originating off a line of origin.
It would also be similar to a bubble series, which lets you specify a position and a size... but we don't want circles.

Finally, to make this maximally fast, we'd like to be able to drop in the four arrays (the X array, the Y array, the X1 array, and the Y1 array), rather than having to call Add(x, y, x1, y1) a hundred thousand times.


Can this request be incorporated into the next release of TeeChart for .NET V3?
If so what would be the time frame we could see this?

Posted: Wed Sep 12, 2007 10:02 am
by narcis
Hi Mike,

Have you tried to achieve this using Shape series and setting their style to rectangle? You could try doing something like this:

Code: Select all

		private void AddRectangle(double x0, double y0, double x1, double y1)
		{
			Steema.TeeChart.Styles.Shape shape1 = new Steema.TeeChart.Styles.Shape(tChart1.Chart);
			shape1.Style = Steema.TeeChart.Styles.ShapeStyles.Rectangle;
			shape1.Brush.Transparency = 60;

			shape1.X0 = x0;
			shape1.Y0 = y0;
			shape1.X1 = x1;
			shape1.Y1 = y1;
		}

Not quite what I need

Posted: Wed Sep 12, 2007 1:52 pm
by 8739068
A big requirement is to be able to add points using arrays of values. Your proposal would not satisfy that requirement I mentioned in my original posting
Finally, to make this maximally fast, we'd like to be able to drop in the four arrays (the X array, the Y array, the X1 array, and the Y1 array), rather than having to call Add(x, y, x1, y1) a hundred thousand times.

Posted: Wed Sep 12, 2007 2:17 pm
by narcis
Hi Mike,

Ok, I'll add your request to our wish-list to be considered for inclusion for future releases but, at the moment, I can't tell you if it will be implemented and when.

The only workaround for now would be that you provided arrays to a method which looped through those arrays and added a rectangle for each values collection. But obviously this is not as fast as directly assigning the arrays to series's ValueLists.

Creating our own

Posted: Wed Sep 12, 2007 2:47 pm
by 8739068
Can I inherit from the Shape style and get access to the necessary properties and methods to implement a solution to meet our needs? I want to avoid changing the TeeChart source code if possible, but I can view the source to see how things are implemented internally.

Shape will not work

Posted: Wed Sep 12, 2007 8:57 pm
by 8739068
As looked into the Shape series, that is not going to work for us.

We need to draw many rectangles. The Shape series only draws one shape from what I can tell. The tower series is much more like what we want to do, we just want to specify the origin of where it draws the rectangles.

Imagine a curve such as a sine wave drawn from 0 to pi. If you had to draw rectangles all within the sine wave and the x=0 axis. At the edges of the sine wave the rectangles will be small, while in the middle they will be large. This is a simple case.

I know for the above example there are ways to shade under a curve, but our requirements go beyond the shading capabilities so we need to be able to draw various rectangles. This is why the Tower series is close to what we need.

Please let me know if I am misunderstanding how the Shape series works.

Posted: Thu Sep 13, 2007 2:20 pm
by narcis
Hi Mike,

No, that's right. However, I hadn't thought about Bubble series which can be used with Rectangle style.

At the code example below I created a customized series style derived from Bubble series. This series, instead of using bubble's radius has 2 new ValueLists for rectangle's width and height. I've also implemented an Add method which supports arrays.

Code: Select all

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

using Steema.TeeChart.Styles;
using Steema.TeeChart.Drawing;

namespace RectangleBubbleSeries
{
	public class RectangleBubble : Bubble
	{
		private Graphics3D g;
		private ValueList widthValues;
		private ValueList heightValues;

		public RectangleBubble() : this(null) { }
		public RectangleBubble(Steema.TeeChart.Chart c)
			: base(c)
		{
			g = c.Graphics3D;

			widthValues = new ValueList(this, "Rectangle Width Values");
			heightValues = new ValueList(this, "Rectangle Height Values");
			Pointer.Style = PointerStyles.Rectangle;
		}

		public override void Draw()
		{
			base.Draw();
		}

		private void IncrementArray(ValueList list)
		{
			list.Count++;
			int tmp = list.Value.Length;
			if (list.Count > tmp)
			{
				if (list.Capacity > 0)
					tmp += list.Capacity;
				else
					if (list.Count > 3)
						tmp += list.Count / 4;
					else
						tmp += 100;

				double[] newValue = new double[tmp];
				list.Value.CopyTo(newValue, 0);
				list.Value = newValue;
			}
		}

		private void InsertChartValue(ValueList list, int valueIndex, double value)
		{
			IncrementArray(list);

			for (int t = list.Count - 1; t > valueIndex; t--) list.Value[t] = list.Value[t - 1];
			list.Value[valueIndex] = value;
		}

		public int Add(double x, double y, double w, double h, string text, Color color)
		{
			int tmp = Add(x, y, text, color);

			InsertChartValue(widthValues, tmp, w);
			InsertChartValue(heightValues, tmp, h);

			return tmp;
		}

		public void Add(Array xValues, Array yValues, Array wValues, Array hValues)
		{
			int numPoints = yValues.GetLength(0);
			widthValues.Count = numPoints;
			widthValues.Value = ConvertArray(wValues, numPoints);
			heightValues.Count = numPoints;
			heightValues.Value = ConvertArray(hValues, numPoints);			
			
			//RadiusValues not used, to avoid axes calculation exception only
			RadiusValues.Count = numPoints;
			RadiusValues.Value = heightValues.Value;

			Add(xValues, yValues);
		}

		public override void DrawValue(int valueIndex)
		{
			point.HorizSize = CalcXSizeValue(widthValues[valueIndex] / 2);
			point.VertSize = CalcYSizeValue(heightValues[valueIndex] / 2);

			//This draws using x and y values as the center of the Rectangle
			DrawPointer(CalcXPos(valueIndex), CalcYPos(valueIndex), ValueColor(valueIndex), valueIndex);
		}

	}

	public partial class Form1 : Form
	{
		public Form1()
		{
			InitializeComponent();
			InitializeChart();
		}

		private void InitializeChart()
		{
			tChart1.Aspect.View3D = false;

			RectangleBubble myBubble1 = new RectangleBubble(tChart1.Chart);

			//myBubble1.Add(0, 0, 4, 10, "rectangle 1", Color.Red);
			//myBubble1.Add(5, 10, 2, 4, "rectangle 2", Color.Blue);
			//myBubble1.Add(8, 20, 5, 5, "rectangle 3", Color.Yellow);

			const int numPoints = 10;

#if VS2005 && NULLABLE
      double?[] myX = new double?[numPoints];
			double?[] myY = new double?[numPoints];
			double?[] myW = new double?[numPoints];
			double?[] myH = new double?[numPoints];
#else
			double[] myX = new double[numPoints];
			double[] myY = new double[numPoints];
			double[] myW = new double[numPoints];
			double[] myH = new double[numPoints];
#endif

			for (int t = 0; t < numPoints; t++)
			{
				myX[t] = t;
				myY[t] = t;
				myW[t] = 1;
				myH[t] = t;
			}

			myBubble1.Add(myX, myY, myW, myH);
		}

	}
}
Please notice that to get the code above working at your end you'll need to make little changes to TeeChart sources which I have already made to ours and will be included in the next release.

Changes made to the sources:

1. In Custom.cs, at the Steema.TeeChart.Styles.Custom class, change the internal point property declaration to protected internal in the class constructor. From this:

Code: Select all

        internal SeriesPointer point;
To:

Code: Select all

        protected internal SeriesPointer point;
2. In Series.cs, at the Steema.TeeChart.Styles.Series class, change the internal ConvertArray method declaration to protected internal. From this:

Code: Select all

#if VS2005 && NULLABLE
		internal double?[] ConvertArray(Array a, int numPoints)
#else
		internal double[] ConvertArray(Array a,int numPoints)
#endif
To:

Code: Select all

#if VS2005 && NULLABLE
		protected internal double?[] ConvertArray(Array a, int numPoints)
#else
		protected internal double[] ConvertArray(Array a,int numPoints)
#endif
Hope this helps!

Perfecto!!!

Posted: Thu Sep 13, 2007 4:10 pm
by 8739068
You are a life saver. I think that will work well for us.

One comment. It looks like the code changes in the source might be a little newer than the latest release. I don't see a compiler constant, NULLABLE , defined anywhere. I don't think it will hurt us, but it did appear to be something recently added. I can imagine why it was added.

I cannot thank you enough. This saved me a few days of work.

Small problem

Posted: Thu Sep 13, 2007 9:43 pm
by 8739068
Looks like there is a problem if we use the Chart Editor.

It appears the new series needs to have an editor for it or at least a way to handle the case when the series type is not found. Steps to reproduce the bug

1. Create a form and drop a TeeChart control on it
2. Add a Chart Controller toolbar to the form and set its chart property to the TeeChart control you just added
3. In the form's code add a new RectangleBubble series to the chart.
4. Go to the ChartController tool bar and select the chart editor
5. Make sure the RectangleBubble series is selected and click on the Series tab
6. Get exception "System.ArgumentOutOfRangeException was unhandled
Message="Index was out of range. Must be non-negative and less than the size of the collection.\r\nParameter name: index"
Source="mscorlib"
ParamName="index"
StackTrace:
at System.ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument argument, ExceptionResource resource)
at System.ThrowHelper.ThrowArgumentOutOfRangeException()
at System.Collections.Generic.List`1.get_Item(Int32 index)
at Steema.TeeChart.Editors.SeriesEditor.ShowSeriesForm(Int32 index) in C:\TCC\Development\3rd Party\Source\TeeChartNetV3\Sources\TeeChart\Editors\Series\SeriesEditor.cs:line 116
....

Looks like it goes to look up the series type Utils.cs, aSeriesTypesOf is missing a definition for this new series. Which also leads to it needs an editor or at least a graceful way of handling this situation. It would be nice if there was an editor, but I realize this gets more involved on your part.

I looked at the Data tab and it is nice, it has columns for X, Y, Radius, Rectangle Height, Rectangle Width. I liked that.

Please let me know what direction you will take on this, so I can code accordingly.

Posted: Fri Sep 14, 2007 8:33 am
by narcis
Hi Mike,

You're very welcome. I'm glad to hear that fits your needs.
One comment. It looks like the code changes in the source might be a little newer than the latest release. I don't see a compiler constant, NULLABLE , defined anywhere. I don't think it will hurt us, but it did appear to be something recently added. I can imagine why it was added.


I think this is not that new. I don't know how this exactly works as I'm not in charge of the installers but some of those comments are removed from the distributed sources.
Looks like it goes to look up the series type Utils.cs, aSeriesTypesOf is missing a definition for this new series. Which also leads to it needs an editor or at least a graceful way of handling this situation. It would be nice if there was an editor, but I realize this gets more involved on your part.
Yes, this is because this custom series doesn't have a specific editor. One way to handle this and being able to partially work with the chart editor is modifying SeriesEditor.cs at Steema.TeeChart.Editor.SeriesEditor.ShowSeriesForm change the line:

Code: Select all

      EditorUtils.InsertForm(seriesForm,tabFormat);
to

Code: Select all

      if (seriesForm != null) EditorUtils.InsertForm(seriesForm,tabFormat);
The code I posted for this series is just a simple example to achieve something like what you requested. To add similar series into a TeeChart production release more work should be done on that field and series should be improved a lot.

Posted: Fri Sep 14, 2007 10:07 am
by narcis
Hi Mike,

As an update to my previous reply, a colleague pointed to me that in v3 we have RegisterSeries method which can be used to specify the editor you want to use for a series.

You can apply this in the example adding an Activate call in the constructor and implementing the method as shown here:

Code: Select all

		public RectangleBubble(Steema.TeeChart.Chart c)
			: base(c)
		{
			g = c.Graphics3D;

			widthValues = new ValueList(this, "Rectangle Width Values");
			heightValues = new ValueList(this, "Rectangle Height Values");
			Pointer.Style = PointerStyles.Rectangle;
			Activate();
		}

		private void Activate()
		{
			if (!Utils.SeriesTypesOf.Contains(typeof(RectangleBubble)))
			{
				Utils.RegisterSeries(typeof(RectangleBubble), typeof(Steema.TeeChart.Editors.Series.PointSeries), 1, 0);
			}
		}
Moreover, this would also allow you to create your own editor for this series style, you could derive it from PointSeries editor.

Hope this helps!

Great!!!

Posted: Fri Sep 14, 2007 1:48 pm
by 8739068
That is good news.

In one of your responses you hinted, that this series type might be added.

Will this series type be adding to a list of new features for a future release? or is it not worth the time and effort?

Posted: Fri Sep 14, 2007 1:56 pm
by narcis
Hi Mike,

Yes, I said that I added a feature request to our wish-list to implement a series like what you requested. However, this doesn't mean that its implementation will be like in the example I posted and may not even derive from Bubble series. The code I posted was the easiest way we could think of easily achieving something to fit your needs and without having to modify TeeChart sources much. Moreover, I think that if the custom series style I implemented had to go into a TeeChart production version it should be revised and would need several enhancements.

Follow-up and update

Posted: Fri Oct 19, 2007 3:47 pm
by 8739068
The RectangleBubble series proposed above is working, but we are seeing a couple of issues.

1. When I zoom in on a chart that contains a RectangleBubble Series, some or all of the RectangleBubble rectangles will disappear. It is strange. I don't know if there is a repaint issue or not. It appears to not draw a rectangle if the center of the rectangle is not within the view. Any advise on trouble shooting this?

2. There appears to be some rounding issues because when rectangles are adjacent sometimes we see overlap of 2 rectangles which shows up as a darker region, we also see gaps between adjacent rectangles. All seem to be 1 pixel due to rounding differences.

Suggestion for issue 2

The rounding errors are caused by the way you use the X.Y as the mid-point, and then subtract
half of the width,height to find one side, and add half of the width,height to find the other side.
The proper rounding to do in that case is difficult, because the rounding of the width needs to
be different in each direction, and different depending upon the direction the mid-point was rounded.

If instead you use the X,Y as the lower-left (or any corner) and then add the width,height to get the
upper-right (or opposite corner), then that would be easy to manage the rounding consistently.

Or, if we specified X1,Y1,X2,Y2 (the two corners), then round-off could be handled easily.
(And it would be faster, since we wouldn't need to subtract those just to have you add them back.)

Is there any way to make RectangeBubbleSeries draw rectangles without using mid-point based code?

Posted: Tue Oct 23, 2007 8:44 am
by narcis
Hi Mike,

As I told you, suggested RectangleBubbleSeries is just a workaround to achieve what you requested while no similar series is implemented in TeeChart.
1. When I zoom in on a chart that contains a RectangleBubble Series, some or all of the RectangleBubble rectangles will disappear. It is strange. I don't know if there is a repaint issue or not. It appears to not draw a rectangle if the center of the rectangle is not within the view. Any advise on trouble shooting this?
Not that I can think of. This also happens with bubble series where this series inherits from. Since the center of the point is not in the chart area it is not drawn. I'll add your request to our wish-list to be enhanced for future releases.
2. There appears to be some rounding issues because when rectangles are adjacent sometimes we see overlap of 2 rectangles which shows up as a darker region, we also see gaps between adjacent rectangles. All seem to be 1 pixel due to rounding differences.

Suggestion for issue 2

The rounding errors are caused by the way you use the X.Y as the mid-point, and then subtract
half of the width,height to find one side, and add half of the width,height to find the other side.
The proper rounding to do in that case is difficult, because the rounding of the width needs to
be different in each direction, and different depending upon the direction the mid-point was rounded.

If instead you use the X,Y as the lower-left (or any corner) and then add the width,height to get the
upper-right (or opposite corner), then that would be easy to manage the rounding consistently.

Or, if we specified X1,Y1,X2,Y2 (the two corners), then round-off could be handled easily.
(And it would be faster, since we wouldn't need to subtract those just to have you add them back.)

Is there any way to make RectangeBubbleSeries draw rectangles without using mid-point based code?
I inherited Bubble series to achieve this as I thought it was the easier option as it already handled bubble's radius so adding another dimension wasn't much difficult.

All series styles I can think can be used for that also use the same approach (e.g.: Points series).

An alternative, as I already suggested you is using Shape series which uses the approach you desire. Maybe, you could create a custom series style which internally uses a new Shape series for each rectangle that needs to be drawn.

Hope this helps!