Profile Picture

color the area between two lines

Posted By Ben Alexander 14 Years Ago
Author
Message
Ben Alexander
Posted 14 Years Ago
View Quick Profile
Forum Newbie

Forum Newbie (5 reputation)Forum Newbie (5 reputation)Forum Newbie (5 reputation)Forum Newbie (5 reputation)Forum Newbie (5 reputation)Forum Newbie (5 reputation)Forum Newbie (5 reputation)Forum Newbie (5 reputation)Forum Newbie (5 reputation)

Group: Forum Members
Last Active: 14 Years Ago
Posts: 5, Visits: 1
I'm looking to make a particular plot, and I'm wondering if Nevron Chart provides an easy way to do it.

The plot I want to make for our biologists is a 'leverage plot with confidence bands', which should look something like this: it's basically a 2-d line plot, with a straight line going across the screen with a set slope, and then with curves on either side of the center line, with each curve bending away from the straight line on both ends (for a rough example, see the graphic at this website: http://mtsu32.mtsu.edu:11308/regression/level2/cbands/concept.htm). The core idea is that I want to create a figure that looks like the sideways projection of an 'hour glass', but the hour glass at a tilt and with a line running down the center (through the 'hole' in the hour glass where the sand would flow).

So far so good. Up until now I've been making these plots without color and the users have been basically satisfied. Now, however, they're wondering if I could add color: they want me to take the area between the two curved lines and make it, say, light blue. I'm having trouble, however, figuring our how to do this with the Nevron library. The NAreaSeries invariably pays attention to the y-values or the curves, while my leverage plots might end up in any part of the XY plane: maybe the whole plot will be above y=0, or maybe below it, or maybe crossing it at some location. Also, I don't want any colors above or below the confident bands -- only between them. Therefore I'm thinking that the NAreaSeries, stacked or unstacked, isn't the answer.

But what other chart type could I used to get the effect I'm seeking? And if there is a chart type that can give me what I want, would it then also handle one additional extension, namely 'prediction bands'? This plot would be very similar to the one I've already described, but would add an additional colored area -- picture this time the projection of a fat hour-glass in orange, and then superimpose a skinnier hour-glass with a different color on top of the fat hour-glass, and then place our center line running through the center of the skinnier hour glass? Is any of this possible? Through the years I've made many a plot with the Nevron package that our biologists have called quite beautiful -- surely there's a way to make this new plot too(?)

Thanks for any ideas,
Ben

bob milanov
Posted 14 Years Ago
View Quick Profile
Supreme Being

Supreme Being (152 reputation)Supreme Being (152 reputation)Supreme Being (152 reputation)Supreme Being (152 reputation)Supreme Being (152 reputation)Supreme Being (152 reputation)Supreme Being (152 reputation)Supreme Being (152 reputation)Supreme Being (152 reputation)

Group: Forum Members
Last Active: 6 Months Ago
Posts: 153, Visits: 11

Hi Ben,

There are two things that come to mind:

1. You can use custom painting.

2. You can use a mesh surface chart looked at the top to custom draw this region.

The following code shows how to achieve this effect usign custom painting:

private void Form1_Load(object sender, EventArgs e)

{

NChart chart = nChartControl1.Charts[0];

NLineSeries line = new NLineSeries();

line.UseXValues = true;

line.DataLabelStyle.Visible = false;

line.Values.Add(10);

line.XValues.Add(10);

line.Values.Add(20);

line.XValues.Add(20);

chart.Series.Add(line);

chart.PaintCallback = new NCustomPaintCallback(nChartControl1, chart);

}

public class NCustomPaintCallback : NPaintCallback

{

NChart m_Chart;

NChartControl m_Control;

public NCustomPaintCallback(NChartControl control, NChart chart)

{

m_Chart = chart;

m_Control = control;

}

/// <summary>

/// Occurs after the panel is painted.

/// </summary>

/// <param name="panel"></param>

/// <param name="eventArgs"></param>

public override void OnAfterPaint(NPanel panel, NPanelPaintEventArgs eventArgs)

{

NGraphics graphics = eventArgs.Graphics;

NFillStyle fill = new NColorFillStyle(Color.FromArgb(125, Color.Red));

NScale2DToViewTransformation transform = new NScale2DToViewTransformation(m_Control.View.Context,

m_Chart,

(int)StandardAxis.PrimaryX,

(int)StandardAxis.PrimaryY);

// create a simple polygon with coordinates close the to the line

NPointF[] points = new NPointF[4];

NPointF begin = new NPointF(10, 10);

NPointF end = new NPointF(20, 20);

double dx = begin.X - end.X;

double dy = begin.Y - end.Y;

float length = (float)Math.Sqrt(dx * dx + dy * dy);

NLineF line = NLineF.FromTwoPoints(begin, end);

NRayF ray = line.ToNRayF();

NVector2DF normVector = ray.DirectionVector.NormalVector;

normVector.Multiply(50);

points[0] = begin + ray.PointFromTime(0);

points[1] = begin + ray.PointFromTime(length);

points[2] = begin + ray.PointFromTime(length) + normVector.ToNPointF();

points[3] = begin + ray.PointFromTime(0) + normVector.ToNPointF();

for (int i =0; i < points.Length; i++)

{

NVector2DD vecScalePoint = new NVector2DD(points[i].X, points[i].Y);

NPointF viewPoint = new NPointF();

transform.Transform(vecScalePoint, ref viewPoint);

points[i] = viewPoint;

}

graphics.PaintPolygon(fill, null, points);

}

}

I'll check if we can add a polygon series for this purpose as it's missing badly from the component.

Hope this helps - let me know if you meet any probelms.

Best regards,
Bob



Ben Alexander
Posted 14 Years Ago
View Quick Profile
Forum Newbie

Forum Newbie (5 reputation)Forum Newbie (5 reputation)Forum Newbie (5 reputation)Forum Newbie (5 reputation)Forum Newbie (5 reputation)Forum Newbie (5 reputation)Forum Newbie (5 reputation)Forum Newbie (5 reputation)Forum Newbie (5 reputation)

Group: Forum Members
Last Active: 14 Years Ago
Posts: 5, Visits: 1

Bob;

Thanks very much for posting your solution -- I modified the code you provided to suit our circumstances and the resulting method does everything I'd been hoping for.  My callback class looks like this:

 

       public class NCustomPaintCallback : NPaintCallback {

            private NChart m_Chart;
            private NChartControl m_Control;
            private ConfidenceIntervalCalculator _confidenceIntervalCalculator = null;

            public NCustomPaintCallback( NChartControl control,
                                         NChart chart,
                                         ConfidenceIntervalCalculator p_confidenceIntervalCalculator ) {
                m_Chart = chart;
                m_Control = control;
                _confidenceIntervalCalculator = p_confidenceIntervalCalculator;
            }


            /// Occurs after the panel is painted.
            public override void OnAfterPaint( NPanel panel, NPanelPaintEventArgs eventArgs ) {

                NGraphics graphics = eventArgs.Graphics;
                NFillStyle fill = new NColorFillStyle( Color.FromArgb( 125, Color.Red ) );
                NScale2DToViewTransformation transform = new NScale2DToViewTransformation( m_Control.View.Context,
                                                                                           m_Chart,
                                                                                           (int)StandardAxis.PrimaryX,
                                                                                           (int)StandardAxis.PrimaryY );

                NPointF[] points = new NPointF[4]; // our polynomial

                for ( int i = 0; i < _confidenceIntervalCalculator.lowerConfidenceBandPointList.Count-1; i++ ) {

                    points[0] = new NPointF( Convert.ToSingle( _confidenceIntervalCalculator.upperConfidenceBandPointList[i].X ),
                                             Convert.ToSingle( _confidenceIntervalCalculator.upperConfidenceBandPointList[i].Y ) );
                    points[1] = new NPointF( Convert.ToSingle( _confidenceIntervalCalculator.upperConfidenceBandPointList[i + 1].X ),
                                             Convert.ToSingle( _confidenceIntervalCalculator.upperConfidenceBandPointList[i + 1].Y ) );
                    points[2] = new NPointF( Convert.ToSingle( _confidenceIntervalCalculator.lowerConfidenceBandPointList[i + 1].X ),
                                            Convert.ToSingle( _confidenceIntervalCalculator.lowerConfidenceBandPointList[i + 1].Y ) );
                    points[3] = new NPointF( Convert.ToSingle( _confidenceIntervalCalculator.lowerConfidenceBandPointList[i].X ),
                                             Convert.ToSingle( _confidenceIntervalCalculator.lowerConfidenceBandPointList[i].Y ) );

                    for ( int j = 0; j < points.Length; j++ ) {
                        NVector2DD vecScalePoint = new NVector2DD( points[j].X, points[j].Y );
                        NPointF viewPoint = new NPointF( );
                        transform.Transform( vecScalePoint, ref viewPoint );
                        points[j] = viewPoint;
                    }

                    graphics.PaintPolygon( fill, null, points );
                }


            }

        }

 

So now I'm completely happy with the plot, with only one exception.  When I now use the mouse to zoom in on my resulting graphic (using the NDataZoomTool) the fill colors wash over the boundaries of the axes (that is, everything scales correctly, but the color doesn't stop at the axes boundaries -- instead it continues all the way to the edge of the screen).  Is there a way to force the filled area I'm adding to obey the boundaries of the axes in a zoomed view, or to I need to go in by hand and decide which portions of which polynomials should properly be visible, and then only use the PaintPolygon method on that restricted area?

Either way, thanks for your original solution.  The graphic produced by my modified code is really quite nice, and a vast improvement over my original line plot.

Ben



bob milanov
Posted 14 Years Ago
View Quick Profile
Supreme Being

Supreme Being (152 reputation)Supreme Being (152 reputation)Supreme Being (152 reputation)Supreme Being (152 reputation)Supreme Being (152 reputation)Supreme Being (152 reputation)Supreme Being (152 reputation)Supreme Being (152 reputation)Supreme Being (152 reputation)

Group: Forum Members
Last Active: 6 Months Ago
Posts: 153, Visits: 11

Hi Ben,

I see - you have to apply clipping:

            NGraphics graphics = eventArgs.Graphics;

   NAxis horzAxis = m_Chart.Axis(StandardAxis.PrimaryX);
   NAxis vertAxis = m_Chart.Axis(StandardAxis.PrimaryY);

            NScale2DToViewTransformation trasnform = new NScale2DToViewTransformation(eventArgs.Context, m_Chart, (int)StandardAxis.PrimaryX, (int)StandardAxis.PrimaryY);

            NPointF topLeft = new NPointF();
            NPointF bottomRight = new NPointF();

            // transform the axis ranges to view coordinates
            trasnform.Transform(new NVector2DD(horzAxis.Scale.RulerRange.Begin, vertAxis.Scale.RulerRange.End), ref topLeft);
            trasnform.Transform(new NVector2DD(horzAxis.Scale.RulerRange.End, vertAxis.Scale.RulerRange.Begin), ref bottomRight);

            int x1 = (int)(topLeft.X + 0.5);
            int x2 = (int)(bottomRight.X + 0.5);
            int y1 = (int)(topLeft.Y + 0.5);
            int y2 = (int)(bottomRight.Y + 0.5);

            // normalize (in case some of the axes is inverted)
            if (x1 > x2)
            {
                int temp = x1;
                x1 = x2;
                x2 = temp;
            }

            if (y1 > y2)
            {
                int temp = y1;
                y1 = y2;
                y2 = temp;
            }

            // apply clipping
            graphics.DeviceGraphics.Clip = new Region(
                new Rectangle(x1, y1, x2 - x1, y2 - y1));

            // painting goes here

            // set clip to empty region
            graphics.DeviceGraphics.Clip = null;

Hope this does the job - let me know if you meet any probems...

Best regards,
Bob



Ben Alexander
Posted 14 Years Ago
View Quick Profile
Forum Newbie

Forum Newbie (5 reputation)Forum Newbie (5 reputation)Forum Newbie (5 reputation)Forum Newbie (5 reputation)Forum Newbie (5 reputation)Forum Newbie (5 reputation)Forum Newbie (5 reputation)Forum Newbie (5 reputation)Forum Newbie (5 reputation)

Group: Forum Members
Last Active: 14 Years Ago
Posts: 5, Visits: 1

Bob;

The clipping you recommended worked perfectly.  Thanks very much for the help.

Ben





Similar Topics


Reading This Topic