Page 1 of 1

### Peak Shading Under a Curve

Posted: Thu Aug 08, 2019 2:56 pm
I am trying to implement shading under a curve. I have successfully used the information/examples from this post: https://www.steema.com/support/viewtopic.php?t=10307

That post explains how to use the SeriesRegionTool to color an area under a curve starting at one X value (X1), ending at a second X value (X2) with an origin value (Y) that defines the baseline of the shaded area by the points (X1, Y) and (X2, Y). The SeriesRegionTool works great for this!

I will now need to extend this solution to handle shading when the baseline points do not share the same Y value (i.e. the baseline is sloped). Here's an example image where the left curve is the straight baseline I currently have working with the SeriesRegionTool and the right curve shows the sloped baseline that I need help implementing: example-sloped-baseline.PNG (6.41 KiB) Viewed 3706 times
I'd like to continue using the SeriesRegionTool for this - but it doesn't seem to be able to handle multiple origin points. Can you please help me understand the best way to shade the area under the curve with a sloped baseline?

Thanks!!

### Re: Peak Shading Under a Curve

Posted: Fri Aug 09, 2019 8:13 am
pmartin wrote:
Thu Aug 08, 2019 2:56 pm
I think the only way you're going to be able to do this is to derive a new Tool from our SeriesRegionTool and then adapt it to your needs. Unfortunately some of the key methods of the SeriesRegionTool are private rather than protected virtual, but in order to help you we can share some of our source code with you:

Code: Select all

``````    public class MySeriesRegionTool : SeriesRegionTool
{
public MySeriesRegionTool(Chart c) : base(c) { }

protected override void ChartEvent(EventArgs e)
{
if (Series != null)
{
if (((e is BeforeDrawSeriesEventArgs) && (DrawBehindSeries))
|| (e is AfterDrawSeriesEventsArgs) && (!DrawBehindSeries))
{
DrawRegion();
}
}
}

/// <summary>
/// Using point x coordinate it calculates point y coordinate
/// </summary>
/// <param name="val">intersection point x coordinate</param>
/// <param name="y">returns intersection point y coordinate.</param>
/// <returns>Point index</returns>
private int IntersectionPoint(double val, out double y)
{
var i = 0;
y = Series.mandatory[i];
while ((val > Series.notMandatory[i]) && (i < Series.Count))
{
i++;
}

// We have two choices:
// #1: value is exactly at point coordinate
// #2: value is between two points - use linear interpolation to calculate y

if (val == Series.notMandatory[i])
{
y = Series.mandatory[i];
}
else
{
double k;
if ((i > 0) && (i < Series.Count))
{
k = Series.mandatory[i] - Series.mandatory[i - 1];
k /= (Series.notMandatory[i] - Series.notMandatory[i - 1]);
y = Series.mandatory[i - 1] + k * (val - Series.notMandatory[i - 1]);
}
}

return i;
}

private void DrawRegion()
{
if (Active && (Chart != null) && (Series != null))
{
var lb = Series.notMandatory.Minimum;
var ub = Series.notMandatory.Maximum;
if (!AutoBound)
{
lb = Math.Max(lb, LowerBound);
ub = Math.Min(ub, UpperBound);
}

// plot only if it makes sense
if ((ub > Series.notMandatory.Minimum) && (lb < Series.notMandatory.Maximum))
{
var first = IntersectionPoint(lb, out var yl);
var last = IntersectionPoint(ub, out var yu);
if (last < first)
{
Utils.SwapInteger(ref first, ref last);
}

var plen = last - first + 1;
var pts = new Point[plen + 4]; // need four extra points

for (var i = 0; i < plen; i++)
{
pts[i].X = Series.CalcXPos(i + first);
pts[i].Y = Series.CalcYPos(i + first);
}

// upper bound intersect point
pts[plen].X = Series.CalcXPosValue(ub);
pts[plen].Y = Series.CalcYPosValue(yu);

// upper bound origin point
pts[plen + 1].X = pts[plen].X;
pts[plen + 1].Y = UseOrigin ? Series.CalcYPosValue(Origin) : Series.GetVertAxis.IEndPos;

// lower bound origin point
pts[plen + 2].X = Series.CalcXPosValue(lb);
pts[plen + 2].Y = pts[plen + 1].Y;

// lower bound intersect point
pts[plen + 3].X = pts[plen + 2].X;
pts[plen + 3].Y = Series.CalcYPosValue(yl);

var gr = Chart.Graphics3D;
gr.Brush = Brush;
gr.Pen = Pen;

var zpos = DrawBehindSeries ? Series.EndZ : Series.StartZ;

var tmpR = gr.RectFromRectZ(Chart.ChartRect, zpos);
gr.ClipRectangle(tmpR);

gr.Polygon(zpos, pts);
gr.UnClip();
}
}
}
}
``````
You can see this working by using this class in the example you mentioned, i.e.

Code: Select all

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

var nSeries = 4;

var yPos = new int[nSeries];
var fast = new FastLine[nSeries];
var region = new MySeriesRegionTool[nSeries];

for (var i = 0; i < nSeries; i++)
{
yPos[i] = i * 5;
fast[i] = new FastLine(tChart1.Chart);
if (i % 2 == 0)
{
for (var j = 0; j < 200; j++) fast[i].Add(Math.Sin((double)j / 10) + yPos[i]);
}
else
{
for (var j = 0; j < 200; j++) fast[i].Add(Math.Cos((double)j / 10) + yPos[i]);
}

region[i] = new MySeriesRegionTool(tChart1.Chart);
region[i].Series = fast[i];
region[i].Origin = yPos[i];
region[i].UseOrigin = true;
region[i].Brush.Style = System.Drawing.Drawing2D.HatchStyle.Vertical;
region[i].Brush.Transparency = 100;
region[i].Pen.Visible = false;
region[i].Brush.ForegroundColor = fast[i].Color;
}
}
``````
Please be aware that deriving TeeChart classes is perfectly valid under our license, although we do count it as 'source-code modification' and as such give no support to such classes. It is worth mentioning that there are a couple of options if you are not able to make the changes yourself to your satisfaction:
1) request a new feature request on http://bugs.teechart.net/
2) ask sales@steema.com for a quote from us for the work