blob: 562efed67c562ecc6cdd200600a8bb7ad6d8b190 [file] [log] [blame]
/*
* 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 java.util.ArrayList;
import java.util.List;
import org.apache.commons.geometry.core.Transform;
import org.apache.commons.geometry.core.exception.GeometryException;
import org.apache.commons.geometry.core.partitioning.ConvexSubHyperplane;
import org.apache.commons.geometry.core.partitioning.Hyperplane;
import org.apache.commons.geometry.core.partitioning.Split;
import org.apache.commons.geometry.core.partitioning.SubHyperplane;
import org.apache.commons.geometry.euclidean.twod.ConvexArea;
import org.apache.commons.geometry.euclidean.twod.RegionBSPTree2D;
/** Class representing an arbitrary region of a plane. This class can represent
* both convex and non-convex regions of its underlying plane.
*
* <p>This class is mutable and <em>not</em> thread safe.</p>
*/
public final class SubPlane extends AbstractSubPlane<RegionBSPTree2D> {
/** The 2D region representing the area on the plane. */
private final RegionBSPTree2D region;
/** Construct a new, empty subplane for the given plane.
* @param plane plane defining the subplane
*/
public SubPlane(final Plane plane) {
this(plane, false);
}
/** Construct a new subplane for the given plane. If {@code full}
* is true, then the subplane will cover the entire plane; otherwise,
* it will be empty.
* @param plane plane defining the subplane
* @param full if true, the subplane will cover the entire space;
* otherwise it will be empty
*/
public SubPlane(final Plane plane, boolean full) {
this(plane, new RegionBSPTree2D(full));
}
/** Construct a new instance from its defining plane and subspace region.
* @param plane plane defining the subplane
* @param region subspace region for the subplane
*/
public SubPlane(final Plane plane, final RegionBSPTree2D region) {
super(plane);
this.region = region;
}
/** {@inheritDoc} */
@Override
public List<ConvexSubPlane> toConvex() {
final List<ConvexArea> areas = region.toConvex();
final Plane plane = getPlane();
final List<ConvexSubPlane> subplanes = new ArrayList<>(areas.size());
for (final ConvexArea area : areas) {
subplanes.add(ConvexSubPlane.fromConvexArea(plane, area));
}
return subplanes;
}
/** {@inheritDoc}
*
* <p>In all cases, the current instance is not modified. However, In order to avoid
* unnecessary copying, this method will use the current instance as the split value when
* the instance lies entirely on the plus or minus side of the splitter. For example, if
* this instance lies entirely on the minus side of the splitter, the subplane
* returned by {@link Split#getMinus()} will be this instance. Similarly, {@link Split#getPlus()}
* will return the current instance if it lies entirely on the plus side. Callers need to make
* special note of this, since this class is mutable.</p>
*/
@Override
public Split<SubPlane> split(final Hyperplane<Vector3D> splitter) {
return splitInternal(splitter, this, (p, r) -> new SubPlane(p, (RegionBSPTree2D) r));
}
/** {@inheritDoc} */
@Override
public RegionBSPTree2D getSubspaceRegion() {
return region;
}
/** {@inheritDoc} */
@Override
public SubPlane transform(final Transform<Vector3D> transform) {
final Plane.SubspaceTransform subTransform = getPlane().subspaceTransform(transform);
final RegionBSPTree2D tRegion = RegionBSPTree2D.empty();
tRegion.copy(region);
tRegion.transform(subTransform.getTransform());
return new SubPlane(subTransform.getPlane(), tRegion);
}
/** Add a convex subplane to this instance.
* @param subplane convex subplane to add
* @throws GeometryException if the given convex subplane is not from
* a plane equivalent to this instance
*/
public void add(final ConvexSubPlane subplane) {
validatePlane(subplane.getPlane());
region.add(subplane.getSubspaceRegion());
}
/** Add a subplane to this instance.
* @param subplane subplane to add
* @throws GeometryException if the given convex subplane is not from
* a plane equivalent to this instance
*/
public void add(final SubPlane subplane) {
validatePlane(subplane.getPlane());
region.union(subplane.getSubspaceRegion());
}
/** Validate that the given plane is equivalent to the plane
* defining this subplane.
* @param inputPlane plane to validate
* @throws GeometryException if the given plane is not equivalent
* to the plane for this instance
*/
private void validatePlane(final Plane inputPlane) {
final Plane plane = getPlane();
if (!plane.eq(inputPlane)) {
throw new GeometryException("Argument is not on the same " +
"plane. Expected " + plane + " but was " +
inputPlane);
}
}
/** {@link Builder} implementation for sublines.
*/
public static class SubPlaneBuilder implements SubHyperplane.Builder<Vector3D> {
/** Subplane instance created by this builder. */
private final SubPlane subplane;
/** Construct a new instance for building subplane region for the given plane.
* @param plane the underlying plane for the subplane region
*/
public SubPlaneBuilder(final Plane plane) {
this.subplane = new SubPlane(plane);
}
/** {@inheritDoc} */
@Override
public void add(final SubHyperplane<Vector3D> sub) {
addInternal(sub);
}
/** {@inheritDoc} */
@Override
public void add(final ConvexSubHyperplane<Vector3D> sub) {
addInternal(sub);
}
/** {@inheritDoc} */
@Override
public SubPlane build() {
return subplane;
}
/** Internal method for adding subhyperplanes to this builder.
* @param sub the subhyperplane to add; either convex or non-convex
*/
private void addInternal(final SubHyperplane<Vector3D> sub) {
if (sub instanceof ConvexSubPlane) {
subplane.add((ConvexSubPlane) sub);
} else if (sub instanceof SubPlane) {
subplane.add((SubPlane) sub);
} else {
throw new IllegalArgumentException("Unsupported subhyperplane type: " + sub.getClass().getName());
}
}
}
}