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

com.esri.core.geometry.Simplificator Maven / Gradle / Ivy

Go to download

The Esri Geometry API for Java enables developers to write custom applications for analysis of spatial data.

There is a newer version: 2.2.4
Show newest version
/*
 Copyright 1995-2013 Esri

   Licensed 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.

 For additional information, contact:
 Environmental Systems Research Institute, Inc.
 Attn: Contracts Dept
 380 New York Street
 Redlands, California, USA 92373

 email: [email protected]
 */
package com.esri.core.geometry;

class Simplificator {
	private EditShape m_shape;
	private int m_geometry;
	private IndexMultiDCList m_sortedVertices;

	private AttributeStreamOfInt32 m_bunchEdgeEndPoints;
	private AttributeStreamOfInt32 m_bunchEdgeCenterPoints;
	private AttributeStreamOfInt32 m_bunchEdgeIndices;
	// private AttributeStreamOfInt32 m_orphanVertices;

	private int m_dbgCounter;
	private int m_sortedVerticesListIndex;
	private int m_userIndexSortedIndexToVertex;
	private int m_userIndexSortedAngleIndexToVertex;
	private int m_nextVertexToProcess;
	private int m_firstCoincidentVertex;
	private int m_knownSimpleResult;
	private boolean m_bWinding;

	private void _beforeRemoveVertex(int vertex, boolean bChangePathFirst) {
		int vertexlistIndex = m_shape.getUserIndex(vertex,
				m_userIndexSortedIndexToVertex);
		// _ASSERT(m_sortedVertices.getData(vertexlistIndex) != 0xdeadbeef);
		if (m_nextVertexToProcess == vertexlistIndex) {
			m_nextVertexToProcess = m_sortedVertices
					.getNext(m_nextVertexToProcess);
		}

		if (m_firstCoincidentVertex == vertexlistIndex)
			m_firstCoincidentVertex = m_sortedVertices
					.getNext(m_firstCoincidentVertex);

		m_sortedVertices.deleteElement(m_sortedVerticesListIndex,
				vertexlistIndex);
		_removeAngleSortInfo(vertex);
		if (bChangePathFirst) {
			int path = m_shape.getPathFromVertex(vertex);
			if (path != -1) {
				int first = m_shape.getFirstVertex(path);
				if (first == vertex) {
					int next = m_shape.getNextVertex(vertex);
					if (next != vertex)
						m_shape.setFirstVertex_(path, next);
					else {
						m_shape.setFirstVertex_(path, -1);
						m_shape.setLastVertex_(path, -1);
					}
				}
			}
		}
	}

	static class SimplificatorAngleComparer extends
			AttributeStreamOfInt32.IntComparator {
		Simplificator m_parent;

		public SimplificatorAngleComparer(Simplificator parent) {
			m_parent = parent;
		}

		@Override
		public int compare(int v1, int v2) {
			return m_parent._compareAngles(v1, v2);
		}

	}

	private boolean _processBunch() {
		boolean bModified = false;
		int iter = 0;
		Point2D ptCenter = new Point2D();
		while (true) {
			m_dbgCounter++;// only for debugging
			iter++;
			// _ASSERT(iter < 10);
			if (m_bunchEdgeEndPoints == null) {
				m_bunchEdgeEndPoints = new AttributeStreamOfInt32(0);
				m_bunchEdgeCenterPoints = new AttributeStreamOfInt32(0);
				m_bunchEdgeIndices = new AttributeStreamOfInt32(0);
			} else {
				m_bunchEdgeEndPoints.clear(false);
				m_bunchEdgeCenterPoints.clear(false);
				m_bunchEdgeIndices.clear(false);
			}

			int currentVertex = m_firstCoincidentVertex;
			int index = 0;
			boolean bFirst = true;
			while (currentVertex != m_nextVertexToProcess) {
				int v = m_sortedVertices.getData(currentVertex);
				{// debug
					Point2D pt = new Point2D();
					m_shape.getXY(v, pt);
					double y = pt.x;
				}
				if (bFirst) {
					m_shape.getXY(v, ptCenter);
					bFirst = false;
				}
				int vertP = m_shape.getPrevVertex(v);
				int vertN = m_shape.getNextVertex(v);
				// _ASSERT(vertP != vertN || m_shape.getPrevVertex(vertN) == v
				// && m_shape.getNextVertex(vertP) == v);

				int id = m_shape.getUserIndex(vertP,
						m_userIndexSortedAngleIndexToVertex);
				if (id != 0xdeadbeef)// avoid adding a point twice
				{
					// _ASSERT(id == -1);
					m_bunchEdgeEndPoints.add(vertP);
					m_shape.setUserIndex(vertP,
							m_userIndexSortedAngleIndexToVertex, 0xdeadbeef);// mark
																				// that
																				// it
																				// has
																				// been
																				// already
																				// added
					m_bunchEdgeCenterPoints.add(v);
					m_bunchEdgeIndices.add(index++);
				}

				int id2 = m_shape.getUserIndex(vertN,
						m_userIndexSortedAngleIndexToVertex);
				if (id2 != 0xdeadbeef) // avoid adding a point twice
				{
					// _ASSERT(id2 == -1);
					m_bunchEdgeEndPoints.add(vertN);
					m_shape.setUserIndex(vertN,
							m_userIndexSortedAngleIndexToVertex, 0xdeadbeef);// mark
																				// that
																				// it
																				// has
																				// been
																				// already
																				// added
					m_bunchEdgeCenterPoints.add(v);
					m_bunchEdgeIndices.add(index++);
				}

				currentVertex = m_sortedVertices.getNext(currentVertex);
			}

			if (m_bunchEdgeEndPoints.size() < 2)
				break;

			// Sort the bunch edpoints by angle (angle between the axis x and
			// the edge, connecting the endpoint with the bunch center)
			m_bunchEdgeIndices.Sort(0, m_bunchEdgeIndices.size(),
					new SimplificatorAngleComparer(this));
			// SORTDYNAMICARRAYEX(m_bunchEdgeIndices, int, 0,
			// m_bunchEdgeIndices.size(), SimplificatorAngleComparer, this);

			for (int i = 0, n = m_bunchEdgeIndices.size(); i < n; i++) {
				int indexL = m_bunchEdgeIndices.get(i);
				int vertex = m_bunchEdgeEndPoints.get(indexL);
				m_shape.setUserIndex(vertex,
						m_userIndexSortedAngleIndexToVertex, i);// rember the
																// sort by angle
																// order
				{// debug
					Point2D pt = new Point2D();
					m_shape.getXY(vertex, pt);
					double y = pt.x;
				}
			}

			boolean bCrossOverResolved = _processCrossOvers(ptCenter);// see of
																		// there
																		// are
																		// crossing
																		// over
																		// edges.
			for (int i = 0, n = m_bunchEdgeIndices.size(); i < n; i++) {
				int indexL = m_bunchEdgeIndices.get(i);
				if (indexL == -1)
					continue;
				int vertex = m_bunchEdgeEndPoints.get(indexL);
				m_shape.setUserIndex(vertex,
						m_userIndexSortedAngleIndexToVertex, -1);// remove
																	// mapping
			}

			if (bCrossOverResolved) {
				bModified = true;
				continue;
			}

			break;
		}

		return bModified;
	}

	private boolean _processCrossOvers(Point2D ptCenter) {
		boolean bFound = false;

		// Resolve all overlaps
		boolean bContinue = true;
		while (bContinue) {
			// The nearest pairts in the middle of the list
			bContinue = false;
			int index1 = 0;
			if (m_bunchEdgeIndices.get(index1) == -1)
				index1 = _getNextEdgeIndex(index1);

			int index2 = _getNextEdgeIndex(index1);

			for (int i = 0, n = m_bunchEdgeIndices.size(); i < n
					&& index1 != -1 && index2 != -1 && index1 != index2; i++) {
				int edgeindex1 = m_bunchEdgeIndices.get(index1);
				int edgeindex2 = m_bunchEdgeIndices.get(index2);

				int vertexB1 = m_bunchEdgeEndPoints.get(edgeindex1);
				int vertexB2 = m_bunchEdgeEndPoints.get(edgeindex2);
				// _ASSERT(vertexB2 != vertexB1);

				int vertexA1 = m_shape.getNextVertex(vertexB1);
				if (!m_shape.isEqualXY(vertexA1, ptCenter))
					vertexA1 = m_shape.getPrevVertex(vertexB1);
				int vertexA2 = m_shape.getNextVertex(vertexB2);
				if (!m_shape.isEqualXY(vertexA2, ptCenter))
					vertexA2 = m_shape.getPrevVertex(vertexB2);

				// _ASSERT(m_shape.isEqualXY(vertexA1, vertexA2));
				// _ASSERT(m_shape.isEqualXY(vertexA1, ptCenter));

				boolean bDirection1 = _getDirection(vertexA1, vertexB1);
				boolean bDirection2 = _getDirection(vertexA2, vertexB2);
				int vertexC1 = bDirection1 ? m_shape.getPrevVertex(vertexA1)
						: m_shape.getNextVertex(vertexA1);
				int vertexC2 = bDirection2 ? m_shape.getPrevVertex(vertexA2)
						: m_shape.getNextVertex(vertexA2);

				boolean bOverlap = false;
				if (_removeSpike(vertexA1))
					bOverlap = true;
				else if (_removeSpike(vertexA2))
					bOverlap = true;
				else if (_removeSpike(vertexB1))
					bOverlap = true;
				else if (_removeSpike(vertexB2))
					bOverlap = true;
				else if (_removeSpike(vertexC1))
					bOverlap = true;
				else if (_removeSpike(vertexC2))
					bOverlap = true;

				if (!bOverlap && m_shape.isEqualXY(vertexB1, vertexB2)) {
					bOverlap = true;
					_resolveOverlap(bDirection1, bDirection2, vertexA1,
							vertexB1, vertexA2, vertexB2);
				}

				if (!bOverlap && m_shape.isEqualXY(vertexC1, vertexC2)) {
					bOverlap = true;
					_resolveOverlap(!bDirection1, !bDirection2, vertexA1,
							vertexC1, vertexA2, vertexC2);
				}

				if (bOverlap)
					bFound = true;

				bContinue |= bOverlap;

				index1 = _getNextEdgeIndex(index1);
				index2 = _getNextEdgeIndex(index1);
			}
		}

		if (!bFound) {// resolve all cross overs
			int index1 = 0;
			if (m_bunchEdgeIndices.get(index1) == -1)
				index1 = _getNextEdgeIndex(index1);

			int index2 = _getNextEdgeIndex(index1);

			for (int i = 0, n = m_bunchEdgeIndices.size(); i < n
					&& index1 != -1 && index2 != -1 && index1 != index2; i++) {
				int edgeindex1 = m_bunchEdgeIndices.get(index1);
				int edgeindex2 = m_bunchEdgeIndices.get(index2);

				int vertexB1 = m_bunchEdgeEndPoints.get(edgeindex1);
				int vertexB2 = m_bunchEdgeEndPoints.get(edgeindex2);

				int vertexA1 = m_shape.getNextVertex(vertexB1);
				if (!m_shape.isEqualXY(vertexA1, ptCenter))
					vertexA1 = m_shape.getPrevVertex(vertexB1);
				int vertexA2 = m_shape.getNextVertex(vertexB2);
				if (!m_shape.isEqualXY(vertexA2, ptCenter))
					vertexA2 = m_shape.getPrevVertex(vertexB2);

				// _ASSERT(m_shape.isEqualXY(vertexA1, vertexA2));
				// _ASSERT(m_shape.isEqualXY(vertexA1, ptCenter));

				boolean bDirection1 = _getDirection(vertexA1, vertexB1);
				boolean bDirection2 = _getDirection(vertexA2, vertexB2);
				int vertexC1 = bDirection1 ? m_shape.getPrevVertex(vertexA1)
						: m_shape.getNextVertex(vertexA1);
				int vertexC2 = bDirection2 ? m_shape.getPrevVertex(vertexA2)
						: m_shape.getNextVertex(vertexA2);

				if (_detectAndResolveCrossOver(bDirection1, bDirection2,
						vertexB1, vertexA1, vertexC1, vertexB2, vertexA2,
						vertexC2)) {
					bFound = true;
				}

				index1 = _getNextEdgeIndex(index1);
				index2 = _getNextEdgeIndex(index1);
			}
		}

		return bFound;
	}

	static class SimplificatorVertexComparer extends
			AttributeStreamOfInt32.IntComparator {
		Simplificator m_parent;

		SimplificatorVertexComparer(Simplificator parent) {
			m_parent = parent;
		}

		@Override
		public int compare(int v1, int v2) {
			return m_parent._compareVerticesSimple(v1, v2);
		}

	}

	private boolean _simplify() {
		boolean bChanged = false;
		boolean bNeedWindingRepeat = true;
		boolean bWinding = false;

		m_userIndexSortedIndexToVertex = -1;
		m_userIndexSortedAngleIndexToVertex = -1;

		int pointCount = m_shape.getPointCount(m_geometry);

		// Sort vertices lexicographically
		// Firstly copy allvertices to an array.
		AttributeStreamOfInt32 verticesSorter = new AttributeStreamOfInt32(0);
		verticesSorter.reserve(pointCount);

		for (int path = m_shape.getFirstPath(m_geometry); path != -1; path = m_shape
				.getNextPath(path)) {
			int vertex = m_shape.getFirstVertex(path);
			for (int index = 0, n = m_shape.getPathSize(path); index < n; index++) {
				verticesSorter.add(vertex);
				vertex = m_shape.getNextVertex(vertex);
			}
		}

		// Sort
		verticesSorter.Sort(0, pointCount,
				new SimplificatorVertexComparer(this));
		// SORTDYNAMICARRAYEX(verticesSorter, int, 0, pointCount,
		// SimplificatorVertexComparer, this);

		// Copy sorted vertices to the m_sortedVertices list. Make a mapping
		// from the edit shape vertices to the sorted vertices.
		m_userIndexSortedIndexToVertex = m_shape.createUserIndex();// this index
																	// is used
																	// to map
																	// from edit
																	// shape
																	// vertex to
																	// the
																	// m_sortedVertices
																	// list
		m_sortedVertices = new IndexMultiDCList();
		m_sortedVerticesListIndex = m_sortedVertices.createList(0);
		for (int i = 0; i < pointCount; i++) {
			int vertex = verticesSorter.get(i);
			{// debug
				Point2D pt = new Point2D();
				m_shape.getXY(vertex, pt);// for debugging
				double y = pt.x;
			}
			int vertexlistIndex = m_sortedVertices.addElement(
					m_sortedVerticesListIndex, vertex);
			m_shape.setUserIndex(vertex, m_userIndexSortedIndexToVertex,
					vertexlistIndex);// remember the sorted list element on the
										// vertex.
			// When we remove a vertex, we also remove associated sorted list
			// element.
		}

		m_userIndexSortedAngleIndexToVertex = m_shape.createUserIndex();// create
																		// additional
																		// list
																		// to
																		// store
																		// angular
																		// sort
																		// mapping.

		m_nextVertexToProcess = -1;

		if (_cleanupSpikes())// cleanup any spikes on the polygon.
			bChanged = true;

		// External iteration loop for the simplificator.
		// ST. I am not sure if it actually needs this loop. TODO: figure this
		// out.
		while (bNeedWindingRepeat) {
			bNeedWindingRepeat = false;

			int max_iter = m_shape.getPointCount(m_geometry) + 10 > 30 ? 1000
					: (m_shape.getPointCount(m_geometry) + 10)
							* (m_shape.getPointCount(m_geometry) + 10);

			// Simplify polygon
			int iRepeatNum = 0;
			boolean bNeedRepeat = false;

			// Internal iteration loop for the simplificator.
			// ST. I am not sure if it actually needs this loop. TODO: figure
			// this out.
			do// while (bNeedRepeat);
			{
				bNeedRepeat = false;

				boolean bVertexRecheck = false;
				m_firstCoincidentVertex = -1;
				int coincidentCount = 0;
				Point2D ptFirst = new Point2D();
				Point2D pt = new Point2D();
				// Main loop of the simplificator. Go through the vertices and
				// for those that have same coordinates,
				for (int vlistindex = m_sortedVertices
						.getFirst(m_sortedVerticesListIndex); vlistindex != IndexMultiDCList
						.nullNode();) {
					int vertex = m_sortedVertices.getData(vlistindex);
					{// debug
						// Point2D pt = new Point2D();
						m_shape.getXY(vertex, pt);
						double d = pt.x;
					}

					if (m_firstCoincidentVertex != -1) {
						// Point2D pt = new Point2D();
						m_shape.getXY(vertex, pt);
						if (ptFirst.isEqual(pt)) {
							coincidentCount++;
						} else {
							ptFirst.setCoords(pt);
							m_nextVertexToProcess = vlistindex;// we remeber the
																// next index in
																// the member
																// variable to
																// allow it to
																// be updated if
																// a vertex is
																// removed
																// inside of the
																// _ProcessBunch.
							if (coincidentCount > 0) {
								boolean result = _processBunch();// process a
																	// bunch of
																	// coinciding
																	// vertices
								if (result) {// something has changed.
												// Note that ProcessBunch may
												// change m_nextVertexToProcess
												// and m_firstCoincidentVertex.
									bNeedRepeat = true;
									if (m_nextVertexToProcess != IndexMultiDCList
											.nullNode()) {
										int v = m_sortedVertices
												.getData(m_nextVertexToProcess);
										m_shape.getXY(v, ptFirst);
									}
								}
							}

							vlistindex = m_nextVertexToProcess;
							m_firstCoincidentVertex = vlistindex;
							coincidentCount = 0;
						}
					} else {
						m_firstCoincidentVertex = vlistindex;
						m_shape.getXY(m_sortedVertices.getData(vlistindex),
								ptFirst);
						coincidentCount = 0;
					}

					vlistindex = m_sortedVertices.getNext(vlistindex);
				}

				m_nextVertexToProcess = -1;

				if (coincidentCount > 0) {
					boolean result = _processBunch();
					if (result)
						bNeedRepeat = true;
				}

				if (iRepeatNum++ > 10) {
					throw new GeometryException("internal error.");
				}

				if (bNeedRepeat)
					_fixOrphanVertices();// fix broken structure of the shape

				if (_cleanupSpikes())
					bNeedRepeat = true;

				bNeedWindingRepeat |= bNeedRepeat && bWinding;

				bChanged |= bNeedRepeat;

			} while (bNeedRepeat);

		}// while (bNeedWindingRepeat)

		// Now process rings. Fix ring orientation and determine rings that need
		// to be deleted.

		m_shape.removeUserIndex(m_userIndexSortedIndexToVertex);
		m_shape.removeUserIndex(m_userIndexSortedAngleIndexToVertex);

		bChanged |= RingOrientationFixer.execute(m_shape, m_geometry,
				m_sortedVertices);

		return bChanged;
	}

	private boolean _getDirection(int vert1, int vert2) {
		if (m_shape.getNextVertex(vert2) == vert1) {
			// _ASSERT(m_shape.getPrevVertex(vert1) == vert2);
			return false;
		} else {
			// _ASSERT(m_shape.getPrevVertex(vert2) == vert1);
			// _ASSERT(m_shape.getNextVertex(vert1) == vert2);
			return true;
		}
	}

	private boolean _detectAndResolveCrossOver(boolean bDirection1,
			boolean bDirection2, int vertexB1, int vertexA1, int vertexC1,
			int vertexB2, int vertexA2, int vertexC2) {
		// _ASSERT(!m_shape.isEqualXY(vertexB1, vertexB2));
		// _ASSERT(!m_shape.isEqualXY(vertexC1, vertexC2));

		if (vertexA1 == vertexA2) {
			_removeAngleSortInfo(vertexB1);
			_removeAngleSortInfo(vertexB2);
			return false;
		}

		// _ASSERT(!m_shape.isEqualXY(vertexB1, vertexC2));
		// _ASSERT(!m_shape.isEqualXY(vertexB1, vertexC1));
		// _ASSERT(!m_shape.isEqualXY(vertexB2, vertexC2));
		// _ASSERT(!m_shape.isEqualXY(vertexB2, vertexC1));
		// _ASSERT(!m_shape.isEqualXY(vertexA1, vertexB1));
		// _ASSERT(!m_shape.isEqualXY(vertexA1, vertexC1));
		// _ASSERT(!m_shape.isEqualXY(vertexA2, vertexB2));
		// _ASSERT(!m_shape.isEqualXY(vertexA2, vertexC2));

		// _ASSERT(m_shape.isEqualXY(vertexA1, vertexA2));

		// get indices of the vertices for the angle sort.
		int iB1 = m_shape.getUserIndex(vertexB1,
				m_userIndexSortedAngleIndexToVertex);
		int iC1 = m_shape.getUserIndex(vertexC1,
				m_userIndexSortedAngleIndexToVertex);
		int iB2 = m_shape.getUserIndex(vertexB2,
				m_userIndexSortedAngleIndexToVertex);
		int iC2 = m_shape.getUserIndex(vertexC2,
				m_userIndexSortedAngleIndexToVertex);
		// _ASSERT(iB1 >= 0);
		// _ASSERT(iC1 >= 0);
		// _ASSERT(iB2 >= 0);
		// _ASSERT(iC2 >= 0);
		// Sort the indices to restore the angle-sort order
		int[] ar = new int[8];
		int[] br = new int[4];

		ar[0] = 0;
		br[0] = iB1;
		ar[1] = 0;
		br[1] = iC1;
		ar[2] = 1;
		br[2] = iB2;
		ar[3] = 1;
		br[3] = iC2;
		for (int j = 1; j < 4; j++)// insertion sort
		{
			int key = br[j];
			int data = ar[j];
			int i = j - 1;
			while (i >= 0 && br[i] > key) {
				br[i + 1] = br[i];
				ar[i + 1] = ar[i];
				i--;
			}
			br[i + 1] = key;
			ar[i + 1] = data;
		}

		int detector = 0;
		if (ar[0] != 0)
			detector |= 1;
		if (ar[1] != 0)
			detector |= 2;
		if (ar[2] != 0)
			detector |= 4;
		if (ar[3] != 0)
			detector |= 8;
		if (detector != 5 && detector != 10)// not an overlap
			return false;

		if (bDirection1 == bDirection2) {
			if (bDirection1) {
				m_shape.setNextVertex_(vertexC2, vertexA1); // B1< >B2
				m_shape.setPrevVertex_(vertexA1, vertexC2); // \ /
				m_shape.setNextVertex_(vertexC1, vertexA2); // A1A2
				m_shape.setPrevVertex_(vertexA2, vertexC1); // / \ //
															// C2>  C1
			}
		} else {
			if (bDirection1) {
				m_shape.setPrevVertex_(vertexA1, vertexB2); // B1<  >B2
				m_shape.setPrevVertex_(vertexB2, vertexA1); // \ /
				m_shape.setNextVertex_(vertexA2, vertexC1); // A1A2
				m_shape.setPrevVertex_(vertexC1, vertexA2); // / \ //
															// C2> >C1

			}
		}

		return true;
	}

	private void _resolveOverlap(boolean bDirection1, boolean bDirection2,
			int vertexA1, int vertexB1, int vertexA2, int vertexB2) {
		if (m_bWinding) {
			_resolveOverlapWinding(bDirection1, bDirection2, vertexA1,
					vertexB1, vertexA2, vertexB2);
		} else {
			_resolveOverlapOddEven(bDirection1, bDirection2, vertexA1,
					vertexB1, vertexA2, vertexB2);
		}
	}

	private void _resolveOverlapWinding(boolean bDirection1,
			boolean bDirection2, int vertexA1, int vertexB1, int vertexA2,
			int vertexB2) {
		throw new GeometryException("not implemented.");
	}

	private void _resolveOverlapOddEven(boolean bDirection1,
			boolean bDirection2, int vertexA1, int vertexB1, int vertexA2,
			int vertexB2) {
		if (bDirection1 != bDirection2) {
			if (bDirection1) {
				// _ASSERT(m_shape.getNextVertex(vertexA1) == vertexB1);
				// _ASSERT(m_shape.getNextVertex(vertexB2) == vertexA2);
				m_shape.setNextVertex_(vertexA1, vertexA2); // B1< B2
				m_shape.setPrevVertex_(vertexA2, vertexA1); // | |
				m_shape.setNextVertex_(vertexB2, vertexB1); // | |
				m_shape.setPrevVertex_(vertexB1, vertexB2); // A1 >A2

				_transferVertexData(vertexA2, vertexA1);
				_beforeRemoveVertex(vertexA2, true);
				m_shape.removeVertexInternal_(vertexA2, true);
				_removeAngleSortInfo(vertexA1);
				_transferVertexData(vertexB2, vertexB1);
				_beforeRemoveVertex(vertexB2, false);
				m_shape.removeVertexInternal_(vertexB2, false);
				_removeAngleSortInfo(vertexB1);
			} else {
				// _ASSERT(m_shape.getNextVertex(vertexB1) == vertexA1);
				// _ASSERT(m_shape.getNextVertex(vertexA2) == vertexB2);
				m_shape.setNextVertex_(vertexA2, vertexA1); // B1 B2<
				m_shape.setPrevVertex_(vertexA1, vertexA2); // | |
				m_shape.setNextVertex_(vertexB1, vertexB2); // | |
				m_shape.setPrevVertex_(vertexB2, vertexB1); // A1< A2

				_transferVertexData(vertexA2, vertexA1);
				_beforeRemoveVertex(vertexA2, false);
				m_shape.removeVertexInternal_(vertexA2, false);
				_removeAngleSortInfo(vertexA1);
				_transferVertexData(vertexB2, vertexB1);
				_beforeRemoveVertex(vertexB2, true);
				m_shape.removeVertexInternal_(vertexB2, true);
				_removeAngleSortInfo(vertexB1);
			}
		} else// bDirection1 == bDirection2
		{
			if (!bDirection1) {
				// _ASSERT(m_shape.getNextVertex(vertexB1) == vertexA1);
				// _ASSERT(m_shape.getNextVertex(vertexB2) == vertexA2);
			} else {
				// _ASSERT(m_shape.getNextVertex(vertexA1) == vertexB1);
				// _ASSERT(m_shape.getNextVertex(vertexA2) == vertexB2);
			}

			// if (m_shape._RingParentageCheckInternal(vertexA1, vertexA2))
			{
				int a1 = bDirection1 ? vertexA1 : vertexB1;
				int a2 = bDirection2 ? vertexA2 : vertexB2;
				int b1 = bDirection1 ? vertexB1 : vertexA1;
				int b2 = bDirection2 ? vertexB2 : vertexA2;

				// m_shape.dbgVerifyIntegrity(a1);//debug
				// m_shape.dbgVerifyIntegrity(a2);//debug

				boolean bVisitedA1 = false;
				m_shape.setNextVertex_(a1, a2); // ^ | <--- ^ \ --.
				m_shape.setNextVertex_(a2, a1); // | | | ^ | |
				m_shape.setPrevVertex_(b1, b2); // | | | | | |
				m_shape.setPrevVertex_(b2, b1); // | | | | | |
				int v = b2; // | | | | =>| |
				while (v != a2) // | | . | | -. |
				{ // | <-- | | | ./ | |
					int prev = m_shape.getPrevVertex(v); // | | | | | | | |
					int next = m_shape.getNextVertex(v); // <-+---<--- |
															// <-+---<--- |
					m_shape.setPrevVertex_(v, next); // --------. <--------
					m_shape.setNextVertex_(v, prev);
					bVisitedA1 |= v == a1;
					v = next;
				}

				if (!bVisitedA1) {
					// a case of two rings being merged
					int prev = m_shape.getPrevVertex(a2);
					int next = m_shape.getNextVertex(a2);
					m_shape.setPrevVertex_(a2, next);
					m_shape.setNextVertex_(a2, prev);
				} else {
					// merge happend on the same ring.
				}

				// m_shape.dbgVerifyIntegrity(b1);//debug
				// m_shape.dbgVerifyIntegrity(a1);//debug

				_transferVertexData(a2, a1);
				_beforeRemoveVertex(a2, true);
				m_shape.removeVertexInternal_(a2, false);
				_removeAngleSortInfo(a1);
				_transferVertexData(b2, b1);
				_beforeRemoveVertex(b2, true);
				m_shape.removeVertexInternal_(b2, false);
				_removeAngleSortInfo(b1);

				// m_shape.dbgVerifyIntegrity(b1);//debug
				// m_shape.dbgVerifyIntegrity(a1);//debug
			}
			// else
			// {
			// m_shape._ReverseRingInternal(vertexA2);
			// _ResolveOverlapOddEven(bDirection1, !bDirection2, vertexA1,
			// vertexB1, vertexA2, vertexB2);
			// }
		}
	}

	private boolean _cleanupSpikes() {
		boolean bModified = false;
		for (int path = m_shape.getFirstPath(m_geometry); path != -1;) {
			int vertex = m_shape.getFirstVertex(path);
			for (int vindex = 0, n = m_shape.getPathSize(path); vindex < n
					&& n > 1;) {
				int prev = m_shape.getPrevVertex(vertex);
				int next = m_shape.getNextVertex(vertex);
				if (m_shape.isEqualXY(prev, next)) {
					bModified = true;
					_beforeRemoveVertex(vertex, false);
					m_shape.removeVertex(vertex, true);// not internal, because
														// path is valid at this
														// point
					_beforeRemoveVertex(next, false);
					m_shape.removeVertex(next, true);
					vertex = prev;
					vindex = 0;
					n = m_shape.getPathSize(path);
				} else {
					vertex = next;
					vindex++;
				}
			}

			if (m_shape.getPathSize(path) < 2) {
				int vertexL = m_shape.getFirstVertex(path);
				for (int vindex = 0, n = m_shape.getPathSize(path); vindex < n; vindex++) {
					_beforeRemoveVertex(vertexL, false);
					vertexL = m_shape.getNextVertex(vertexL);
				}

				path = m_shape.removePath(path);
				bModified = true;
			} else
				path = m_shape.getNextPath(path);
		}

		return bModified;
	}

	private boolean _removeSpike(int vertexIn) {
		// m_shape.dbgVerifyIntegrity(vertex);//debug
		int vertex = vertexIn;

		// _ASSERT(m_shape.isEqualXY(m_shape.getNextVertex(vertex),
		// m_shape.getPrevVertex(vertex)));
		boolean bFound = false;
		while (true) {
			int next = m_shape.getNextVertex(vertex);
			int prev = m_shape.getPrevVertex(vertex);
			if (next == vertex) {// last vertex in a ring
				_beforeRemoveVertex(vertex, true);
				m_shape.removeVertexInternal_(vertex, false);
				return true;
			}

			if (!m_shape.isEqualXY(next, prev))
				break;

			bFound = true;
			_removeAngleSortInfo(prev);
			_removeAngleSortInfo(next);
			_beforeRemoveVertex(vertex, true);
			m_shape.removeVertexInternal_(vertex, false);
			// m_shape.dbgVerifyIntegrity(prev);//debug
			_transferVertexData(next, prev);
			_beforeRemoveVertex(next, true);
			m_shape.removeVertexInternal_(next, true);
			if (next == prev)
				break;// deleted the last vertex

			// m_shape.dbgVerifyIntegrity(prev);//debug

			vertex = prev;
		}
		return bFound;
	}

	private void _fixOrphanVertices() {
		int pathCount = 0;
		// clean any path info
		for (int node = m_sortedVertices.getFirst(m_sortedVertices
				.getFirstList()); node != -1; node = m_sortedVertices
				.getNext(node)) {
			int vertex = m_sortedVertices.getData(node);
			m_shape.setPathToVertex_(vertex, -1);
		}
		int geometrySize = 0;
		for (int path = m_shape.getFirstPath(m_geometry); path != -1;) {
			int first = m_shape.getFirstVertex(path);
			if (first == -1 || m_shape.getPathFromVertex(first) != -1) {
				int p = path;
				path = m_shape.getNextPath(path);
				m_shape.removePathOnly_(p);
				continue;
			}

			m_shape.setPathToVertex_(first, path);
			int pathSize = 1;
			for (int vertex = m_shape.getNextVertex(first); vertex != first; vertex = m_shape
					.getNextVertex(vertex)) {
				// _ASSERT(m_shape.getPathFromVertex(vertex) == -1);
				m_shape.setPathToVertex_(vertex, path);
				// _ASSERT(m_shape.getNextVertex(m_shape.getPrevVertex(vertex))
				// == vertex);
				pathSize++;
			}
			m_shape.setPathSize_(path, pathSize);
			m_shape.setLastVertex_(path, m_shape.getPrevVertex(first));
			geometrySize += pathSize;
			pathCount++;
			path = m_shape.getNextPath(path);
		}

		// produce new paths for the orphan vertices.
		for (int node = m_sortedVertices.getFirst(m_sortedVertices
				.getFirstList()); node != -1; node = m_sortedVertices
				.getNext(node)) {
			int vertex = m_sortedVertices.getData(node);
			if (m_shape.getPathFromVertex(vertex) != -1)
				continue;
			int path = m_shape.insertPath(m_geometry, -1);
			int pathSize = 0;
			int first = vertex;
			while (true) {
				m_shape.setPathToVertex_(vertex, path);
				pathSize++;
				int next = m_shape.getNextVertex(vertex);
				// _ASSERT(m_shape.getNextVertex(m_shape.getPrevVertex(vertex))
				// == vertex);
				if (next == first)
					break;
				vertex = next;
			}

			m_shape.setClosedPath(path, true);

			m_shape.setPathSize_(path, pathSize);
			m_shape.setFirstVertex_(path, first);
			m_shape.setLastVertex_(path, m_shape.getPrevVertex(first));
			geometrySize += pathSize;
			pathCount++;
		}
		m_shape.setGeometryPathCount_(m_geometry, pathCount);
		int totalPointCount = m_shape.getTotalPointCount()
				- m_shape.getPointCount(m_geometry);
		m_shape.setGeometryVertexCount_(m_geometry, geometrySize);
		m_shape.setTotalPointCount_(totalPointCount + geometrySize);
	}

	private int _getNextEdgeIndex(int indexIn) {
		int index = indexIn;
		for (int i = 0, n = m_bunchEdgeIndices.size() - 1; i < n; i++) {
			index = (index + 1) % m_bunchEdgeIndices.size();
			if (m_bunchEdgeIndices.get(index) != -1)
				return index;
		}
		return -1;
	}

	private void _transferVertexData(int vertexFrom, int vertexTo) {
		int v1 = m_shape.getUserIndex(vertexTo, m_userIndexSortedIndexToVertex);
		int v2 = m_shape.getUserIndex(vertexTo,
				m_userIndexSortedAngleIndexToVertex);
		m_shape.transferAllDataToTheVertex(vertexFrom, vertexTo);
		m_shape.setUserIndex(vertexTo, m_userIndexSortedIndexToVertex, v1);
		m_shape.setUserIndex(vertexTo, m_userIndexSortedAngleIndexToVertex, v2);
	}

	private void _removeAngleSortInfo(int vertex) {
		int angleIndex = m_shape.getUserIndex(vertex,
				m_userIndexSortedAngleIndexToVertex);
		if (angleIndex != -1) {
			m_bunchEdgeIndices.set(angleIndex, -1);
			m_shape.setUserIndex(vertex, m_userIndexSortedAngleIndexToVertex,
					-1);
		}
	}

	protected Simplificator() {
		m_dbgCounter = 0;
	}

	public static boolean execute(EditShape shape, int geometry,
			int knownSimpleResult) {
		Simplificator simplificator = new Simplificator();
		simplificator.m_shape = shape;
		// simplificator.m_bWinding = bWinding;
		simplificator.m_geometry = geometry;
		simplificator.m_knownSimpleResult = knownSimpleResult;
		return simplificator._simplify();
	}

	int _compareVerticesSimple(int v1, int v2) {
		Point2D pt1 = new Point2D();
		m_shape.getXY(v1, pt1);
		Point2D pt2 = new Point2D();
		m_shape.getXY(v2, pt2);
		int res = pt1.compare(pt2);
		return res;
	}

	int _compareAngles(int index1, int index2) {
		int vert1 = m_bunchEdgeEndPoints.get(index1);
		Point2D pt1 = new Point2D();
		m_shape.getXY(vert1, pt1);
		Point2D pt2 = new Point2D();
		int vert2 = m_bunchEdgeEndPoints.get(index2);
		m_shape.getXY(vert2, pt2);

		if (pt1.isEqual(pt2))
			return 0;// overlap case

		int vert10 = m_bunchEdgeCenterPoints.get(index1);
		Point2D pt10 = new Point2D();
		m_shape.getXY(vert10, pt10);

		int vert20 = m_bunchEdgeCenterPoints.get(index2);
		Point2D pt20 = new Point2D();
		m_shape.getXY(vert20, pt20);
		// _ASSERT(pt10.isEqual(pt20));

		Point2D v1 = new Point2D();
		v1.sub(pt1, pt10);
		Point2D v2 = new Point2D();
		v2.sub(pt2, pt20);
		int result = Point2D._compareVectors(v1, v2);
		return result;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy