Data wrapping for angles and similar traces
jchart2d is a real-time charting library written in java.
Brought to you by:
achimwestermann
It would be nice to be able to use a line chart where the data is able to wrap over a certain domain. I could implement this myself using two traces and a creative custom axis range policy, but that would still leave unsightly jumps when the data jumps between, oh say, 180 and -180 degrees, and it would be harder to implement on multiple traces. I'm hoping you know of a better way to implement this.
Anonymous
Sorry Poik,
I did not get the point: What do you want to achieve? Any example links or pictures? Pick me up from the surface, diploma thesis was long ago ;-)
Achim
I did word my request a little strangely, my apologies. I'm looking for something similar to http://www.mathworks.com/help/techdoc/ref/unwrap.html but I was also curious about getting the axis to keep the labeled values within the value range instead of going outside it which would be a matter using modulus on the axis formatter. This is another cosmetic request, but it helps when you end up with data similar to the attached screenshot.
Thank you for your time.
I am getting closer but I am still not with you. This may be as I forgot most of my mathematics I learned in school or university.
Regarding the screenshot: How should it look like ( I am trying to come from the side I know, just think you are talking to a dumb user who just knows the UI). Do you want the negative parts be shifted to the positive side. Complex conjugations should be transformed to positive numbers?
Regarding the scale: how should it stay? Still show -180 degrees?
Before the unwrap transformation.
One way for it to look after.
Another possible look.
I've attached a screenshot and two edits. The first one is the version I had in mind, and the second one is the one that we were originally going to try for in our project.
Wow, I guess finally I get the problem on the jchart2d side:
- You could just change the data before adding the points.
- But then the labels would also change. And they should look the same?
Try having a look in AAxisTransformation. There are implementations for AxisLog10/AxisLogE.
The trick of the transformation: Original values of the trace points are never changed but their internal normalized values that are used to be able to scale the points into the target sized chart area are transformed.
There you could do something in the transform method like :
protected double transform(final double in) {
double out -Math.abs(in);
return out;
}
I guess you will know better about the transformation for "unwrap". Perhaps you had to store the previous "in" to detect a complex conjugated jump and either return -Math.abs(..) or Math.abs(..).
But caution: By now there is a bug: Not only the datapoints are transformed but also the labels. This is a filed bug for log axes that will be fixed.
kind regards,
Achim
I've used the AAxisTransformation before, it didn't occur to me to use it here. I have tried to make your suggested implementation as follows:
@Override
protected double transform(double in) throws IllegalArgumentException {
if (in-prevVal>=wrapVal) {
currentWrapping-=2*wrapVal;
} else if (prevVal-in>=wrapVal) {
currentWrapping+=2*wrapVal;
}
prevVal = in;
return in + currentWrapping;
}
I ran this as shown and it seems to get stuck in an infinite loop at each complex conjugated jump.
And when I run this with the += and -= switched, every time there is a jump, it jumps multiple times, and it transforms the entire line of data, not just the data after the jump. Correct me if I'm wrong, this method is called by more than just the addPoint methods. To properly use this, I would need a transform that is only called by the points the first time the points are drawn. That, or I could fix the viewpoint and completely weed out the uses of getMax() and getMin(), which would actually be a problem when the data unwraps to off the screen.
It would be simple to add to the abstract class a transformPoint(double) method that is over-writable and initially just returns the value from transform(double), if you want, I can create this and give you a patch. I think this is one of the few cases where it would be necessary. I'm not entirely sure how to make it only transform the data in order instead of all at once, though. Would I just need to set the currentWrapping value to 0 before the while (itPoints.hasNext()) loop in scaleTrace?
I should have said: "...get stuck in an infinite loop at the first complex conjugated jump.." since it never makes it past that one.
Hi Poik,
before I read your final suggestion of "transformPoint(double) I was thinking about this solution. Yes, transform is also invoked from other calls (getMin and max I guess...).
So you are free to send me the patch as described.
I do not fully understand
". I'm not
entirely sure how to make it only transform the data in order instead of
all at once, though. "
Perhaps overriding the "initPaintIteration" callback could allow you to initialize correctly for that? If I am not ringing your bell there feel free to try enlighten me a bit further about this (didn't read your code line by line)
good night,
Achim
My apologies, I had made an incorrect assumption. I did notice, though, to get it to change values initially, I had to edit the method "getScaledValue" which is deprecated. Is this intentional? If it's still used, why is it deprecated? Just curious.
Also, my apologies again, I realized after running this for a bit that to properly use this, as the min and max values aren't using the unwrap transform, the data runs off the chart. I guess one could fix this with creative edits to getMin and getMax, but the easiest way to do it would be to edit findMin and findMax of the AAxis class. Honestly, I think that just transforming the data as it comes in might just be necessary unless I really want to get that deep into editing the classes. Thank you for your support. I made a patch and uploaded it, but I'm starting to doubt how useful it will be.
Lastly, I couldn't help but notice while I was working on this that the AAxisTransformation code on Git doesn't support the constructor without the ScalePolicy. This could break people's code when they update. If you include a constructor that passes the AxisScalePolicyAutomaticBestFit in AAxis (and edit the others accordingly) it will fit this. If you wish, I can upload patches for your Axis classes to do this.
Hi Poik,
thank you for your feedback.
The deprecation you mentioned was a remainder. I once tried to enforce batch-scaling of all points at once but then noticed that on some occasions (single tracepoint was added/changed without breaking the min/max of any dimension) single-scaling is faster. I removed that.
Regarding the constructors: I'll add them later, no time just now.
Changing findMax/findMin in AAxis does not sound like a good idea. These methods should not do any transformation or different thing than finding min and max from the raw data. I assume side-effects.
Please note: There is this bug: https://sourceforge.net/tracker/?func=detail&aid=3291886&group_id=50440&atid=459734 and I thought that your problem matches the problem: That data (tracepoints) are scaled with the transformation but also (as min and max use transform too) the scale changes. For that bug I'd first like to follow the "transformPoint" approach. Not that I'd like to have a transform method for other stuff than transforming points. But I only want the points to be transformed while scaling and therefor offer "getMinForTransformation" which is only called from code that scales while other stuff (e.g. needed for scale labels) remains untoutched. Could you wait until I fix that bug and then see if it works for you too?
kind regards,
Achim