| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.commons.geometry.euclidean.threed; |
| |
| import org.apache.commons.geometry.core.Transform; |
| import org.apache.commons.geometry.core.precision.DoublePrecisionContext; |
| import org.apache.commons.geometry.euclidean.oned.Interval; |
| import org.apache.commons.geometry.euclidean.oned.Vector1D; |
| import org.apache.commons.geometry.euclidean.threed.Line3D.SubspaceTransform; |
| |
| /** Class representing a line segment in 3 dimensional Euclidean space. |
| * |
| * <p>This class is guaranteed to be immutable.</p> |
| */ |
| public final class Segment3D extends AbstractSubLine3D<Interval> { |
| /** String used to indicate the start point of the segment in the toString() representation. */ |
| private static final String START_STR = "start= "; |
| |
| /** String used to indicate the direction the segment in the toString() representation. */ |
| private static final String DIR_STR = "direction= "; |
| |
| /** String used to indicate the end point of the segment in the toString() representation. */ |
| private static final String END_STR = "end= "; |
| |
| /** String used as a separator value in the toString() representation. */ |
| private static final String SEP_STR = ", "; |
| |
| /** The interval representing the region of the line contained in |
| * the line segment. |
| */ |
| private final Interval interval; |
| |
| /** Construct a line segment from an underlying line and a 1D interval |
| * on it. |
| * @param line the underlying line |
| * @param interval 1D interval on the line defining the line segment |
| */ |
| private Segment3D(final Line3D line, final Interval interval) { |
| super(line); |
| |
| this.interval = interval; |
| } |
| |
| /** Get the start value in the 1D subspace of the line. |
| * @return the start value in the 1D subspace of the line. |
| */ |
| public double getSubspaceStart() { |
| return interval.getMin(); |
| } |
| |
| /** Get the end value in the 1D subspace of the line. |
| * @return the end value in the 1D subspace of the line |
| */ |
| public double getSubspaceEnd() { |
| return interval.getMax(); |
| } |
| |
| /** Get the start point of the line segment or null if no start point |
| * exists (ie, the segment is infinite). |
| * @return the start point of the line segment or null if no start point |
| * exists |
| */ |
| public Vector3D getStartPoint() { |
| return interval.hasMinBoundary() ? getLine().toSpace(interval.getMin()) : null; |
| } |
| |
| /** Get the end point of the line segment or null if no end point |
| * exists (ie, the segment is infinite). |
| * @return the end point of the line segment or null if no end point |
| * exists |
| */ |
| public Vector3D getEndPoint() { |
| return interval.hasMaxBoundary() ? getLine().toSpace(interval.getMax()) : null; |
| } |
| |
| /** Return true if the segment is infinite. |
| * @return true if the segment is infinite. |
| */ |
| public boolean isInfinite() { |
| return interval.isInfinite(); |
| } |
| |
| /** Return true if the segment is finite. |
| * @return true if the segment is finite. |
| */ |
| public boolean isFinite() { |
| return interval.isFinite(); |
| } |
| |
| /** Return the 1D interval for the line segment. |
| * @return the 1D interval for the line segment |
| * @see #getSubspaceRegion() |
| */ |
| public Interval getInterval() { |
| return interval; |
| } |
| |
| /** {@inheritDoc} |
| * |
| * <p>This is an alias for {@link #getInterval()}.</p> |
| */ |
| @Override |
| public Interval getSubspaceRegion() { |
| return getInterval(); |
| } |
| |
| /** Return true if the given point lies in the segment. |
| * @param pt point to check |
| * @return true if the point lies in the segment |
| */ |
| public boolean contains(final Vector3D pt) { |
| final Line3D line = getLine(); |
| return line.contains(pt) && interval.contains(line.toSubspace(pt)); |
| } |
| |
| /** Transform this instance. |
| * @param transform the transform to apply |
| * @return a new, transformed instance |
| */ |
| public Segment3D transform(final Transform<Vector3D> transform) { |
| final SubspaceTransform st = getLine().subspaceTransform(transform); |
| |
| return new Segment3D(st.getLine(), interval.transform(st.getTransform())); |
| } |
| |
| /** Return a string representation of the segment. |
| * |
| * <p>In order to keep the representation short but informative, the exact format used |
| * depends on the properties of the instance, as demonstrated in the examples |
| * below. |
| * <ul> |
| * <li>Infinite segment - |
| * {@code "Segment3D[lineOrigin= (0.0, 0.0, 0.0), lineDirection= (1.0, 0.0, 0.0)]}"</li> |
| * <li>Start point but no end point - |
| * {@code "Segment3D[start= (0.0, 0.0, 0.0), direction= (1.0, 0.0, 0.0)]}"</li> |
| * <li>End point but no start point - |
| * {@code "Segment3D[direction= (1.0, 0.0, 0.0), end= (0.0, 0.0, 0.0)]}"</li> |
| * <li>Start point and end point - |
| * {@code "Segment3D[start= (0.0, 0.0, 0.0), end= (1.0, 0.0, 0.0)]}"</li> |
| * </ul> |
| */ |
| @Override |
| public String toString() { |
| final Vector3D startPoint = getStartPoint(); |
| final Vector3D endPoint = getEndPoint(); |
| |
| final StringBuilder sb = new StringBuilder(); |
| sb.append(this.getClass().getSimpleName()) |
| .append('['); |
| |
| if (startPoint != null && endPoint != null) { |
| sb.append(START_STR) |
| .append(startPoint) |
| .append(SEP_STR) |
| .append(END_STR) |
| .append(endPoint); |
| } else if (startPoint != null) { |
| sb.append(START_STR) |
| .append(startPoint) |
| .append(SEP_STR) |
| .append(DIR_STR) |
| .append(getLine().getDirection()); |
| } else if (endPoint != null) { |
| sb.append(DIR_STR) |
| .append(getLine().getDirection()) |
| .append(SEP_STR) |
| .append(END_STR) |
| .append(endPoint); |
| } else { |
| final Line3D line = getLine(); |
| |
| sb.append("lineOrigin= ") |
| .append(line.getOrigin()) |
| .append(SEP_STR) |
| .append("lineDirection= ") |
| .append(line.getDirection()); |
| } |
| |
| sb.append(']'); |
| |
| return sb.toString(); |
| } |
| |
| /** Create a line segment between two points. The underlying line points in the direction from {@code start} |
| * to {@code end}. |
| * @param start start point for the line segment |
| * @param end end point for the line segment |
| * @param precision precision context used to determine floating point equality |
| * @return a new line segment between {@code start} and {@code end}. |
| */ |
| public static Segment3D fromPoints(final Vector3D start, final Vector3D end, |
| final DoublePrecisionContext precision) { |
| |
| final Line3D line = Line3D.fromPoints(start, end, precision); |
| return fromPointsOnLine(line, start, end); |
| } |
| |
| /** Construct a line segment from a starting point and a direction that the line should extend to |
| * infinity from. This is equivalent to constructing a ray. |
| * @param start start point for the segment |
| * @param direction direction that the line should extend from the segment |
| * @param precision precision context used to determine floating point equality |
| * @return a new line segment starting from the given point and extending to infinity in the |
| * specified direction |
| */ |
| public static Segment3D fromPointAndDirection(final Vector3D start, final Vector3D direction, |
| final DoublePrecisionContext precision) { |
| final Line3D line = Line3D.fromPointAndDirection(start, direction, precision); |
| return fromInterval(line, Interval.min(line.toSubspace(start).getX(), precision)); |
| } |
| |
| /** Create a line segment from an underlying line and a 1D interval on the line. |
| * @param line the line that the line segment will belong to |
| * @param interval 1D interval on the line |
| * @return a line segment defined by the given line and interval |
| */ |
| public static Segment3D fromInterval(final Line3D line, final Interval interval) { |
| return new Segment3D(line, interval); |
| } |
| |
| /** Create a line segment from an underlying line and a 1D interval on the line. |
| * @param line the line that the line segment will belong to |
| * @param a first 1D location on the line |
| * @param b second 1D location on the line |
| * @return a line segment defined by the given line and interval |
| */ |
| public static Segment3D fromInterval(final Line3D line, final double a, final double b) { |
| return fromInterval(line, Interval.of(a, b, line.getPrecision())); |
| } |
| |
| /** Create a line segment from an underlying line and a 1D interval on the line. |
| * @param line the line that the line segment will belong to |
| * @param a first 1D point on the line; must not be null |
| * @param b second 1D point on the line; must not be null |
| * @return a line segment defined by the given line and interval |
| */ |
| public static Segment3D fromInterval(final Line3D line, final Vector1D a, final Vector1D b) { |
| return fromInterval(line, a.getX(), b.getX()); |
| } |
| |
| /** Create a new line segment from a line and points known to lie on the line. |
| * @param line the line that the line segment will belong to |
| * @param start line segment start point known to lie on the line |
| * @param end line segment end poitn known to lie on the line |
| * @return a new line segment created from the line and points |
| */ |
| private static Segment3D fromPointsOnLine(final Line3D line, final Vector3D start, final Vector3D end) { |
| final double subspaceStart = line.toSubspace(start).getX(); |
| final double subspaceEnd = line.toSubspace(end).getX(); |
| |
| return fromInterval(line, subspaceStart, subspaceEnd); |
| } |
| } |