/*
* 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.math3.geometry.spherical.twod;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.math3.exception.MathIllegalStateException;
import org.apache.commons.math3.exception.util.LocalizedFormats;
import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
import org.apache.commons.math3.geometry.partitioning.BSPTree;
import org.apache.commons.math3.geometry.partitioning.BSPTreeVisitor;
import org.apache.commons.math3.geometry.partitioning.BoundaryAttribute;
import org.apache.commons.math3.geometry.spherical.oned.Arc;
import org.apache.commons.math3.geometry.spherical.oned.ArcsSet;
import org.apache.commons.math3.geometry.spherical.oned.S1Point;
Visitor building edges.
Since: 3.3
/** Visitor building edges.
* @since 3.3
*/
class EdgesBuilder implements BSPTreeVisitor<Sphere2D> {
Root of the tree. /** Root of the tree. */
private final BSPTree<Sphere2D> root;
Tolerance below which points are consider to be identical. /** Tolerance below which points are consider to be identical. */
private final double tolerance;
Built edges and their associated nodes. /** Built edges and their associated nodes. */
private final Map<Edge, BSPTree<Sphere2D>> edgeToNode;
Reversed map. /** Reversed map. */
private final Map<BSPTree<Sphere2D>, List<Edge>> nodeToEdgesList;
Simple constructor.
Params: - root – tree root
- tolerance – below which points are consider to be identical
/** Simple constructor.
* @param root tree root
* @param tolerance below which points are consider to be identical
*/
EdgesBuilder(final BSPTree<Sphere2D> root, final double tolerance) {
this.root = root;
this.tolerance = tolerance;
this.edgeToNode = new IdentityHashMap<Edge, BSPTree<Sphere2D>>();
this.nodeToEdgesList = new IdentityHashMap<BSPTree<Sphere2D>, List<Edge>>();
}
{@inheritDoc} /** {@inheritDoc} */
public Order visitOrder(final BSPTree<Sphere2D> node) {
return Order.MINUS_SUB_PLUS;
}
{@inheritDoc} /** {@inheritDoc} */
public void visitInternalNode(final BSPTree<Sphere2D> node) {
nodeToEdgesList.put(node, new ArrayList<Edge>());
@SuppressWarnings("unchecked")
final BoundaryAttribute<Sphere2D> attribute = (BoundaryAttribute<Sphere2D>) node.getAttribute();
if (attribute.getPlusOutside() != null) {
addContribution((SubCircle) attribute.getPlusOutside(), false, node);
}
if (attribute.getPlusInside() != null) {
addContribution((SubCircle) attribute.getPlusInside(), true, node);
}
}
{@inheritDoc} /** {@inheritDoc} */
public void visitLeafNode(final BSPTree<Sphere2D> node) {
}
Add the contribution of a boundary edge.
Params: - sub – boundary facet
- reversed – if true, the facet has the inside on its plus side
- node – node to which the edge belongs
/** Add the contribution of a boundary edge.
* @param sub boundary facet
* @param reversed if true, the facet has the inside on its plus side
* @param node node to which the edge belongs
*/
private void addContribution(final SubCircle sub, final boolean reversed,
final BSPTree<Sphere2D> node) {
final Circle circle = (Circle) sub.getHyperplane();
final List<Arc> arcs = ((ArcsSet) sub.getRemainingRegion()).asList();
for (final Arc a : arcs) {
final Vertex start = new Vertex((S2Point) circle.toSpace(new S1Point(a.getInf())));
final Vertex end = new Vertex((S2Point) circle.toSpace(new S1Point(a.getSup())));
start.bindWith(circle);
end.bindWith(circle);
final Edge edge;
if (reversed) {
edge = new Edge(end, start, a.getSize(), circle.getReverse());
} else {
edge = new Edge(start, end, a.getSize(), circle);
}
edgeToNode.put(edge, node);
nodeToEdgesList.get(node).add(edge);
}
}
Get the edge that should naturally follow another one.
Params: - previous – edge to be continued
Throws: - MathIllegalStateException – if there is not a single other edge
Returns: other edge, starting where the previous one ends (they
have not been connected yet)
/** Get the edge that should naturally follow another one.
* @param previous edge to be continued
* @return other edge, starting where the previous one ends (they
* have not been connected yet)
* @exception MathIllegalStateException if there is not a single other edge
*/
private Edge getFollowingEdge(final Edge previous)
throws MathIllegalStateException {
// get the candidate nodes
final S2Point point = previous.getEnd().getLocation();
final List<BSPTree<Sphere2D>> candidates = root.getCloseCuts(point, tolerance);
// the following edge we are looking for must start from one of the candidates nodes
double closest = tolerance;
Edge following = null;
for (final BSPTree<Sphere2D> node : candidates) {
for (final Edge edge : nodeToEdgesList.get(node)) {
if (edge != previous && edge.getStart().getIncoming() == null) {
final Vector3D edgeStart = edge.getStart().getLocation().getVector();
final double gap = Vector3D.angle(point.getVector(), edgeStart);
if (gap <= closest) {
closest = gap;
following = edge;
}
}
}
}
if (following == null) {
final Vector3D previousStart = previous.getStart().getLocation().getVector();
if (Vector3D.angle(point.getVector(), previousStart) <= tolerance) {
// the edge connects back to itself
return previous;
}
// this should never happen
throw new MathIllegalStateException(LocalizedFormats.OUTLINE_BOUNDARY_LOOP_OPEN);
}
return following;
}
Get the boundary edges.
Throws: - MathIllegalStateException – if there is not a single other edge
Returns: boundary edges
/** Get the boundary edges.
* @return boundary edges
* @exception MathIllegalStateException if there is not a single other edge
*/
public List<Edge> getEdges() throws MathIllegalStateException {
// connect the edges
for (final Edge previous : edgeToNode.keySet()) {
previous.setNextEdge(getFollowingEdge(previous));
}
return new ArrayList<Edge>(edgeToNode.keySet());
}
}