Skip to content
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

Dash mode #13

Open
dy opened this issue Sep 16, 2017 · 6 comments
Open

Dash mode #13

dy opened this issue Sep 16, 2017 · 6 comments

Comments

@dy
Copy link
Member

dy commented Sep 16, 2017

Distance-based dash is more svg-ish but prone to errors on big data, position-based dash is round error proof, but bad on scale.

We can switch that mode by the calculated distance.

Although it seems that we cannot track scale-independent distance.

@dy dy mentioned this issue Sep 18, 2017
7 tasks
@dy
Copy link
Member Author

dy commented Oct 1, 2018

Turns out that is an issue of plotly/plotly.js#2946.

@dy dy changed the title Add distance/position dash modes Dash mode Oct 1, 2018
@dy
Copy link
Member Author

dy commented Oct 1, 2018

When we have dash length more than a segment length, dash pattern draws inconsistently on segments join.

There are two basic approaches to the problem: length-based and global position-based with linked segment offsets. Both of them require re-calculating every segment's length or pattern offset, which is O(n) for dataRange change.

Possible approaches to reduce O(n) → O(c):

  • some tricky math for separate x/y distances
    • total length is defined via sum of sq.roots of sums of squares, there seems to be no way to split terms to separate x/y sums)
  • reversed rendering: walking by dashes and fetching data from texture with point coordinates.
    • that requires storing points with discrete length interval, ie. large distances will require large textures.
  • length-based clustering
    • + reduces complexity of redraw
    • − increases complexity of update
    • ? no clear way of calculating segment length
  • xy-based clustering
    • ? no clear way of calculating segment length

@etpinard
Copy link
Member

etpinard commented Oct 1, 2018

Thanks for taking a close look at this @dy !

which is O(n) for dataRange change.

Can you try benchmarking this for 1e5 and 1e6 pts? If we only have to compute this on x/y data updates, I'd be ok adding 10-100ms to our _module.calc step when line.dash is enable.

@alexcjohnson
Copy link
Contributor

Wow, that's a really annoying problem! @etpinard calc isn't enough to really match SVG. Imagine a curve with evenly spaced x values and y: [2,1,1,1,1,1,1]. Changing the y range - changing how many dashes fit in the first segment - would shift the dashes in all following segments, even though the length of those segments wouldn't change at all. In general a given segment's offset wrt the dash pattern depends on all previous segments in a way that doesn't have any simple relationship with the x and y scales.

@dy I wonder if there's an approximate solution that would work reasonably well for common situations at least - I'm thinking during initial setup (ie calc) we partition the curve into straight-ish sections that are expected to contain many dash patterns (ideally more than about 3), then in the shader (which only needs to know the section start & end points) we stretch/shrink the dash pattern within each section so that the section endpoints exactly match dash pattern ends (ie if 3.4 patterns fit in the section we stretch the pattern, within that section, so exactly 3 of them fit; if 3.6 fit we shrink it to 4 patterns) and draw it as a function only of the perpendicular distance along the section start to end vector.

In principle "straight-ish" and how long a section needs to be in order to fit many dash patterns depends on the scales, so even that would need to be recalculated as the ranges change... but perhaps we can come up with a heuristic that does a reasonable job. Dash patterns don't make much sense when the curve is very small or jagged anyway, so I'm not too concerned if we start to fail in those cases, as long as the smoother and larger cases work OK.

@dy
Copy link
Member Author

dy commented Oct 1, 2018

Yes, that strategy is somewhat similar to dashadjust property in SVG https://www.w3.org/TR/svg-strokes/#StrokeDashadjustProperty.
If we allow for not-so-ideal solutions, there even math tricks may work, like storing lengths by x/y. In this case we can provide correct number of dashes for purely vertical/horizontal lines, and any line can be displayed as vertical/horizontal via adjusting x/y scale. But in this case any diagonal will screw the number of dashes, and formula for that is multi-termed, and tbh I did not contrive correct linear approximation of that.

@alexcjohnson
Copy link
Contributor

Ah nice find - yes, kind of a hybrid of stroke-dashadjust: compress/stretch.

Not-so-ideal is fine. I think we can accept any solution that gets the general flavor of the dashes correct - ie people will be happy with the outcome as long as the dash ends are perpendicular to the path segment, the pattern doesn't abruptly shift from one segment to the next, and the dash & gap lengths are correct most of the time to within something like 20%. Seems to me the goal of dashes is either to distinguish multiple traces (and match them up with the legend) or to mark a curve or portion of a curve as somehow fuzzier (like an interpolation/extrapolation, confidence bands, that sort of thing), and neither of those cases cares about the precise dash lengths.

@dy dy mentioned this issue Oct 5, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants