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.math3.geometry.euclidean.threed;
018
019import java.util.ArrayList;
020import java.util.List;
021
022import org.apache.commons.math3.exception.MathIllegalArgumentException;
023import org.apache.commons.math3.geometry.Point;
024import org.apache.commons.math3.geometry.euclidean.oned.Euclidean1D;
025import org.apache.commons.math3.geometry.euclidean.oned.Interval;
026import org.apache.commons.math3.geometry.euclidean.oned.IntervalsSet;
027import org.apache.commons.math3.geometry.euclidean.oned.Vector1D;
028import org.apache.commons.math3.geometry.partitioning.Region.Location;
029
030/** This class represents a subset of a {@link Line}.
031 * @since 3.0
032 */
033public class SubLine {
034
035    /** Default value for tolerance. */
036    private static final double DEFAULT_TOLERANCE = 1.0e-10;
037
038    /** Underlying line. */
039    private final Line line;
040
041    /** Remaining region of the hyperplane. */
042    private final IntervalsSet remainingRegion;
043
044    /** Simple constructor.
045     * @param line underlying line
046     * @param remainingRegion remaining region of the line
047     */
048    public SubLine(final Line line, final IntervalsSet remainingRegion) {
049        this.line            = line;
050        this.remainingRegion = remainingRegion;
051    }
052
053    /** Create a sub-line from two endpoints.
054     * @param start start point
055     * @param end end point
056     * @param tolerance tolerance below which points are considered identical
057     * @exception MathIllegalArgumentException if the points are equal
058     * @since 3.3
059     */
060    public SubLine(final Vector3D start, final Vector3D end, final double tolerance)
061        throws MathIllegalArgumentException {
062        this(new Line(start, end, tolerance), buildIntervalSet(start, end, tolerance));
063    }
064
065    /** Create a sub-line from two endpoints.
066     * @param start start point
067     * @param end end point
068     * @exception MathIllegalArgumentException if the points are equal
069     * @deprecated as of 3.3, replaced with {@link #SubLine(Vector3D, Vector3D, double)}
070     */
071    public SubLine(final Vector3D start, final Vector3D end)
072        throws MathIllegalArgumentException {
073        this(start, end, DEFAULT_TOLERANCE);
074    }
075
076    /** Create a sub-line from a segment.
077     * @param segment single segment forming the sub-line
078     * @exception MathIllegalArgumentException if the segment endpoints are equal
079     */
080    public SubLine(final Segment segment) throws MathIllegalArgumentException {
081        this(segment.getLine(),
082             buildIntervalSet(segment.getStart(), segment.getEnd(), segment.getLine().getTolerance()));
083    }
084
085    /** Get the endpoints of the sub-line.
086     * <p>
087     * A subline may be any arbitrary number of disjoints segments, so the endpoints
088     * are provided as a list of endpoint pairs. Each element of the list represents
089     * one segment, and each segment contains a start point at index 0 and an end point
090     * at index 1. If the sub-line is unbounded in the negative infinity direction,
091     * the start point of the first segment will have infinite coordinates. If the
092     * sub-line is unbounded in the positive infinity direction, the end point of the
093     * last segment will have infinite coordinates. So a sub-line covering the whole
094     * line will contain just one row and both elements of this row will have infinite
095     * coordinates. If the sub-line is empty, the returned list will contain 0 segments.
096     * </p>
097     * @return list of segments endpoints
098     */
099    public List<Segment> getSegments() {
100
101        final List<Interval> list = remainingRegion.asList();
102        final List<Segment> segments = new ArrayList<Segment>(list.size());
103
104        for (final Interval interval : list) {
105            final Vector3D start = line.toSpace((Point<Euclidean1D>) new Vector1D(interval.getInf()));
106            final Vector3D end   = line.toSpace((Point<Euclidean1D>) new Vector1D(interval.getSup()));
107            segments.add(new Segment(start, end, line));
108        }
109
110        return segments;
111
112    }
113
114    /** Get the intersection of the instance and another sub-line.
115     * <p>
116     * This method is related to the {@link Line#intersection(Line)
117     * intersection} method in the {@link Line Line} class, but in addition
118     * to compute the point along infinite lines, it also checks the point
119     * lies on both sub-line ranges.
120     * </p>
121     * @param subLine other sub-line which may intersect instance
122     * @param includeEndPoints if true, endpoints are considered to belong to
123     * instance (i.e. they are closed sets) and may be returned, otherwise endpoints
124     * are considered to not belong to instance (i.e. they are open sets) and intersection
125     * occurring on endpoints lead to null being returned
126     * @return the intersection point if there is one, null if the sub-lines don't intersect
127     */
128    public Vector3D intersection(final SubLine subLine, final boolean includeEndPoints) {
129
130        // compute the intersection on infinite line
131        Vector3D v1D = line.intersection(subLine.line);
132        if (v1D == null) {
133            return null;
134        }
135
136        // check location of point with respect to first sub-line
137        Location loc1 = remainingRegion.checkPoint((Point<Euclidean1D>) line.toSubSpace((Point<Euclidean3D>) v1D));
138
139        // check location of point with respect to second sub-line
140        Location loc2 = subLine.remainingRegion.checkPoint((Point<Euclidean1D>) subLine.line.toSubSpace((Point<Euclidean3D>) v1D));
141
142        if (includeEndPoints) {
143            return ((loc1 != Location.OUTSIDE) && (loc2 != Location.OUTSIDE)) ? v1D : null;
144        } else {
145            return ((loc1 == Location.INSIDE) && (loc2 == Location.INSIDE)) ? v1D : null;
146        }
147
148    }
149
150    /** Build an interval set from two points.
151     * @param start start point
152     * @param end end point
153     * @return an interval set
154     * @param tolerance tolerance below which points are considered identical
155     * @exception MathIllegalArgumentException if the points are equal
156     */
157    private static IntervalsSet buildIntervalSet(final Vector3D start, final Vector3D end, final double tolerance)
158        throws MathIllegalArgumentException {
159        final Line line = new Line(start, end, tolerance);
160        return new IntervalsSet(line.toSubSpace((Point<Euclidean3D>) start).getX(),
161                                line.toSubSpace((Point<Euclidean3D>) end).getX(),
162                                tolerance);
163    }
164
165}