Measurement Studio for .NET Languages

cancel
Showing results for 
Search instead for 
Did you mean: 

use and abuse of annotation/cursor/customdivision collections

I m still working on my project, still trying to display thousands of plots, cursors, annotation etc at the same time in my screen.

Currently I need to display many annotations, cursors and divisons. When they are out of the graph range I delete them :

while( _xAxis.CustomDivisions.Count != 0 && _xAxis.CustomDivisions[0].Value > clearStep )
{
_xAxis.CustomDivisions.RemoveAt( 0 );
}

(I call the function every 1 sec)

So I am always adding new divisions at the end of the collection and removing items at the begining of the collection.

It might be a bad idea depending on how is implemented the collection. So should I try to improve my code ? (for example I can replace the old divisions with the new ones)

Thank you for helping,
Pipo


PS : I m experiencing a display problem : a custom division with gridlines is blinking if it is on a division grid, on the screenshot both custom divisions are blinking (when the graph is moving of course).
0 Kudos
Message 1 of 8
(4,692 Views)
I found a strange behavior with annotations : FillStyle.FromImage + ImageAlignment.Tile + moving graph = moving image

I dont know if it is supposed to act like that, I made a little C# demo.
0 Kudos
Message 2 of 8
(4,666 Views)
Thanks for the project. The reason you are seeing this behavior is the brush is not being translated to fit the moving bounds when ImageAlignment.Tile is set. So the behavior looks like the image is moving when in fact you are just seeing a different portion of the tiled image. I filed a report to have this fixed. To fix the problem yourself you can create a CustomFillStyle derived from FillStyle. Here is an example that works.


public class TiledImage : FillStyle
{
private Image _image;

public TiledImage(Image image)
{
if(image == null)
throw new ArgumentNullException("image");

_image = image;
}

public override bool IsContextDependent
{
get
{
//means createBrush is called when the context bounds change.
//In our case the annotation image bounds.
return true;
}
}

public override Brush CreateBrush(object context, FillStyleDrawArgs e)
{
TextureBrush brush = brush = new TextureBrush(_image, WrapMode.Tile);
Rectangle bounds = e.ContextBounds;
//translate brush to bounds desired.
brush.TranslateTransform(bounds.X, bounds.Y);
return brush;
}
}


Now create and instance and assign it in your test project like so:


_annotation.ShapeFillStyle = new TiledImage(_imageList.Images[0]);
Message 3 of 8
(4,651 Views)
Regarding your original question, could you please describe the behavior you're trying to achieve with this approach, specifically the removing/adding of custom divisions? There may be a better way, but I need to know more about what you're trying to do in order to describe an alternate approach.

- Elton
0 Kudos
Message 4 of 8
(4,631 Views)
Hi Elton,
here is how I m using the divisions and cursors :

- I add a cursor each time a point is exceeding a threshold. Then I delete them when they are out of the screen (in fact when XPosition < XAxis.Range.Minimum - 1).
- I add a custom division to show additional informations that can explain the plot behavior (a strange behavior can be an error but it is not if we are near a magnetic field). The custom divisions are also deleted when they are out of the screen.
0 Kudos
Message 5 of 8
(4,621 Views)
Your current approach may be the easiest way. The adding/removing of items to/from the collection should not cause a problem. Another approach that you could try is to highlight the points that are above the threshold with the plot's custom drawing events instead of using cursors. For example, the following method highlights all points whose Y value is above the specified threshold value with an alpha-blended red circle with a yellow outline:


void HighlightThresholdPoints(AfterDrawXYPlotEventArgs e, double threshold)
{
if (e.Plot.HistoryCount > 0)
{
// This overload of ClipDataPoints returns the points that are
// currently in the plot's axis ranges.
double[] xData, yData;
e.Plot.ClipDataPoints(out xData, out yData);

for (int i = 0; i < xData.Length; ++i)
{
double xPoint = xData[i], yPoint = yData[i];

if (yPoint > threshold)
{
// Map the current data point to the corresponding screen
// coordinate.
PointF point = e.Plot.MapDataPoint(e.Bounds, xPoint, yPoint);

// Highlight the current data point.
using (Brush brush = new SolidBrush(Color.FromArgb(128, Color.Red)))
using (Pen pen = new Pen(Color.Yellow))
{
RectangleF highlightBounds = new RectangleF(point.X - 5, point.Y - 5, 10, 10);
e.Graphics.FillEllipse(brush, highlightBounds);
e.Graphics.DrawEllipse(pen, highlightBounds);
}
}
}
}
}


You could call this from the graph's AfterDrawPlot event. For example, you could add an event handler for the graph's AfterDrawPlot event and call HighlightThresholdPoints to highlight all points whose Y value is above 7 like this:


private void OnAfterDrawPlot(object sender, AfterDrawXYPlotEventArgs e)
{
HighlightThresholdPoints(e, 7);
}


The advantage of this approach is that all points that are above the threshold in the plot's axis ranges would always be highlighted and you don't have to worry about manipulating another cursor or keeping the contents of the collection in sync. The disadvantage is that you have to implement this part of the drawing yourself, but as you can see above, the graph and plot provide methods and events that that help a lot with this and the actual drawing logic that you would have to implement is not that bad.

Hope this helps.

- Elton
0 Kudos
Message 6 of 8
(4,608 Views)
Thanks alot for your time Elton.

Your approach is interesting and learnt me a few tips. However I the one hand I dont have a lot of cursors (I only add them when there is an error) and in the other hand I have a lot of points (1000 * 10 plots at the moment). Of course I could use a Queue to save bad points and only highlight them but I dont bother much about them.

My real problem is with custom divisions (and maybe with annotations... the customers now want vertival bands to show custom informations). As you can see in my first post screenshot, I have a lot of them printed on the graph.

Should I use the drawing events to display them ?


Pipo
0 Kudos
Message 7 of 8
(4,604 Views)
Regarding the blinking custom divisions, I suspect that what's happening is that the custom divisions and the grid lines are close, but not the same values. The graph stores values in data coordinates, which are doubles, and maps them to pixel screen coordinates, which are ints, which results in rounding. When your graph is charting, the rounding could be fluctuating between rounding up or rounding down, depending on the value, the plot area bounds, etc.

You could do this with the custom drawing events, but I think you would end up with the same issue unless you either coerced the mapped custom division screen coordinate to the corresponding grid line screen coordinate or handled drawing the grid lines, too.

I don't think you would have the same issue with annotations because A.) the vertical band would be wider and may not overlap with the grid line in the same way, and B.) annotations and grid lines are always drawn. The graph will only draw either the grid line or the custom division's grid line if they are mapped to the same screen coordinate, where the custom division grid line takes precedence.

- Elton
0 Kudos
Message 8 of 8
(4,587 Views)