A path is defined as a list of 2D points (x,y) (and may be open or closed).
Given a path p, what is an algorithm which generates a path p’ which is “inside” p and at constant offset to p i.e. each line segment of p’ is parallel to the corresponding line segment of p.
6
Well, this turns out to be non-trivial extension of Veldaeven’s solution.
For each sequence of 3 points, calculate the 2 vectors between them, as suggested by Veldaeven.
Find the angle between the two vectors by adding the angles from the x axis – (nb do not use dot product which always returns an angle less than pi and hence is too symmetrical for our problem).
Halve this angle. This is the perpendicular bisector along which the resulting vector will lie.
Now to calculate which quadrant to draw the point in (the “which side” problem). Find the difference between the 2 vectors. If the gradient of this (i.e. the “double derivative”) is positive then the path is going back on itself through a maximum: add pi/2. If the gradient is negative, the path is going through a minimum: subtract pi / 2
innerWallClockwise :: Float -> V2 Float -> [Point V2 Float] -> [V2 Float]
innerWallClockwise w acc (p : p' : p'' : ps) =
let v = p' .-. p
v' = p'' .-. p'
d2v = v' .-. v
phi = atan2 (v ^. _y) (v ^. _x)
rho = atan2 (v' ^. _y) (v' ^. _x)
theta = phi + rho
d2vtheta = atan2 (d2v ^. _y) (d2v ^. _x)
theta' = theta / 2 + if d2vtheta > 0 then pi / 2 else (-1) * pi / 2
v''' = w *^ angle theta'
v'''' = acc .+^ v'''
in
if ps == [] then [v''''] else v'''' : (innerWallClockwise w (acc .+^ v') (p' : p'' : (head ps) : (tail ps)))
Let’s give it a simple rectangular path
let path=[P (V2 0 0), P(V2 0 5), P(V2 5 5), P (V2 5 (-5)), P (V2 0 (-5))]
Try out our function:
innerWallClockwise 1 (V2 0 5) path
=> [V2 0.70710677 4.2928934,V2 4.2928934 4.2928934,V2 4.2928934 (-4.2928934)]
Yes, the points are all inside the input path at a distance of sqrt(2) from the vertex.
NB all methods using e.g. the cross product of the two vectors are too symmetrical.
Hopefully this will help someone in the future.
I don’t know of a formal algorithm. I implemented this once long ago. The algorithm depends on not only the enumeration of line segments, but also on the “side” of the line segments that the parallel path is meant to follow. Assume you know the “side”.
My set of line segments was (as you describe) a list of 2d points (x,y).
- For each point
p
, I looked at the two vectors formed by using its
previous and subsequent points in the enumeration. - I found the angle between these two vectors using a dot product
- I split that angle in half, making a third vector that bisected the first two.
(This is where knowing “what side of the path” comes in) - By setting the magnitude of this third vector to the “constant offset”, you come up with a point
p'
on the “parallel path” that corresponds to pointp
- For the endpoints of a path that is not closed, I simply used the constant offset to set the magnitude of a vector perpendicular to the vector formed by the endpoint and its neighboring point.
4
I believe you’re looking for an offset-polygon.
This stackoverflow post details approaches to the problem.