ChartDirector General Forum Home   Search
InterLineLayer doesn't show properly when data of X is very large and value is too close
Posted by David on Jun-14-2023 11:39
Attachments:
I am using "Binary Data Series" to show signal ON/OFF status of a certain period in unit of micro-second.

double dataY[] = { 1, 0, 1, 0, 1, 1 };
double dataX[] = { 0, 21783974, 61984073, 71778214, 71783269, 90490793 };

dataY is on/off status. dataX is in unit of micro-second. value 71778214 and 71783269 are very close. I can still see them in "Logic Line" but they disappear in "ON/OFF Filling" and "On Only Filling".

I think I should see a white vertical line (ON Only Filling) or a red vertical line (ON/OFF Filling) with width of 1 pixel for the period between 71778214 and 71783269. Actually, I don't see it.

Could anyone help? Thx

Re: InterLineLayer doesn't show properly when data of X is very large and value is too close
Posted by Peter Kwan on Jun-14-2023 13:26
Hi David,

The charts are all correct. You need to think exactly what you want to see in various cases.

If two points are too close, they occupy the same pixel. In the top chart, the first point is drawn as a blue line. The the second point is also drawn as a blue line, overwrite the first point (as they are on the same pixel), so you see a blue line.

In the second chart, the first point is drawn as a red, and the second point is drawn as green, overwrite the first point (as they are on the same pixel), so you see green.

What you need to determine is what color to use in case many points are so close together that they are on the same pixel. You can consider the following case to determine exactly what you want to see:

Suppose you have many points (red, green, red, green, ...) very close together, so they are on the same pixel, what do you expect to see on that pixel? Should it be red, green, or whatever color the first/last point is? Is one color more important than the other color?

Suppose the green other color is just the "background", and the red color is what is important. In this case, in the second chart, after drawing it normally, you can draw a red step step on top. So any pixel that is both red and green will now show red.

Best Regards
Peter Kwan

Re: InterLineLayer doesn't show properly when data of X is very large and value is too close
Posted by David on Jun-14-2023 20:52
Hi Peter,

Thank you for quick reply and explanation. How about the "ON only filling"? It is what I am going to use in my application.

is there a way to do that as you suggested to "On/off filling" solution?

Thanks,
David

Re: InterLineLayer doesn't show properly when data of X is very large and value is too close
Posted by Peter Kwan on Jun-15-2023 15:03
Attachments:
Hi David,

For your case, I assume 1 is ON, and 0 is OFF. With this interpretation, your data contains a short period of turning OFF at x = 71778214 to 71783269. Do you mean you want to put an empty gap there so that the OFF is visible? Or do you mean the opposite - that if there is a short period of turning ON (which does not occur in your data), it should be visible?

Note that there may be conflicts if you require both (that a short period of turning OFF is visible as a gap, and also a short period of turning ON is visible too as a filled region). It is because a single pixel can contain both cases, with is conflicting. So one of the them must be invisible.

I have inverted your data, so that they is a now short period of turning ON (I guess this is your intention). I modified the original binaryseries sample code to obtain the attached chart. Basically, I added an additional layer to ensure every ON period will be at least one pixel wide.

void binaryseries(CChartViewer *viewer, int /* chartIndex */)
{
// The data for the chart
double dataY[] = { 0, 1, 0, 1, 0, 0 };
const int dataY_size = (int)(sizeof(dataY)/sizeof(*dataY));
double dataX[] = { 0, 21783974, 61984073, 71778214, 71783269, 90490793 };
const int dataX_size = (int)(sizeof(dataX)/sizeof(*dataX));

// In this example, we only use position 1, 3, 5 for the data series. Positions 0, 2, 4, 6 are
// empty and serve as gaps.
const char* labels[] = {"", "ON Only Filling", "",
"<*font,color=cc2200*>ON<*/font*> / <*font,color=00aa22*>OFF<*/font*> Filling", "",
"Logic Line", ""};
const int labels_size = (int)(sizeof(labels)/sizeof(*labels));

// Create a XYChart object of size 600 x 180 pixels
XYChart* c = new XYChart(600, 180);

// Add a title to the chart using 10 points Arial Bold font. Set top/bottom margins to 12
// pixels.
TextBox* title = c->addTitle("Binary Data Series Demonstration", "Arial Bold", 10);

// Tentatively set the plotarea at (100, 30) and of size 470 x 120 pixels. Use transparent
// border. Use grey (888888) solid line and light grey (ccccc) dotted line as major and minor
// vertical grid lines.
c->setPlotArea(100, 30, 470, 120, -1, -1, Chart::Transparent)->setGridColor(Chart::Transparent,
0x888888, Chart::Transparent, c->dashLineColor(0xcccccc, Chart::DotLine));

// Set axes to transparent
c->xAxis()->setColors(Chart::Transparent);
c->yAxis()->setColors(Chart::Transparent);

// Set the y axis labels
c->yAxis()->setLabels(StringArray(labels, labels_size));

// Set y-axis label style to 8pt Arial Bold
c->yAxis()->setLabelStyle("Arial Bold", 8);

// Set x-axis major and minor tick density to 50 and 5 pixels. ChartDirector auto-scaling will
// use this as the guideline when putting ticks on the x-axis.
c->xAxis()->setTickDensity(50, 5);

// Use "<*font=Arial Bold*>{value|mmm dd}" for the first label of an hour, and "{value|hh:nn}"
// for all other labels.
c->xAxis()->setMultiFormat(Chart::StartOfDayFilter(), "<*font=Arial Bold*>{value|mmm dd}",
Chart::AllPassFilter(), "{value|hh:nn}");

//
// A Logic Line can be achieved using a StepLineLayer in ChartDirector
//

// Shift the data by 4.5, so instead of 0 - 1, it is now 4.5 to 5.5, or fluctuate around the y =
// 5 (Logic Line label) position.

// Add step lines using the original and the reversed data
layer0->setXData(DoubleArray(dataX, dataX_size));

//
// To perform ON/OFF filling, we draw the logic line, and its reverse, and fill the region in
// between
//

// Shift the data by 2.5, so instead of 0 - 1, it is now 2.5 to 3.5, or fluctuate around the y =
// 3 (ON/OFF Filing label) position.
// Reverse the data, so the 0 becomes 1 and 1 becomes 0, and shift it as well.

// Add step lines using the original and the reversed data
layer1->setXData(DoubleArray(dataX, dataX_size));

// Fill the region between the two step lines with green (00aa22) or red (cc2200), depending on
// whether the original or the reserve is higher.

//
// The ON Only filling is the same as ON/OFF filling, except the OFF filling color is
// transparent
//

// Shift the data by 0.5, so instead of 0 - 1, it is now 0.5 to 1.5, or fluctuate around the y =
// 1 (ON Only Filing label) position.
// Reverse the data, so the 0 becomes 1 and 1 becomes 0, and shift it as well.

// Add step lines using the original and the reversed data
layer2->setXData(DoubleArray(dataX, dataX_size));

// Fill the region between the two step lines with green (00aa22) or transparent, depending on
// whether the original or the reserve is higher.

// Highlight transitions
std::vector<double> highLightX;
std::vector<double> highLightY;

DoubleArray shiftedLine2Data = shiftedLine2.result();
for (int i = 0; i < dataX_size - 1; ++i)
{
if ((dataY[i] == 1) && (dataY[i + 1] == 0))
{
highLightX.push_back((dataX[i] + dataX[i + 1]) / 2);
highLightY.push_back(shiftedLine2Data[i]);
highLightX.push_back(highLightX.back());
highLightY.push_back(shiftedLine2Data[i + 1]);
highLightX.push_back(Chart::NoValue);
highLightY.push_back(Chart::NoValue);
}
}
DoubleArray(highLightX.data(), highLightX.size()));

// Adjust the plot area size, such that the bounding box (inclusive of axes) is 10 pixels from
// the left edge, 10 pixels  below the title, 30 pixels from the right edge, and 10 pixels above
// the bottom edge.
c->packPlotArea(10, title->getHeight() + 10, c->getWidth() - 30, c->getHeight() - 10);

// Output the chart
viewer->setChart(c);
}

Best Regards
Peter Kwan

Re: InterLineLayer doesn't show properly when data of X is very large and value is too close
Posted by David on Jun-21-2023 22:03
Hi Peter,

This really helps.

I have another question. I put the chart in a sizeable pane window in which the height of the pane window can be adjusted. When the pane window is a square, chart looks very ugly because the height of each horizontal bar of chart close to 1/4 of the window height.
Is there a ways to specify a fixed height of each horizontal bar of chart. when height of window is too large, just leave empty space on top. When i have more chart (20+), it becomes unreadable when the height of pane window is not large enough. Is it possible to add a vertical scroll bar on right and the height of each chart is a reasonable fixed value?

Thank you very much in advance,
David

Re: InterLineLayer doesn't show properly when data of X is very large and value is too close
Posted by Peter Kwan on Jun-22-2023 03:18
Hi David,

The height of each bar is already specified in the code. The height is 1 axis unit, while the spacing between the bars (the distance between the center of two bars) is 2 axis units. The entire axis contains 6 units, because Axis.setLabels are used and there are 7 axis labels (some of them are empty strings), and by default the labels are at 0, 1, 2, .... 6. As the y-axis is docked to the left of the plot area, the pixel length of the axis is equal to the height of the plot area, which is 120 pixels in the sample code. So 1 axis unit is 120/6 = 20 pixels.

If you want the bar height to be constant, one method is to extend the axis scale proportionally to the plot area height, so that 1 axis unit remains at 20 pixels. Instead of Axis.setLabels, you may use the following method to configure the y-axis:

//c->yAxis()->setLabels(StringArray(labels, labels_size));
// The axis scale is proportional to the plot area height (I assume your code will
// the height is at least 120 pixels.
c->yAxis()->setLinearScale(0, 6 * c->getPlotArea()->getHeight() / 120, Chart::NoValue);
// Add the labels at 0, 1, 2, .... 6.
for (int i = 0; i < labels_size; ++i)