Page Index Toggle Pages: 1 Send TopicPrint
Normal Topic Custom Rendering on a Timeseries (Part 2) (Read 2314 times)
hyper
YaBB Newbies
*
Offline


I Love MindFusion!

Posts: 14
Joined: Apr 8th, 2021
Custom Rendering on a Timeseries (Part 2)
Sep 21st, 2021 at 2:57pm
Print Post  
Here I'm including my code for Draw; and it's clear there's something about the graphics context I don't understand.

The Chart output should show SQUARES proportional to data, and colored either RED or GREEN at Y axis 90 or -90 but, as you can see, I get only the first -90 yAxis uses for ALL, and only GREEN used for ALL.

To possibly account for IMMUTABILITY of objects, I've created Arrays where each is created fresh for each data point; and that doesn't work either, to get the desired behavior.

Posting the Draw code, where I'm sure you'll find problem(s):

Code
Select All
            protected override void Draw(RenderContext context) {
                // verified, if (DRAW_PRINT) parentForm.getStrategy().ninjaPrint2(" ...calling Draw");
                if (_seriesIndex < 0) {
                    parentForm.getStrategy().ninjaPrint2(" Draw seriesIndex not provided ! ");
                    return;
                }
                if (_myPlot == null) {
                    parentForm.getStrategy().ninjaPrint2(" Draw Plot reference not provided ! ");
                    return;
                }
                Axis xAxis = context.GetXAxis();
                Axis yAxis = context.GetYAxis();
                // we can determine whether a data point falls within the visible Axes
                // _myPlot is the "component" we will use for the visibility testing
                int posCount = 0; // ensuring we get both positive and negative values, verified
                int negCount = 0;
                int pointCount = mySeries.doubleDateTimestampList.Count;
                // creating arrays of possibly immutable objects, for testing ??
                PointF[] myPoints = new PointF[pointCount];
                RectangleF[] rects = new RectangleF[pointCount];
                Brush[] brushes = new Brush[pointCount];
                for (int i = 0; i < pointCount; i++) { // all are same length
                    double _X = mySeries.GetValue(i, TIMESTAMP_DIMENSION); // x axis realtime timestamp
                    double _Y = mySeries.GetValue(i, YVALUE_DIMENSION); // y axis "price'
                    float d = (float)(mySeries.GetValue(i, DATA3D_DIMENSION) * 5); // the 3rd dimension size/color square
                    Color c = Color.Gray; // overwritten
                    if (d < 0) {
                        _Y = 90; // we are overriding the Y value to be either +90 or -90 temporarily
                        c = Color.Red;
                        ++negCount;
                    } else {
                        _Y = -90;
                        c = Color.Green;
                        ++posCount;
                    }
                    // now test whether the data values are within the visible Axes intervals
                    if (!xAxis.InRange(_X)) continue;
                    // not needed with this application; if ( !yAxis.InRange(_Y)) continue;
                    // it is within the visible x-axis plot area; so now convert the data to a pixel
                    myPoints[i] = GetPixel( // I guess each of these becomes immutable?
                        _X, xAxis,
                        _Y, yAxis,
                        _myPlot);
                    rects[i] = new System.Drawing.RectangleF( // these may also each be immutable?
                        myPoints[i].X - d / 2, myPoints[i].Y - d / 2, d, d);
                    brushes[i] = new System.Drawing.SolidBrush(c); // IDisposable
                    context.Graphics.FillRectangle(brushes[i], rects[i]);

                }
                // would be in a finally clause
                for(int i=0; i<brushes.Length; i++) {
                    if (brushes[i] != null) brushes[i].Dispose();
                }
                //if (posCount==0 || negCount==0) { // it appears we do have both pos and neg values, verified
                //    parentForm.getStrategy().ninjaPrint2(" Draw pos: " + posCount + " neg: " + negCount + " drawn");
                //}
            } // end Draw

 



And will also try to post the visual result, where GREEN squares are seen always at -90 on the y-Axis and nowhere else, and no color changes either.  Of course, I'm missing some key concept here, and I hope you'll clear it up for me, please.  This is a fast real-time chart with a lot of indicators, which I hope you'll be able to ignore !  Smiley



  

Size-block-rendering-bugs.PNG ( 415 KB | 149 Downloads )
Size-block-rendering-bugs.PNG
Back to top
 
IP Logged
 
Slavcho
God Member
*****
Offline


tech.support

Posts: 3343
Joined: Oct 19th, 2005
Re: Custom Rendering on a Timeseries (Part 2)
Reply #1 - Sep 21st, 2021 at 3:52pm
Print Post  
Hi,

I imagine either there aren't any negative "d" values returned for the (d < 0) check, or the FillRectangle call fails for a rectangle with negative width / height passed to RectangleF(..., d, d) constructor. If there are negative d's, try passing them by absolute value to RectangleF.

Regards,
Slavcho
  
Back to top
 
IP Logged
 
hyper
YaBB Newbies
*
Offline


I Love MindFusion!

Posts: 14
Joined: Apr 8th, 2021
Re: Custom Rendering on a Timeseries (Part 2)
Reply #2 - Sep 21st, 2021 at 5:31pm
Print Post  
THANK YOU, THANK YOU...

Yes, the d's are both positive and negative, which I had previously verified, but I made the MISTAKE of not using the float dAbs = Math.Abs(d) and then using dAbs in the RectangleF call... I was using negative d's and that was the problem.

That was the BUG, and it works now; perhaps I'll not need those Arrays anymore...

I certainly Appreciate ALL of your quick help; and I'm very happy with the solution now !!!

[EDIT] here's the expected behavior with the bug fix.  THANKS.

hyper

  

Custom-renderer-expected-behavior.PNG ( 165 KB | 148 Downloads )
Custom-renderer-expected-behavior.PNG
Back to top
 
IP Logged
 
hyper
YaBB Newbies
*
Offline


I Love MindFusion!

Posts: 14
Joined: Apr 8th, 2021
Re: Custom Rendering on a Timeseries (Part 2)
Reply #3 - Sep 22nd, 2021 at 7:29pm
Print Post  
Another detail I don't quite understand.

My Draw routine does not draw a line connecting successive data points, but the chart above shows a thin LIME colored line which always snaps precisely to the BLACK line ("price") and that is associated with each of my data points in the series, even though I'm coercing the values to +/- 90 for the SQUARES.

I have simply changed the Y when rendering the SQUARE, to a +90 or a -90; but I'd like that LIME colored line not to be drawn.

I wonder what is drawing that line, which is the Series3D data series index 1, which is "price" and the Black line is also drawn on "price".  [EDIT] I mean the yAxis here.

So what is happening in Draw appears to be more than what my code should generate; and something is drawing the connecting thin LIME colored line which is correctly drawn on "price" where there is a SQUARE to be rendered (but not on price).

That was confusing; but I'm sure there's a way to suppress that connecting line?

hyper
  
Back to top
 
IP Logged
 
Slavcho
God Member
*****
Offline


tech.support

Posts: 3343
Joined: Oct 19th, 2005
Re: Custom Rendering on a Timeseries (Part 2)
Reply #4 - Sep 23rd, 2021 at 5:52am
Print Post  
Hi,

If adding your custom renderers to a standard LineChart control, that could be the output of its default LineRenderer. In such case you should be able to hide it by removing the first element of chart.Plot.SeriesRenderers.

Some other options you have:
- do not add any series to LineChart.Series collection but keep them in a separate list;
- use the dashboard API shown in tutorials to start with an empty Plot;
- subclass the BiaxialChart class to contain only your custom renderer (in otherwise same layout as other chart controls from the library), e.g. here's the gist of LineChart implementation:

Code
Select All
public class LineChart : BiaxialChart
{
	public LineChart()
		: this(new LineRenderer(new ObservableCollection<Series>()))
	{ }

	private LineChart(LineRenderer seriesRenderer)
		: base(seriesRenderer)
	{
		this.seriesRenderer = seriesRenderer;
	}

	public override ObservableCollection<Series> Series
	{
		get { return seriesRenderer.Series; }
		set { seriesRenderer.Series = value; }
	}

	protected override SeriesRenderer SeriesRenderer
	{
		get { return seriesRenderer; }
	}
... 



Regards,
Slavcho
Mindfusion
  
Back to top
 
IP Logged
 
Page Index Toggle Pages: 1
Send TopicPrint