-
-
Notifications
You must be signed in to change notification settings - Fork 6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Scatter Charts touch point resolution algorithm is broken (fix included) #3875
Comments
I misunderstood the scope of the problem ... thought it was just the highlighter but it's the dataset that is unable to calculate the correct points based on a touch. The dataset doesn't have access to the transformer so it means that the solution is a bit less accurate. So the fix above is only a partial fix and still generates some errors due to the dataset making some broad mistakes about the closest entry to the X and Y datapoints being asked. Two objects need to be patched, the dataset and chart highlighter. The method is to use the broken implementation to make a sampling span over a percentage of the X values, then iterate through them to find the closest Y value. Then the highlighter has to be patched to determine if the closest X point is within range to bother highlighting. These two classes should drop in as fixes with the patched chart view object as above. A better implementation is possible if the thinking here is applied deeper into the code.
|
it's too long for me to digest. Can you make a short summary? are you able to send us a pull request so we could review? |
The short summary is that the code is only checking the X axis to find the closest data point to a touch. That's not valid. You need to check for the closest point on the X and the Y axis both. The patch checks both axes. The current algorithm simply does a binary search along the X axis. It looks for the closest point in terms of X position and then quits. So if you have this set of data for your chart: x=50, y=100 If the user touched at x=52, y=100 It's very obvious that the closest point to 52,100 is 50,100. But since the algorithm is only checking the X values, the point returned as closest is 51, 100000000000. So: core problem: you are using a one dimensional search to search a two dimensional space. Thus, the result can be correct sometimes, but wrong sometimes. The closer your data is to a linear plot the more accurate the existing algorithm will be. I neither program in Swift nor do I use git worth a damn so anything I try to put in will just screw up. All I can say is drop those classes into a project and they will fix the issue (except they were for 3.0.4 and you made a bunch of breaking changes again, to 3.3). If you can understand what the problem is (only checking X values, and assuming that the point with the closest X value is actually closest to the touch point, without considering Y), then either the solution as written or an alternate solution can be imagined. I will try to explain again visually using a chessboard. Consider this chess position of two queens and a bishop. . So in this case, if you touch the black queen, and ask me what is the closest white piece to the black queen, the answer is simple. It's the white queen. Because you need to look at the X and Y positions, and calculate the distance using both positions. then the green vector is shorter than the red vector, thus the white queen is closest. Anyone can answer this question correctly at a glance, but the math is one by calculating simple point distance from 8th grade.
The closest point will have the minimum distance between the touch and the entry. The current algorithm is one dimensional so is only considering the X axis. As a result this is what the current algorithm does, only considering X values, takes the green vector and says the white bishop is closer than the white queen. This is the core algorithmic error. You can design your own algorithm for finding the closest point to a user's touch, but you need to use both X and Y positions to find that closest point. Your problem: given an array of data points, find the closest point in the array to an arbitrary point T. Brute force solution:
If we know the array is sorted, then we can shortcut the search space by sampling only part of the array close to the X position the user touched. So we can say this is the array of sorted X values, everyone has this, sorted from min to max minX [----------------------------------------------------------------] maxX Now the user touched somewhere in the screen, we find the closest entry by means of the X axis to what the user touched. The current algorithm does this, returns it and stops without checking Y values. minX [------------------X---------------------------------------------] maxX So we can't stop here and just return X because we are not checking the Y values like in the chessboard. My approach is to continue, and take a sample of the entries with X values close enough to approximately be within a candidate radius (guessing the user's finger radius is about 32 pixels wide, I use that as a candidate to extract a set of candidate points from the sorted X values): [---------------xxxXxxx-----------------------------------------] We know they are within a finger's width in the X axis but we need to calculate the closest point still by individually checking them all for minimum distance. To do that we use the search algorithm above, to brute force search the sample space within the finger radius. Other solutions are possible. I wanted to work with the existing code as much as possible without rewriting stuff I don't understand, in a language I don't understand (Swift). The core though is understanding the algorithm that is being used is wrong. Hopefully this explains clearly what is wrong with the algorithm as well as explaining what I am doing to patch the existing algorithm |
Closed by #4721 |
What did you do?
I generated a large number of scatter points, and implemented the delegate message to respond to highlighting taps.
The tap position was obtained by calling back via:
When points are tapped I ran a popover at the touch position and this came up in the wrong position consistently unless the touch was extremely accurate, which is hard to do on a chart with a lot of datapoints. The current implementation shows this problem becoming exponentially worse with larger numbers of touch points.
This problem is likely present in various other chart types, there are some reports about bubble charts that I saw, and fixes were attempted but people still complain about the problem. It may be the same issue as this, I'm not sure.
What did you expect to happen?
The closest entry in X and Y distance from the touch point should be returned.
What happened instead?
The closest entry in X position is returned regardless of Y position and absolute distance to the touch point.
Visual description is as follows:
Solution
The solution to this is to repair the method
And implement a correct search algorithm for the touch point. The key issues are:
In order to fix (1) it is necessary to go up the call stack and hand down both the X and Y values instead of just the X, and furthermore to forward the touch point so it can be used as an anchor point for the search.
Note:
I am not a Swift programmer and I don't know the language. Furthermore I hate the language and reading it makes my brain scream, so I may have not adopted correct coding habits for this fix. I however tried to walk it out point by point so that at least it's a framework for someone to adopt the fix and correct whatever coding habits are no good. I'm also trying to trim and reduce calculations as much as possible, as i am dealing with large datasets (20,000 to 100,000 points), which reveals a lot of performance issues in the current code base. So I suggest that the cutoffs and method stay in place.
The result of the patch is that the correct highlight is chosen and the delegate gets the correct position of the selected data point when asking back to the chart.
The same touch now generates the correct point regardless of the packing of the data.
Code
This code was implemented as a plugin subclass to ChartHighlighter. You put it in place with:
Dropin subclass that fixes the calculations.
The text was updated successfully, but these errors were encountered: