# Graphing Questions -

19 views
ago

We're using graphing components that are based off the PulseOximeter example. So, stroked path and Bezier3.

Wondering if there is a way to render a dis-continuous line where between time t and t+n there is no line visible.

Or if there is a way to change the color of the line between a certain period of time.

Code we inherited from PulsiOxi is as follows:

``````// iterate through all visible data entries of the data storage
while (( x < StrokePath.Bounds.w + PixelPerValue ) && ( inx >= 0 ))
{
if ( inx < noOfData )
{
data = DataStorage.GetData( current - inx );
slope = DataStorage.GetSlope( current - inx );

data = (float)StrokePath.Bounds.h * ( data - (float)MinRange ) / (float)( MaxRange - MinRange );

if ( firstVal == true )
{
Path.Begin( 0, x - (int32)LineWidth, data /*StrokePath.Bounds.h  / 2 */);
Path.AddLine( 0, x, data );
firstVal = false;
}
else
{
Path.AddBezier3( 0, x - dx, lastData + lastSlope * ratio, x - dx, data - slope * ratio, x, data, noOfEdges );
}

lastSlope = slope;
lastData = data;
}

x = x + PixelPerValue;
inx = inx - 1;
}

Path.AddLine( 0, x + (int32)LineWidth, StrokePath.Bounds.h / 2 );``````

## 1 Answer

0 votes

Hello Mike,

Wondering if there is a way to render a dis-continuous line where between time t and t+n there is no line visible.

You can achieve such effects by splitting the single path is several sub-path. Let's assume, you want to split one curve in 3 parts. For this purpose you configure the Path object to be able to store 3 sub-paths. Then you can initialize each sub-path individually, e.g. with a Beziér curve. In this manner, each sub-path can contain separate curves not connected together. Please note the first parameter in the invocation of AddBezier(). It determines the sub-path to which the Beziér curve has to be added. See also Initialize a sub-path and Store data in a sub-path.

Or if there is a way to change the color of the line between a certain period of time.

In this application case you would need to store the individual curves in separate Path objects and connect each Path object to its individual Stroked Path view. In each view you can configure other color. In other words, if you want the curve to appear in 3 colors, then you need 3 Stroked Path views and 3 Path data objects. If the effect you want to achieve is just a color gradient, then it would be already possible to do this with a single Stroked Path view. In this case you can specify the colors for the left and right edges of the view and the displayed curve is interpolated linearly between the specified colors.

Does it help you further?

Best regards

Paul Banach

ago
We plot data and at times it may be invalid. So I need to visually indicate these invalid data points or path segments. And the number of them may be indeterminate or large. So I'm unsure if, as you suggested, having separate path objects will work if I have a pre-determined number of those objects.

A similar problem is we have events that take place that I would like to visually indicate with a symbol along the path line.

Perhaps the solution to this is not in tinkering with the paths themselves. But to generate a colored scrim or object(s) that scrolls with the X time access...?

Hello Mike,

the Path data object and Stroked Path view are optimised to store coordinates and raster from them the corresponding vector graphic shapes. For the sake of efficiency you should load the Path data object only with data points you want to display. Embedded Wizard own Path data objects are not intended to manage indeterminate or large amount of data point.

I would try following approach:

1. Manage all data in separate data storage - outside of the Path data object.

2. From the data storage extract the interesting range of data points and load the values in Path data objects. Doing this you can configure the Path data objects with the actually expected number of sub-paths and the number of values in each sub-path.

3. If necessary, create Path data objects dynamically depending on the actual situation. Similarly, you can create the Stroked Path views dynamically.

4. If necessary (when data changes), you can recreate or re-initialize the Path data objects as well as the StrokedPath views.

A similar problem is we have events that take place that I would like to visually indicate with a symbol along the path line.

5. I'm not sure whether I understand what you mean. Displaying some cursor, decorations, etc. together with vector graphic should not be an issue. You will need to know the corresponding coordinates and arrange the 'object' at this position.

Perhaps the solution to this is not in tinkering with the paths themselves. But to generate a colored scrim or object(s) that scrolls with the X time access...?

6. The Path object implements methods to scroll its contents. This means, when new data arrive, the oldest entries are removed before the new entries are added. Maybe this is what you mean with "scroll with the X time ...". See the section Evaluate and modify the coordinates stored in the Path Data object. Especially note the method ShiftNodes().

Does it help you further?

Best regards

Paul Banach

ago

Got it!

Wasn't too bad. Just created two further paths for the two types of invalid data states we can have. Then just did:  if(inInvalidState) PathInvalid.AddLine()

Looks like this: ago

Oh, I take that back. Works until there is another invalid set of data on the same path. Then it draws a line segment from the end of the first red line to the start of the next red line: I must need to create a new subPath every time there's an invalid data path segment....?

From my UpdateViewState()

``````
//  ZEROS PATH /////////////////////////////////////////////////////////////////////////
noOfEdges = PixelPerValue / 4 + 1;
current = DataStorage.Current;
noOfData = DataStorage.NoOfData;

// search for index and horizontal position of the first coordinate that is left of the views origin
inx = (ScrollOffset + Bounds.w) / PixelPerValue + 1;
x = ( ScrollOffset + Bounds.w ) % PixelPerValue - PixelPerValue;

// clear the path
Path_Zeros.InitSubPath( 0, noOfEdges * inx + 4 );

lastData = 0.0;
lastSlope = 0.0;
dx = PixelPerValue / 2;
firstVal = true;
ratio = 0.25;

// iterate through all visible data entries of the data storage
while (( x < StrokePath_Zeros.Bounds.w + PixelPerValue ) && ( inx >= 0 ))
{
if ( inx < noOfData )
{
data = DataStorage.GetData( current - inx );
slope = DataStorage.GetSlope( current - inx );

if(Application::Device.STATUS_Machine_State_Storage.GetData( current - inx ) == 30.0)
{
data = (float)StrokePath_Zeros.Bounds.h * ( data - (float)MinRange ) / (float)( MaxRange - MinRange );

if ( firstVal == true )
{
Path_Zeros.Begin( 0, x - (int32)StrokePath_Zeros.Width, data /*StrokePath.Bounds.h  / 2 */);
Path_Zeros.AddLine( 0, x, data );
firstVal = false;
}
else
{
Path_Zeros.AddBezier3( 0, x - dx, lastData + lastSlope * ratio, x - dx, data - slope * ratio, x, data, noOfEdges );
}

lastSlope = slope;
lastData = data;
}
}

x = x + PixelPerValue;
inx = inx - 1;
}

if(Application::Device.STATUS_Machine_State_Storage.GetData( current - inx ) == 30.0)
Path_Zeros.AddLine( 0, x + (int32)StrokePath_Zeros.Width, StrokePath.Bounds.h / 2 );
// END ZEROS PATH /////////////////////////////////////////////////////////////////////////

``````

ago

Tried adding a SubPaths counter "SubPaths_Zeros". Then tried .Close() ing the path. But that doesnt seem to fix the issue...

``````//  ZEROS PATH /////////////////////////////////////////////////////////////////////////
noOfEdges = PixelPerValue / 4 + 1;
current = DataStorage.Current;
noOfData = DataStorage.NoOfData;

// search for index and horizontal position of the first coordinate that is left of the views origin
inx = (ScrollOffset + Bounds.w) / PixelPerValue + 1;
x = ( ScrollOffset + Bounds.w ) % PixelPerValue - PixelPerValue;

Path_Zeros.SetMaxNoOfSubPaths( 4 );

// clear the path
Path_Zeros.InitSubPath( SubPaths_Zero, noOfEdges * inx + 4 );

lastData = 0.0;
lastSlope = 0.0;
dx = PixelPerValue / 2;
firstVal = true;
ratio = 0.25;

// iterate through all visible data entries of the data storage
while (( x < StrokePath_Zeros.Bounds.w + PixelPerValue ) && ( inx >= 0 ))
{
if ( inx < noOfData )
{
data = DataStorage.GetData( current - inx );
slope = DataStorage.GetSlope( current - inx );

if(Application::Device.STATUS_Machine_State_Storage.GetData( current - inx ) == 30.0)
{
data = (float)StrokePath_Zeros.Bounds.h * ( data - (float)MinRange ) / (float)( MaxRange - MinRange );

if ( firstVal == true )
{
Path_Zeros.Begin( SubPaths_Zero, x - (int32)StrokePath_Zeros.Width, data /*StrokePath.Bounds.h  / 2 */);
Path_Zeros.AddLine( SubPaths_Zero, x, data );
firstVal = false;
}
else
{
Path_Zeros.AddBezier3( SubPaths_Zero, x - dx, lastData + lastSlope * ratio, x - dx, data - slope * ratio, x, data, noOfEdges );
}

lastSlope = slope;
lastData = data;
}
}

x = x + PixelPerValue;
inx = inx - 1;
}

if(Application::Device.STATUS_Machine_State_Storage.GetData( current - inx ) == 30.0)
{
Path_Zeros.AddLine( SubPaths_Zero, x + (int32)StrokePath_Zeros.Width, StrokePath.Bounds.h / 2 );
Path_Zeros.Close(SubPaths_Zero);
SubPaths_Zero = SubPaths_Zero + 1;
}
// END ZEROS PATH /////////////////////////////////////////////////////////////////////////

``````

Hello Mike,

You are on the right track.

Works until there is another invalid set of data on the same path. Then it draws a line segment from the end of the first red line to the start of the next red line [...]

I must need to create a new subPath every time there's an invalid data path segment....?

Sure, if you want multiple, separate path segments then you will need to store them as individual sub-paths.

Tried adding a SubPaths counter "SubPaths_Zeros". Then tried .Close() ing the path. But that doesnt seem to fix the issue...

I'm not sure what you tried to achieve ... The Close() operation may be is too much. It causes the end position of the sub-path to be connected with the begin position of the sub-path. The resulting shape is then 'closed'.

Did you find the solution?

Best regards

Paul Banach

ago
I see, I'll avoid doing a Close() on a SubPath then.

I'm still confused as to how I should be iterating through SubpPaths. I assume I have to keep an index (what I meant by a SubPath counter) and call a new Path_Zeros.InitSubPath() with a new path index...? I'm confused as to when to InitSubPath(). Should it be on firstVal?  Or do I only do firstVal's Begin() and AddLine() once for the entire plot, and instead InitSubPath() based on the logic of the data (if its invalid)...?

Hello Mike,

I'm still confused as to how I should be iterating through SubpPaths. I assume I have to keep an index (what I meant by a SubPath counter) and call a new Path_Zeros.InitSubPath() with a new path index...? I'm confused as to when to InitSubPath(). Should it be on firstVal?  Or do I only do firstVal's Begin() and AddLine() once for the entire plot, and instead InitSubPath() based on the logic of the data (if its invalid)...?

You call InitSubPath() before you load data to the sub-path for the first time. Later, you can omit InitSubPath() invocation if the capacity of the sub-path does not change. It means, with InitSubPath() you instruct Embedded Wizard to reserve memory for given number of path edges (coordinate pairs). Consequently, if the number of edges within a sub-path grows, you will need to re-initialize the sub-path with bigger capacity. If the number remains unchanged, it is sufficient to invoke InitSubPath() at the first initialization time.

Finally, the Path objects can be seen as an array of sub-path objects. A sub-path object in turn can be seen as an array of coordinate pairs (edges). By using the methods SetMaxNoOfSubPaths() and InitSubPath() you determine the capacity of those arrays.

Does it help you?

Best regards

Paul Banach