flo_curves icon indicating copy to clipboard operation
flo_curves copied to clipboard

Offset gets really lumpy at large offset deltas.

Open MatthewBlanchard opened this issue 4 years ago • 6 comments
trafficstars

Capture

The purple ribs here represent the offsets. I didn't really notice this problem until I got editing going in real time. This is with modifications that correct the divide point to be 1/2 of length instead of 1/2 of t, but the results are approximately the same either way.

Capture

This is with the constant inside curve.rs set to like .9999 too. It seems like this just makes the lumpiness higher frequency.

Capture

You can see here it shows up in more reasonable examples too.

I wonder if reducing the curvature of the output beziers as you get further from the curve itself could work? I'm going to try to come up with some method of bringing the handles closer to the output curve as they get farther from the input curve.

It also seems linked more with a change from start -> finish than with the actual value of offset itself as well.

MatthewBlanchard avatar Feb 24 '21 21:02 MatthewBlanchard

Lerping the handles toward the control points by 1. - min(start_offset, end_offset) / max(start_offset, end_offset) does work in getting rid of the lumpiness but it comes with a ton of problems of it's own that make it an unsuitable patch for this. I knew it was a bit naive, but I figured I'd report that I attempted it anyway.

MatthewBlanchard avatar Feb 26 '21 01:02 MatthewBlanchard

It looks like the scaling algorithm starts to break down the larger the difference in offsets: I think there's probably a critical ratio between the start and the end points where the control points start to overlap too much and this effect starts to show up. I suspect it's fixable but I'm going to need to spend some time on it.

In the meantime, though, I've just pushed a new offsetting routine I've been thinking of including for a while that uses the least-mean-squared curve fitting algorithm instead. This makes it possible to use any function for the offset and will always produce really good approximations, but is much slower than the scaling algorithm, and has a slight tendency to produce straight lines if you don't use enough subdivisions. It's called like this:

bezier::offset_lms_sampling(&initial_curve, |t| -((5.0-200.0)*t+200.0), 20, 4.0);

(We can use large values for max_error here I think as we expect the points to already roughly lie on a bezier curve)

Here's a side-by side comparison, old offset in green, new in red, slightly different offsets to make the differences more clear:

Screenshot 2021-02-26 at 21 20 32

Logicalshift avatar Feb 26 '21 21:02 Logicalshift

All that said, I just realised I was using the wrong scale factors for the control points when the offsets were changing. I've fixed this issue too, so the error of that algorithm is now much less. Here's the LMS and the newly fixed scaling algorithm overlaid on top of each other:

Screenshot 2021-02-26 at 22 11 54

I had much less pronounced 'lumpiness' in the example curve I chose, but you can see it has been eliminated here.

The issue was the routine was using the wrong scale factors for moving the control point - they should be scaled with the offsets at t=1/3 and t=2/3 rather than at the start and end of the curve.

Logicalshift avatar Feb 26 '21 22:02 Logicalshift

This is an update not to poorly describe another bug (lumpy is not exactly a technical term), but to thank you. The new offset_lms is absolutely fantastic, performant, and it's outputs are more stable as points change. Flo_curves is an awesome library and you are an awesome maintainer. You've replied to every one of my issues. I plan to move as much of my existing math code over to using flo_curve's primitives as they're higher quality than mine.

You mentioned it being slower than the other offset code, but on my system in release mode it takes <1ms to offset a piecewise collection of curves.

The addition of the closure to describe offsets over the curve allowed me to add cosine and cubic interpolation which seriously improves the output by removing discontinuities where the offsets meet. Here is your offset code + a bunch of code I wrote for our editor and stroking library in action:

https://user-images.githubusercontent.com/310356/110059324-00314d80-7d32-11eb-8a6c-e8edcca30751.mp4

The results are absolutely great, and the way the new offset handles cusps and strange curvature at control points is much better.

MatthewBlanchard avatar Mar 05 '21 02:03 MatthewBlanchard

On the topic of the new offset algorithm I'd definitely add a way to specify tangent offsets too. In my fork I simply made the closure return (f64, f64) and offset by the first along the normal, and the second along the tangent.

MatthewBlanchard avatar Mar 06 '21 04:03 MatthewBlanchard

Hi,

I've now added a function that can do this: distort_curve. It's not quite the same as what you've asked for here, as it just allows for arbitrary displacement along the usual x and y axis instead of the normal axis, but as it provides the t value as well as the location on the curve for each point, it should be reasonably simple to use the tangent and normal to get the effect you're looking for.

Logicalshift avatar Apr 02 '21 14:04 Logicalshift