AbstractGreatArcConnector.java
- /*
- * 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.spherical.twod;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.Objects;
- import org.apache.commons.geometry.euclidean.internal.AbstractPathConnector;
- import org.apache.commons.geometry.euclidean.threed.Vector3D;
- /** Abstract class for joining collections of great arcs into connected
- * paths. This class is not thread-safe.
- */
- public abstract class AbstractGreatArcConnector
- extends AbstractPathConnector<AbstractGreatArcConnector.ConnectableGreatArc> {
- /** Add an arc to the connector, leaving it unconnected until a later call to
- * to {@link #connect(Iterable)} or {@link #connectAll()}.
- * @param arc arc to add
- * @see #connect(Iterable)
- * @see #connectAll()
- */
- public void add(final GreatArc arc) {
- addPathElement(new ConnectableGreatArc(arc));
- }
- /** Add a collection of arcs to the connector, leaving them unconnected
- * until a later call to {@link #connect(Iterable)} or
- * {@link #connectAll()}.
- * @param arcs arcs to add
- * @see #connect(Iterable)
- * @see #connectAll()
- * @see #add(GreatArc)
- */
- public void add(final Iterable<GreatArc> arcs) {
- for (final GreatArc segment : arcs) {
- add(segment);
- }
- }
- /** Add a collection of arcs to the connector and attempt to connect each new
- * arc with existing ones. Connections made at this time will not be
- * overwritten by subsequent calls to this or other connection methods,
- * (eg, {@link #connectAll()}).
- *
- * <p>The connector is not reset by this call. Additional arc can still be added
- * to the current set of paths.</p>
- * @param arcs arcs to connect
- * @see #connectAll()
- */
- public void connect(final Iterable<GreatArc> arcs) {
- final List<ConnectableGreatArc> newEntries = new ArrayList<>();
- for (final GreatArc segment : arcs) {
- newEntries.add(new ConnectableGreatArc(segment));
- }
- connectPathElements(newEntries);
- }
- /** Add the given arcs to this instance and connect all current
- * arc into paths. This call is equivalent to
- * <pre>
- * connector.add(arcs);
- * List<GreatArcPath> result = connector.connectAll();
- * </pre>
- *
- * <p>The connector is reset after this call. Further calls to
- * add or connect arcs will result in new paths being generated.</p>
- * @param arcs arcs to add
- * @return the connected arc paths
- * @see #add(Iterable)
- * @see #connectAll()
- */
- public List<GreatArcPath> connectAll(final Iterable<GreatArc> arcs) {
- add(arcs);
- return connectAll();
- }
- /** Connect all current arcs into connected paths, returning the result as a
- * list of arc paths.
- *
- * <p>The connector is reset after this call. Further calls to
- * add or connect arcs will result in new paths being generated.</p>
- * @return the connected line segments paths
- */
- public List<GreatArcPath> connectAll() {
- final List<ConnectableGreatArc> roots = computePathRoots();
- final List<GreatArcPath> paths = new ArrayList<>(roots.size());
- for (final ConnectableGreatArc root : roots) {
- paths.add(toPath(root));
- }
- return paths;
- }
- /** Convert the linked list of path elements starting at the argument
- * into a {@link GreatArcPath}.
- * @param root root of a connected path linked list
- * @return a great arc path representing the linked list path
- */
- private GreatArcPath toPath(final ConnectableGreatArc root) {
- final GreatArcPath.Builder builder = GreatArcPath.builder(null);
- builder.append(root.getArc());
- ConnectableGreatArc current = root.getNext();
- while (current != null && current != root) {
- builder.append(current.getArc());
- current = current.getNext();
- }
- return builder.build();
- }
- /** Internal class for connecting {@link GreatArc}s into {@link GreatArcPath}s.
- */
- protected static class ConnectableGreatArc extends AbstractPathConnector.ConnectableElement<ConnectableGreatArc> {
- /** Segment start point. This will be used to connect to other path elements. */
- private final Point2S start;
- /** Great arc for this instance. */
- private final GreatArc arc;
- /** Create a new instance with the given start point. This constructor is
- * intended only for performing searches for other path elements.
- * @param start start point
- */
- public ConnectableGreatArc(final Point2S start) {
- this(start, null);
- }
- /** Create a new instance from the given arc.
- * @param arc arc for the instance
- */
- public ConnectableGreatArc(final GreatArc arc) {
- this(arc.getStartPoint(), arc);
- }
- /** Create a new instance with the given start point and arc.
- * @param start start point
- * @param arc arc for the instance
- */
- private ConnectableGreatArc(final Point2S start, final GreatArc arc) {
- this.start = start;
- this.arc = arc;
- }
- /** Get the arc for the instance.
- * @return the arc for the instance
- */
- public GreatArc getArc() {
- return arc;
- }
- /** {@inheritDoc} */
- @Override
- public boolean hasStart() {
- return start != null;
- }
- /** {@inheritDoc} */
- @Override
- public boolean hasEnd() {
- return arc.getEndPoint() != null;
- }
- /** {@inheritDoc} */
- @Override
- public boolean endPointsEq(final ConnectableGreatArc other) {
- if (hasEnd() && other.hasEnd()) {
- return arc.getEndPoint()
- .eq(other.arc.getEndPoint(), arc.getCircle().getPrecision());
- }
- return false;
- }
- /** Return true if this instance has a size equivalent to zero.
- * @return true if this instance has a size equivalent to zero.
- */
- public boolean hasZeroSize() {
- return arc != null && arc.getCircle().getPrecision().eqZero(arc.getSize());
- }
- /** {@inheritDoc} */
- @Override
- public boolean canConnectTo(final ConnectableGreatArc next) {
- final Point2S end = arc.getEndPoint();
- final Point2S nextStart = next.start;
- return end != null && nextStart != null &&
- end.eq(nextStart, arc.getCircle().getPrecision());
- }
- /** {@inheritDoc} */
- @Override
- public double getRelativeAngle(final ConnectableGreatArc other) {
- return arc.getCircle().angle(other.getArc().getCircle());
- }
- /** {@inheritDoc} */
- @Override
- public ConnectableGreatArc getConnectionSearchKey() {
- return new ConnectableGreatArc(arc.getEndPoint());
- }
- /** {@inheritDoc} */
- @Override
- public boolean shouldContinueConnectionSearch(final ConnectableGreatArc candidate,
- final boolean ascending) {
- if (candidate.hasStart()) {
- final double candidatePolar = candidate.getArc().getStartPoint().getPolar();
- final double thisPolar = arc.getEndPoint().getPolar();
- final int cmp = arc.getCircle().getPrecision().compare(candidatePolar, thisPolar);
- return ascending ? cmp <= 0 : cmp >= 0;
- }
- return true;
- }
- /** {@inheritDoc} */
- @Override
- public int compareTo(final ConnectableGreatArc other) {
- int cmp = Point2S.POLAR_AZIMUTH_ASCENDING_ORDER.compare(start, other.start);
- if (cmp == 0) {
- // sort entries without arcs before ones with arcs
- final boolean thisHasArc = arc != null;
- final boolean otherHasArc = other.arc != null;
- cmp = Boolean.compare(thisHasArc, otherHasArc);
- if (cmp == 0 && thisHasArc) {
- // place point-like segments before ones with non-zero length
- cmp = Boolean.compare(this.hasZeroSize(), other.hasZeroSize());
- if (cmp == 0) {
- // sort by circle pole
- cmp = Vector3D.COORDINATE_ASCENDING_ORDER.compare(
- arc.getCircle().getPole(),
- other.arc.getCircle().getPole());
- }
- }
- }
- return cmp;
- }
- /** {@inheritDoc} */
- @Override
- public int hashCode() {
- return Objects.hash(start, arc);
- }
- /** {@inheritDoc} */
- @Override
- public boolean equals(final Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null || !this.getClass().equals(obj.getClass())) {
- return false;
- }
- final ConnectableGreatArc other = (ConnectableGreatArc) obj;
- return Objects.equals(this.start, other.start) &&
- Objects.equals(this.arc, other.arc);
- }
- /** {@inheritDoc} */
- @Override
- protected ConnectableGreatArc getSelf() {
- return this;
- }
- }
- }