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

Feature: convert sharp corners to round corners #515

Open
jiangzhouq opened this issue Jul 12, 2022 · 10 comments
Open

Feature: convert sharp corners to round corners #515

jiangzhouq opened this issue Jul 12, 2022 · 10 comments

Comments

@jiangzhouq
Copy link

When I use a knife to cut, the sharp corners are not handled very well,
easy to lift,

@abey79
Copy link
Owner

abey79 commented Jul 12, 2022

I'm not sure I understand what you mean. Do you mean this?

image

@jiangzhouq
Copy link
Author

I'm not sure I understand what you mean. Do you mean this?

image

yes , man!can vpype do this job? I read the guide and didn’t find.

@abey79
Copy link
Owner

abey79 commented Jul 12, 2022

No, there is no such feature for the time being. It would make for a great addition and/or plug-in though!

@jiangzhouq
Copy link
Author

Thx for reply!

@abey79 abey79 reopened this Jul 12, 2022
@abey79
Copy link
Owner

abey79 commented Jul 12, 2022

We can keep his open for records. It's a good feature request.

@abey79 abey79 changed the title Is there a way to deal with sharp corners Feature: convert sharp corners to round corners Jul 12, 2022
@dev-89
Copy link

dev-89 commented Aug 5, 2022

Hi guys, that really sounds like a cool feature to implement. Furthermore this excellent Stack Overflow post describes the geometry of the problem at hand very well. I just have a concrete implementation question at hand: should I simply add a "rounder_corners" method to the LineCollection class? Effectively this method would create out of two LineStrings two shortened LineStrings and one LinearRing.

@abey79
Copy link
Owner

abey79 commented Aug 5, 2022

@dev-89 Thanks for reaching out!

I wouldn't cut/merge paths with this feature (where "path", in vpype, is always a line string – technically a numpy array of complex – where the last point might be identical to the first if it's closes). So the function would take a path, and return another path with its angle rounded (based on a radius and some quantisation control parameter).

I would first create a function to operate on a single path (typically in vpype/geometry.py). I'd skip adding a member in LineCollection and just create a roundcorner command in vpype_cli (maybe in vpype_cli/operations.py).

@dev-89
Copy link

dev-89 commented Aug 5, 2022

Right, I think I understand now. Will get coding in the coming days and any further questions we can discuss over the PR.

@tatarize
Copy link
Contributor

There's other more simple methods of doing this. You're focusing on just replacing the corner with an arc. But really you can just segmentize the segments. Apply smooth (#216) repeatedly. Then simplify the segments.

  1. Segmentize: If we have a heavy corner detected where the angle shift is beyond tolerance. We take the lines that lead to and from this corner and turn them into 50 tiny lines.
  2. Smooth: We then apply smooth repeatedly where we find all 3 points in series, and we move the middle point towards the midpoint of the first and last point. amount * (p1 - p2) + p2
  3. Simplify: We check the determinate (or merely the segment angle (arctan2)) and we delete the point if the line is still going straight enough (within tolerances). This way if we ran simplify just after segmentize it would restore the original lines. This differs a bit from linesimplify in that we delete all points won't deviate a line by more than 1% (for example). It's basically simplify but for angles and not distance.

And most of these things are already there and can be applied pretty easy. We only really need as many segments as we're applying the smooth. So if we apply smooth 10 times only the 10 closest points could be affected. You can also segment just part of a line nearest to the sharp corner, or some distance along that line (sort of radius)).

@tatarize
Copy link
Contributor

Keep in mind a strong point of this proposed algorithm is the use of numpy. You can apply this to line segments really easily with the code you already have without bunch of other libraries.

The segmentize is easy. It's linear interpolation between the two complex points.

Smooth is a little bit more complex, but follows pretty nicely (spitballing).

midpoint = (line[2:] + line[:-2]) / 2.0
middlepoint = line[1:-1]
line[1:-1] = amount * (midpoint - middlepoint) + middlepoint)

The simplify isn't too bad. Again spitballing.

first = line[2:]
mid = line[1:-1]
last = line[:-2]
x1 = np.real(first)
y1 = np.real(first)
x2 = np.real(mid)
y2 = np.real(mid)
x3 = x2
y3 = y2
x4 = np.real(last)
y4 = np.real(last)
denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)

Then delete the points where abs(denom) < 1e-3 or whatever. At 0 the lines are straight. It's used more often to find intersections and at 0 lines are parallel but here since p2 == p3 they are just connected there.

So all straight forward with the code you have currently.

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

4 participants