001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.geometry.euclidean.threed.line;
018
019import org.apache.commons.geometry.core.Transform;
020import org.apache.commons.geometry.euclidean.threed.Bounds3D;
021import org.apache.commons.geometry.euclidean.threed.Vector3D;
022import org.apache.commons.numbers.core.Precision;
023
024/** Class representing a line segment in 3D Euclidean space. A line segment is a portion of
025 * a line with finite start and end points.
026 *
027 * <p>Instances of this class are guaranteed to be immutable.</p>
028 * @see Lines3D
029 * @see <a href="https://en.wikipedia.org/wiki/Line_segment">Line Segment</a>
030 */
031public final class Segment3D extends LineConvexSubset3D {
032
033    /** Start abscissa for the segment. */
034    private final double start;
035
036    /** End abscissa for the segment. */
037    private final double end;
038
039    /** Construct a new instance from a line and two points on the line. The points are projected onto
040     * the line and must be in order of increasing abscissa. No validation is performed.
041     * @param line line for the segment
042     * @param startPoint segment start point
043     * @param endPoint segment end point
044     */
045    Segment3D(final Line3D line, final Vector3D startPoint, final Vector3D endPoint) {
046        this(line, line.abscissa(startPoint), line.abscissa(endPoint));
047    }
048
049    /** Construct a new instance from a line and two abscissa locations on the line.
050     * The abscissa locations must be in increasing order. No validation is performed.
051     * @param line line for the segment
052     * @param start abscissa start location
053     * @param end abscissa end location
054     */
055    Segment3D(final Line3D line, final double start, final double end) {
056        super(line);
057
058        this.start = start;
059        this.end = end;
060    }
061
062    /** {@inheritDoc}
063    *
064    * <p>This method always returns {@code false}.</p>
065    */
066    @Override
067    public boolean isInfinite() {
068        return false;
069    }
070
071    /** {@inheritDoc}
072     *
073     * <p>This method always returns {@code true}.</p>
074     */
075    @Override
076    public boolean isFinite() {
077        return true;
078    }
079
080    /** {@inheritDoc} */
081    @Override
082    public Vector3D getStartPoint() {
083        return getLine().toSpace(start);
084    }
085
086    /** {@inheritDoc} */
087    @Override
088    public double getSubspaceStart() {
089        return start;
090    }
091
092    /** {@inheritDoc} */
093    @Override
094    public Vector3D getEndPoint() {
095        return getLine().toSpace(end);
096    }
097
098    /** {@inheritDoc} */
099    @Override
100    public double getSubspaceEnd() {
101        return end;
102    }
103
104    /** {@inheritDoc} */
105    @Override
106    public double getSize() {
107        return end - start;
108    }
109
110    /** {@inheritDoc} */
111    @Override
112    public Vector3D getCentroid() {
113        return getLine().toSpace((0.5 * (end - start)) + start);
114    }
115
116    /** {@inheritDoc} */
117    @Override
118    public Bounds3D getBounds() {
119        return Bounds3D.builder()
120                .add(getStartPoint())
121                .add(getEndPoint())
122                .build();
123    }
124
125    /** {@inheritDoc} */
126    @Override
127    public Segment3D transform(final Transform<Vector3D> transform) {
128        final Vector3D t1 = transform.apply(getStartPoint());
129        final Vector3D t2 = transform.apply(getEndPoint());
130
131        final Line3D tLine = getLine().transform(transform);
132
133        return new Segment3D(tLine, t1, t2);
134    }
135
136    /** {@inheritDoc} */
137    @Override
138    public String toString() {
139        final StringBuilder sb = new StringBuilder();
140        sb.append(getClass().getSimpleName())
141            .append("[startPoint= ")
142            .append(getStartPoint())
143            .append(", endPoint= ")
144            .append(getEndPoint())
145            .append(']');
146
147        return sb.toString();
148    }
149
150    /** {@inheritDoc} */
151    @Override
152    boolean containsAbscissa(final double abscissa) {
153        final Precision.DoubleEquivalence precision = getLine().getPrecision();
154        return precision.gte(abscissa, start) &&
155                precision.lte(abscissa, end);
156    }
157}