# Lines

The DJUTILS-DRAW line package defines several line-like objects:

• PolyLine: lines with two end points and zero or more intermediate points.
• Polygon: closed PolyLine with at least two points.
• LineSegment: line segment defined by two points. This much like a PolyLine with no intermediate points.
• Ray: line with one finite end point; going all the way to infinity in the other direction.

All line types exist in 2D and 3D versions. Additionally there is the `Bezier` class that generates `PolyLine` objects in 2D or 3D. More such generating classes will be added. E.g. for ellipses or segments thereof, clothoids.

## PolyLine

A `PolyLine` can be constructed from a collection of suitable `Point` objects (a `PolyLine2d` can be constructed from `Point2d` objects). It can also be constructed from a `double[]` (double array) for each of the dimensions. The following code shows four ways to construct the exact same `PolyLine2d` object:

``````    PolyLine2d pl1 = new PolyLine2d(new Point2d(1, 2), new Point2d(3, 4), new Point2d(20, -5));
System.out.println(pl1);
Point2d[] pointArray = new Point2d[] {new Point2d(1, 2), new Point2d(3, 4), new Point2d(20, -5)};
PolyLine2d pl2 = new PolyLine2d(pointArray);
System.out.println(pl2);
double[] x = new double[] { 1, 3, 20 };
double[] y = new double[] { 2, 4, -5 };
PolyLine2d pl3 = new PolyLine2d(x, y);
System.out.println(pl3);
List<Point2d> pointList = new ArrayList<>();
PolyLine2d pl4 = new PolyLine2d(pointList);
System.out.println(pl4);``````

This outputs:

```PolyLine2d [x=1.000000, y=2.000000, x=3.000000, y=4.000000, x=20.000000, y=-5.000000]
PolyLine2d [x=1.000000, y=2.000000, x=3.000000, y=4.000000, x=20.000000, y=-5.000000]
PolyLine2d [x=1.000000, y=2.000000, x=3.000000, y=4.000000, x=20.000000, y=-5.000000]
PolyLine2d [x=1.000000, y=2.000000, x=3.000000, y=4.000000, x=20.000000, y=-5.000000]
```

A `PolyLine2d` can also be constructed from an `Iterator<Point2d>` and even from a `java.awt.geom.Path2d` object, provided it only contains SEG_MOVETO and SEG_LINETO segments. If there is a SEG_CLOSE segment, anything after that is ignored. Any other segment types will cause the constructor to throw a `DrawRuntimeException`.

A `PolyLine3d` is very much like a `PolyLine2d`, except that it has z-coordinates and cannot be constructed from a `java.awt.geom.Path2d` object.

An attempt to create a PolyLine with two successive, idential points will fail with a `DrawRuntimeException`. Several `PolyLine` objects can be concatenated, provided the end point of each one matches the first point of the next. There is also a `concatenate` operation that allows an error margin for concatenation. The `noiseFilteredLine` constructor allows a `PolyLine` to be constructed from a list or array of points while filtering out intermediate points that are within a specified margin from the preceding point.

The total length of a `PolyLine` can be obtained with the`getLength()` method. The number of points that make up a `PolyLine` can be obtained with the `size()` method. The individual points with the `get(int)` method; calling this method creates a new `Point` object. The start point can be obtained with the `getFirst()` method; the end point with the `getLast()` method. Each double coordinate value can be directly obtained (without creating a `Point` object) with `getX(int)`, `getY(int)` and (only for `PolyLine3d`) `getZ(int)`. The cumulative length up to any of the points can be obtained with the `lengthAtIndex(int)` method. The result of `lengthAtIndex(0)` is always 0.0; the result of `lengthAtIndex(size() - 1)` is equal to `getLength()`.

A fragment of a `PolyLine` can be created with the `extract(double, double)` method. The parameters specify the distance from the start point and must be ordered by distance and be within the length of the `PolyLine`. The `extractFractional` method does the same, but the parameters are specified as fractions of the length of the `PolyLine`:

``````    PolyLine2d polyLine2d = new PolyLine2d(new Point2d(1, 1), new Point2d(5, 1), new Point2d(5, 2), new Point2d(9, 5));
System.out.println("PolyLine: " + polyLine2d);
System.out.println("length: " + polyLine2d.getLength());
System.out.println("fragment: " + polyLine2d.extract(2.0, 9.0));
System.out.println("fragment: " + polyLine2d.extractFractional(0.2, 0.9));``````

Prints

```PolyLine: PolyLine2d [x=1.000000, y=1.000000, x=5.000000, y=1.000000, x=5.000000, y=2.000000, x=9.000000, y=5.000000]
length: 10.0
fragment: PolyLine2d [x=3.000000, y=1.000000, x=5.000000, y=1.000000, x=5.000000, y=2.000000, x=8.200000, y=4.400000]
fragment: PolyLine2d [x=3.000000, y=1.000000, x=5.000000, y=1.000000, x=5.000000, y=2.000000, x=8.200000, y=4.400000]
```

Any location along the `PolyLine` can be obtained with the `getLocation(double)` method. The parameter must be between `0.0` and the length of the `PolyLine`. Extrapolation is also possible with the `getLocationExtended` method:

``````    System.out.println("PolyLine: " + polyLine2d);
System.out.println("location at distance 7.0: " + polyLine2d.getLocation(7.0));
System.out.println("extended location at distance 15: " + polyLine2d.getLocationExtended(15.0));
System.out.println("extended location at distance -8: " + polyLine2d.getLocationExtended(-8.0));``````

Prints

```PolyLine: PolyLine2d [x=1.000000, y=1.000000, x=5.000000, y=1.000000, x=5.000000, y=2.000000, x=9.000000, y=5.000000]
location at distance 7.0: Ray2d [x=6.6 y=3.2 phi=0.6435011087932843]
extended location at distance 15: Ray2d [x=13.0 y=8.0 phi=0.6435011087932844]
extended location at distance -8: Ray2d [x=-7.0 y=1.0 phi=0.0]
```

As you can see, the `getLocation` and `getLocationExtended` methods return `Ray` objects. A ray is a point with a direction. The `getLocation` method sets the direction of the ray to the direction of the `PolyLine` at the requested point. Extrapolation for negative distances is done in the direction of the first segment of the `PolyLine`. Extrapolation after the length of the `PolyLine` uses the direction of the last segment of the `PolyLine`. If the location is at one of the intermediate points of the `PolyLine`, the direction of the returned ray may depend on rounding errors; it is either the direction of the preceding segment, or that of the succeeding segment.

A `PolyLine` has a method to extract any of the segments from which it is constructed with the`getSegment(int)` method. the parameter selects the first point of the segment. The valid range of this parameter is `0..size() - 2`.

``````    System.out.println("PolyLine: " + polyLine2d);
for (int index = 0; index < polyLine2d.size() - 1; index++)
{
System.out.println("segment " + index + ": " + polyLine2d.getSegment(index));
}``````

Prints:

```PolyLine: PolyLine2d [x=1.000000, y=1.000000, x=5.000000, y=1.000000, x=5.000000, y=2.000000, x=9.000000, y=5.000000]
segment 0: LineSegment2d [startX=1.000000, startY=1.000000 - endX= 5.0, endY=1.000000]
segment 1: LineSegment2d [startX=5.000000, startY=1.000000 - endX= 5.0, endY=2.000000]
segment 2: LineSegment2d [startX=5.000000, startY=2.000000 - endX= 9.0, endY=5.000000]
```

Often, it is necessary to find the point on a `PolyLine` that is closest to a given `Point`. This can be obtained with the `closestPointOnPolyline` method. In some cases one is only interested in the closest point if it is not on one of the vertices of the `PolyLine`. In that case, one of the `projectOrthogonal` methods can be used. The `projectOrthogonal` methods return null if there is a closer point, but that projection is not orthogonal to the line segment on which it lies.

``````    System.out.println("PolyLine: " + polyLine2d);
System.out.println("closest point to (0,1): " + polyLine2d.closestPointOnPolyLine(new Point2d(0, 1)));
System.out.println("closest point to (6,0): " + polyLine2d.closestPointOnPolyLine(new Point2d(6, 0)));
System.out.println("closest point to (10,0): " + polyLine2d.closestPointOnPolyLine(new Point2d(10, 0)));
System.out.println("closest point to (50,0): " + polyLine2d.closestPointOnPolyLine(new Point2d(50, 0)));
System.out.println("project (0,0) orthogonal: " + polyLine2d.projectOrthogonal(new Point2d(0, 0)));
System.out.println("project (4,0) orthogonal: " + polyLine2d.projectOrthogonal(new Point2d(4, 0)));
System.out.println("project (5,0) orthogonal: " + polyLine2d.projectOrthogonal(new Point2d(5, 0)));
System.out.println("project (6,0) orthogonal: " + polyLine2d.projectOrthogonal(new Point2d(6, 0)));
System.out.println("project (10,0) orthogonal: " + polyLine2d.projectOrthogonal(new Point2d(10, 0)));
System.out.println("project (50,0) orthogonal: " + polyLine2d.projectOrthogonal(new Point2d(50, 0)));
System.out.println("project (50,0) orthogonal extended: " + polyLine2d.projectOrthogonalExtended(new Point2d(50, 0)));``````

Prints:

```PolyLine: PolyLine2d [x=1.000000, y=1.000000, x=5.000000, y=1.000000, x=5.000000, y=2.000000, x=9.000000, y=5.000000]
closest point to (0,1): Ray2d [x=1.0 y=1.0 phi=0.0]
closest point to (6,0): Ray2d [x=5.0 y=1.0 phi=1.5707963267948966]
closest point to (10,0): Ray2d [x=7.24 y=3.6800000000000006 phi=0.6435011087932843]
closest point to (50,0): Ray2d [x=9.0 y=5.0 phi=0.6435011087932844]
project (0,0) orthogonal: null
project (4,0) orthogonal: Ray2d [x=4.0 y=1.0 phi=0.0]
project (5,0) orthogonal: Ray2d [x=5.0 y=1.0 phi=1.5707963267948966]
project (6,0) orthogonal: null
project (10,0) orthogonal: Ray2d [x=7.24 y=3.6800000000000006 phi=0.6435011087932843]
project (50,0) orthogonal: null
project (50,0) orthogonal extended: Ray2d [x=32.839999999999996 y=22.879999999999995 phi=0.6435011087932844]
```

There are also variants of these methods that return a fractional location along the `PolyLine`. A fractional location is a value that is scaled by dividing it by the length of the `PolyLine`. The result of those methods is a double value. For results that are within the range of the `PolyLine`, that value is in the range `0.0 .. 1.0`. The `extended` variants can return values outside that range. If there is no valid result, these methods return `Double.NaN`.

TODO Explain the offsetLine method of PolyLine2d.

### LineSegment

A `LineSegment` is effectively a `PolyLine` with only two end points and no intermediate points. A `LineSegment` can be constructed from two `Point` objects, or one `Point` and one set of coordinates, or two sets of coordinates:

``````    LineSegment2d ls1 = new LineSegment2d(new Point2d(1, 2), new Point2d(5, 0));
System.out.println("ls1: " + ls1);
LineSegment2d ls2 = new LineSegment2d(new Point2d(1, 2), 5, 0);
System.out.println("ls2: " + ls2);
LineSegment2d ls3 = new LineSegment2d(1, 2, new Point2d(5, 0));
System.out.println("ls3: " + ls3);
LineSegment2d ls4 = new LineSegment2d(1, 2, 5, 0);
System.out.println("ls4: " + ls4);``````

Prints:

```ls1: LineSegment2d [startX=1.000000, startY=2.000000 - endX= 5.0, endY=0.000000]
ls2: LineSegment2d [startX=1.000000, startY=2.000000 - endX= 5.0, endY=0.000000]
ls3: LineSegment2d [startX=1.000000, startY=2.000000 - endX= 5.0, endY=0.000000]
ls4: LineSegment2d [startX=1.000000, startY=2.000000 - endX= 5.0, endY=0.000000]
```

The coordinates of the start and end points can be accessed directly as the `startX`, `startY`, `endX` and `endY` fields (and, in case of a LineSegment3d, the `startZ` and `endZ` fields). The start and end points can also be obtained with `getStartPoint()` and `getEndPoint()`. These methods construct a new `Point`.

Like the `PolyLine` classes, the `LineSegment` classes implement the `closestPointOnSegment`, `projectOrthogonal`, `getLocation` and `getLocationExtended` methods.

### Ray

A ray is a line with one finite end point. In the other direction a ray continues to infinity. In DJUTILS-DRAW, rays are available in 2D and 3D versions. The constructors take the (coordinates of the) finite end point and an additional through point, or a direction (just `phi` to construct a `Ray2d`, or `phi` and `theta` to construct a `Ray3d`). There are six main ways to construct a Ray:

``````    Ray2d r1 = new Ray2d(new Point2d(1, 2), new Point2d(1, 6));
System.out.println(r1);
Ray2d r2 = new Ray2d(1, 2, 1, 6);
System.out.println(r2);
Ray2d r3 = new Ray2d(new Point2d(1, 2), 1, 6);
System.out.println(r3);
Ray2d r4 = new Ray2d(1, 2, new Point2d(1, 6));
System.out.println(r4);
Ray2d r5 = new Ray2d(1, 2, Math.PI / 2);
System.out.println(r5);
Ray2d r6 = new Ray2d(new Point2d(1, 2), Math.PI / 2);
System.out.println(r6);``````

Prints:

```Ray2d [x=1.0 y=2.0 phi=1.5707963267948966]
Ray2d [x=1.0 y=2.0 phi=1.5707963267948966]
Ray2d [x=1.0 y=2.0 phi=1.5707963267948966]
Ray2d [x=1.0 y=2.0 phi=1.5707963267948966]
Ray2d [x=1.0 y=2.0 phi=1.5707963267948966]
Ray2d [x=1.0 y=2.0 phi=1.5707963267948966]
```

The direction of a Ray2d is encoded as an angle (in Radians) from the X-axis. These examples all create a ray that points along the Y axis. The Ray3d classes simply take extra arguments (two z coordinates, or one z coordinate and `theta`; the angle from the Z-axis).

The coordinates of the finite end point can be directly accessed as the `x`, `y` (and for `Ray3d`) `z` fields. There are also getters for these: `getX()`, `getY()` (and for `Ray3d` `getZ()`). The field `phi` (and for Ray3d `theta`) is also directly accessible, or with the method `getPhi()` (and for `Ray3d` `getTheta()`). The finite end point can also be obtained with the `getEndPoint()` method.

Rays implement `getLocation` and `getLocationExtended` methods. The first takes a non-negative distance argument and returns a new `Ray` which is coincident with the original, but starts the distance value from the start point of the first ray. The `getLocationExtended` method allows the argument to be negative (and will return a new `Ray` which is coincident with the original, but starts the distance value along, or before the original `Ray`). The `closestPointOnRay` method takes a `Point` as parameter and returns a new `Ray` that starts at some point along the original ray which is closest to the given `Point`.

Like the `LineSegment` classes, `Ray` classes implement four `projectOrthogonal` methods. If the orthogonal projection of the given `Point` falls before the finite end point of the `Ray`, the `projectOrthogonal` method returns null and the `projectOrthogonalFractional` method returns `Double.NaN`. The `projectOrthogonalExtended` method will return negative values if the given `Point` projects before the finite end point of the `Ray` and the `projectOrthogonalFractionalExtended` method will return negative values if that happens.

The `Bounds` of a `Ray` can be obtained with the `getBounds()` method and is finite is some directions and infinite in others.

The `flip()` method creates a new `Ray` with the same finite end point, but pointing in the opposite direction. The `neg()` method creates a new `Ray` at a point that has all coordinates inverted from the original and pointing in the opposite direction.

The `Ray` classes implement `epsilonEquals` to compare similar rays for equality with specified tolerances for the coordinates and the angles. Before that comparison takes place, the angles are normalized.