All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.commons.geometry.euclidean.threed.EmbeddedTreePlaneSubset Maven / Gradle / Ivy

The newest version!
/*
 * 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.partitioning.Hyperplane;
import org.apache.commons.geometry.core.partitioning.Split;
import org.apache.commons.geometry.euclidean.twod.ConvexArea;
import org.apache.commons.geometry.euclidean.twod.RegionBSPTree2D;
import org.apache.commons.geometry.euclidean.twod.Vector2D;
import org.apache.commons.geometry.euclidean.twod.rotation.Rotation2D;
import org.apache.commons.numbers.core.Precision;

/** Class representing an arbitrary subset of a plane using a {@link RegionBSPTree2D}.
 * This class can represent convex, non-convex, finite, infinite, and empty regions.
 *
 * 

This class is mutable and not thread safe.

*/ public final class EmbeddedTreePlaneSubset extends AbstractEmbeddedRegionPlaneSubset { /** The 2D region representing the area on the plane. */ private final RegionBSPTree2D region; /** Construct a new, empty plane subset for the given plane. * @param plane plane containing the subset */ public EmbeddedTreePlaneSubset(final EmbeddingPlane plane) { this(plane, false); } /** Construct a new subset for the given plane. If {@code full} * is true, then the subset will cover the entire plane; otherwise, * it will be empty. * @param plane plane containing the subset * @param full if true, the subset will cover the entire space; * otherwise it will be empty */ public EmbeddedTreePlaneSubset(final EmbeddingPlane plane, final boolean full) { this(plane, new RegionBSPTree2D(full)); } /** Construct a new instance from its defining plane and subspace region. * @param plane plane containing the subset * @param region subspace region for the plane subset */ public EmbeddedTreePlaneSubset(final EmbeddingPlane plane, final RegionBSPTree2D region) { super(plane); this.region = region; } /** {@inheritDoc} */ @Override public PlaneSubset.Embedded getEmbedded() { return this; } /** {@inheritDoc} */ @Override public RegionBSPTree2D getSubspaceRegion() { return region; } /** {@inheritDoc} */ @Override public List toConvex() { final List areas = region.toConvex(); final List facets = new ArrayList<>(areas.size()); for (final ConvexArea area : areas) { facets.add(Planes.subsetFromConvexArea(getPlane(), area)); } return facets; } /** {@inheritDoc} */ @Override public List toTriangles() { final EmbeddingPlane plane = getPlane(); final List triangles = new ArrayList<>(); List vertices; for (final ConvexArea area : region.toConvex()) { if (area.isInfinite()) { throw new IllegalStateException("Cannot convert infinite plane subset to triangles: " + this); } vertices = plane.toSpace(area.getVertices()); triangles.addAll(Planes.convexPolygonToTriangleFan(plane, vertices)); } return triangles; } /** {@inheritDoc} */ @Override public Bounds3D getBounds() { return getBoundsFromSubspace(region); } /** {@inheritDoc} * *

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 plane subset * 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.

*/ @Override public Split split(final Hyperplane splitter) { return Planes.subspaceSplit((Plane) splitter, this, (p, r) -> new EmbeddedTreePlaneSubset(p, (RegionBSPTree2D) r)); } /** {@inheritDoc} */ @Override public EmbeddedTreePlaneSubset transform(final Transform transform) { final EmbeddingPlane.SubspaceTransform subTransform = getPlane().getEmbedding().subspaceTransform(transform); final RegionBSPTree2D tRegion = RegionBSPTree2D.empty(); tRegion.copy(region); tRegion.transform(subTransform.getTransform()); return new EmbeddedTreePlaneSubset(subTransform.getPlane(), tRegion); } /** Add a plane convex subset to this instance. * @param subset plane convex subset to add * @throws IllegalArgumentException if the given plane subset is not from * a plane equivalent to this instance */ public void add(final PlaneConvexSubset subset) { Planes.validatePlanesEquivalent(getPlane(), subset.getPlane()); final PlaneConvexSubset.Embedded embedded = subset.getEmbedded(); final Rotation2D rot = getEmbeddedRegionRotation(embedded); final ConvexArea subspaceArea = embedded.getSubspaceRegion(); final ConvexArea toAdd = rot != null ? subspaceArea.transform(rot) : subspaceArea; region.add(toAdd); } /** Add a plane subset to this instance. * @param subset plane subset to add * @throws IllegalArgumentException if the given plane subset is not from * a plane equivalent to this instance */ public void add(final EmbeddedTreePlaneSubset subset) { Planes.validatePlanesEquivalent(getPlane(), subset.getPlane()); final RegionBSPTree2D otherTree = subset.getSubspaceRegion(); final Rotation2D rot = getEmbeddedRegionRotation(subset); final RegionBSPTree2D regionToAdd; if (rot != null) { // we need to transform the subspace region before adding regionToAdd = otherTree.copy(); regionToAdd.transform(rot); } else { regionToAdd = otherTree; } region.union(regionToAdd); } /** Construct a rotation transform used to transform the subspace of the given embedded region plane * subset into the subspace of this instance. Returns null if no transform is needed. This method must only * be called with embedded regions that share an equivalent plane with this instance, meaning that the * planes have the same origin point and normal * @param embedded the embedded region plane subset to compare with the current instance * @return a rotation transform to convert from the subspace of the argument into the current subspace; returns * null if no such transform is needed */ private Rotation2D getEmbeddedRegionRotation(final PlaneSubset.Embedded embedded) { // check if we need to apply a rotation to the given embedded subspace final EmbeddingPlane thisPlane = getPlane(); final EmbeddingPlane otherPlane = embedded.getPlane(); final Precision.DoubleEquivalence precision = thisPlane.getPrecision(); final double uDot = thisPlane.getU().dot(otherPlane.getU()); if (!precision.eq(uDot, 1.0)) { final Vector2D otherPlaneU = thisPlane.toSubspace(otherPlane.getOrigin().add(otherPlane.getU())); final double angle = Math.atan2(otherPlaneU.getY(), otherPlaneU.getX()); return Rotation2D.of(angle); } return null; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy