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 java.util.ArrayList;
020import java.util.List;
021
022import org.apache.commons.geometry.core.Transform;
023import org.apache.commons.geometry.euclidean.oned.Interval;
024import org.apache.commons.geometry.euclidean.oned.RegionBSPTree1D;
025import org.apache.commons.geometry.euclidean.oned.Vector1D;
026import org.apache.commons.geometry.euclidean.threed.Bounds3D;
027import org.apache.commons.geometry.euclidean.threed.Vector3D;
028import org.apache.commons.geometry.euclidean.threed.line.Line3D.SubspaceTransform;
029
030/** Class representing an arbitrary subset of a line in 3D Euclidean space using a
031 * {@link RegionBSPTree1D}. This class can represent convex, non-convex, finite,
032 * infinite, and empty regions.
033 *
034 * <p>This class is mutable and <em>not</em> thread safe.</p>
035 */
036public final class EmbeddedTreeLineSubset3D extends LineSubset3D {
037    /** The 1D region representing the area on the line. */
038    private final RegionBSPTree1D region;
039
040    /** Construct a new, empty subset for the given line.
041     * @param line line defining the subset
042     */
043    public EmbeddedTreeLineSubset3D(final Line3D line) {
044        this(line, false);
045    }
046
047    /** Construct a new subset for the given line. If {@code full}
048     * is true, then the subset will cover the entire line; otherwise,
049     * it will be empty.
050     * @param line line defining the subset
051     * @param full if true, the subset will cover the entire space;
052     *      otherwise it will be empty
053     */
054    public EmbeddedTreeLineSubset3D(final Line3D line, final boolean full) {
055        this(line, new RegionBSPTree1D(full));
056    }
057
058    /** Construct a new instance from its defining line and subspace region.
059     * @param line line defining the subset
060     * @param region subspace region for the subset
061     */
062    public EmbeddedTreeLineSubset3D(final Line3D line, final RegionBSPTree1D region) {
063        super(line);
064
065        this.region = region;
066    }
067
068    /** {@inheritDoc} */
069    @Override
070    public double getSize() {
071        return region.getSize();
072    }
073
074    /** {@inheritDoc} */
075    @Override
076    public RegionBSPTree1D getSubspaceRegion() {
077        return region;
078    }
079
080    /** {@inheritDoc} */
081    @Override
082    public Vector3D getCentroid() {
083        final Vector1D subcenter = region.getCentroid();
084        return subcenter != null ?
085                getLine().toSpace(subcenter) :
086                null;
087    }
088
089    /** {@inheritDoc} */
090    @Override
091    public Bounds3D getBounds() {
092        final double min = region.getMin();
093        final double max = region.getMax();
094
095        if (Double.isFinite(min) && Double.isFinite(max)) {
096            final Line3D line = getLine();
097
098            return Bounds3D.builder()
099                    .add(line.toSpace(min))
100                    .add(line.toSpace(max))
101                    .build();
102        }
103
104        return null;
105    }
106
107    /** Transform this instance.
108     * @param transform the transform to apply
109     * @return a new, transformed instance
110     */
111    public EmbeddedTreeLineSubset3D transform(final Transform<Vector3D> transform) {
112        final SubspaceTransform st = getLine().subspaceTransform(transform);
113
114        final RegionBSPTree1D tRegion = RegionBSPTree1D.empty();
115        tRegion.copy(region);
116        tRegion.transform(st.getTransform());
117
118        return new EmbeddedTreeLineSubset3D(st.getLine(), tRegion);
119    }
120
121    /** Return a list of {@link LineConvexSubset3D} instances representing the same region
122     * as this instance.
123     * @return a list of {@link LineConvexSubset3D} instances representing the same region
124     *      as this instance.
125     */
126    public List<LineConvexSubset3D> toConvex() {
127        final List<Interval> intervals = region.toIntervals();
128
129        final Line3D line = getLine();
130        final List<LineConvexSubset3D> convex = new ArrayList<>(intervals.size());
131
132        for (final Interval interval : intervals) {
133            convex.add(Lines3D.subsetFromInterval(line, interval));
134        }
135
136        return convex;
137    }
138
139    /** {@inheritDoc} */
140    @Override
141    public String toString() {
142        final Line3D line = getLine();
143
144        final StringBuilder sb = new StringBuilder();
145        sb.append(this.getClass().getSimpleName())
146            .append('[')
147            .append("lineOrigin= ")
148            .append(line.getOrigin())
149            .append(", lineDirection= ")
150            .append(line.getDirection())
151            .append(", region= ")
152            .append(region)
153            .append(']');
154
155        return sb.toString();
156    }
157}