| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- import hashmap from "./hash/hashmap.js";
- import equalPoint from "./hash/point-equal.js";
- import hashPoint from "./hash/point-hash.js";
- // Given a cut topology, combines duplicate arcs.
- export default function(topology) {
- var coordinates = topology.coordinates,
- lines = topology.lines, line,
- rings = topology.rings, ring,
- arcCount = lines.length + rings.length,
- i, n;
- delete topology.lines;
- delete topology.rings;
- // Count the number of (non-unique) arcs to initialize the hashmap safely.
- for (i = 0, n = lines.length; i < n; ++i) {
- line = lines[i]; while (line = line.next) ++arcCount;
- }
- for (i = 0, n = rings.length; i < n; ++i) {
- ring = rings[i]; while (ring = ring.next) ++arcCount;
- }
- var arcsByEnd = hashmap(arcCount * 2 * 1.4, hashPoint, equalPoint),
- arcs = topology.arcs = [];
- for (i = 0, n = lines.length; i < n; ++i) {
- line = lines[i];
- do {
- dedupLine(line);
- } while (line = line.next);
- }
- for (i = 0, n = rings.length; i < n; ++i) {
- ring = rings[i];
- if (ring.next) { // arc is no longer closed
- do {
- dedupLine(ring);
- } while (ring = ring.next);
- } else {
- dedupRing(ring);
- }
- }
- function dedupLine(arc) {
- var startPoint,
- endPoint,
- startArcs, startArc,
- endArcs, endArc,
- i, n;
- // Does this arc match an existing arc in order?
- if (startArcs = arcsByEnd.get(startPoint = coordinates[arc[0]])) {
- for (i = 0, n = startArcs.length; i < n; ++i) {
- startArc = startArcs[i];
- if (equalLine(startArc, arc)) {
- arc[0] = startArc[0];
- arc[1] = startArc[1];
- return;
- }
- }
- }
- // Does this arc match an existing arc in reverse order?
- if (endArcs = arcsByEnd.get(endPoint = coordinates[arc[1]])) {
- for (i = 0, n = endArcs.length; i < n; ++i) {
- endArc = endArcs[i];
- if (reverseEqualLine(endArc, arc)) {
- arc[1] = endArc[0];
- arc[0] = endArc[1];
- return;
- }
- }
- }
- if (startArcs) startArcs.push(arc); else arcsByEnd.set(startPoint, [arc]);
- if (endArcs) endArcs.push(arc); else arcsByEnd.set(endPoint, [arc]);
- arcs.push(arc);
- }
- function dedupRing(arc) {
- var endPoint,
- endArcs,
- endArc,
- i, n;
- // Does this arc match an existing line in order, or reverse order?
- // Rings are closed, so their start point and end point is the same.
- if (endArcs = arcsByEnd.get(endPoint = coordinates[arc[0]])) {
- for (i = 0, n = endArcs.length; i < n; ++i) {
- endArc = endArcs[i];
- if (equalRing(endArc, arc)) {
- arc[0] = endArc[0];
- arc[1] = endArc[1];
- return;
- }
- if (reverseEqualRing(endArc, arc)) {
- arc[0] = endArc[1];
- arc[1] = endArc[0];
- return;
- }
- }
- }
- // Otherwise, does this arc match an existing ring in order, or reverse order?
- if (endArcs = arcsByEnd.get(endPoint = coordinates[arc[0] + findMinimumOffset(arc)])) {
- for (i = 0, n = endArcs.length; i < n; ++i) {
- endArc = endArcs[i];
- if (equalRing(endArc, arc)) {
- arc[0] = endArc[0];
- arc[1] = endArc[1];
- return;
- }
- if (reverseEqualRing(endArc, arc)) {
- arc[0] = endArc[1];
- arc[1] = endArc[0];
- return;
- }
- }
- }
- if (endArcs) endArcs.push(arc); else arcsByEnd.set(endPoint, [arc]);
- arcs.push(arc);
- }
- function equalLine(arcA, arcB) {
- var ia = arcA[0], ib = arcB[0],
- ja = arcA[1], jb = arcB[1];
- if (ia - ja !== ib - jb) return false;
- for (; ia <= ja; ++ia, ++ib) if (!equalPoint(coordinates[ia], coordinates[ib])) return false;
- return true;
- }
- function reverseEqualLine(arcA, arcB) {
- var ia = arcA[0], ib = arcB[0],
- ja = arcA[1], jb = arcB[1];
- if (ia - ja !== ib - jb) return false;
- for (; ia <= ja; ++ia, --jb) if (!equalPoint(coordinates[ia], coordinates[jb])) return false;
- return true;
- }
- function equalRing(arcA, arcB) {
- var ia = arcA[0], ib = arcB[0],
- ja = arcA[1], jb = arcB[1],
- n = ja - ia;
- if (n !== jb - ib) return false;
- var ka = findMinimumOffset(arcA),
- kb = findMinimumOffset(arcB);
- for (var i = 0; i < n; ++i) {
- if (!equalPoint(coordinates[ia + (i + ka) % n], coordinates[ib + (i + kb) % n])) return false;
- }
- return true;
- }
- function reverseEqualRing(arcA, arcB) {
- var ia = arcA[0], ib = arcB[0],
- ja = arcA[1], jb = arcB[1],
- n = ja - ia;
- if (n !== jb - ib) return false;
- var ka = findMinimumOffset(arcA),
- kb = n - findMinimumOffset(arcB);
- for (var i = 0; i < n; ++i) {
- if (!equalPoint(coordinates[ia + (i + ka) % n], coordinates[jb - (i + kb) % n])) return false;
- }
- return true;
- }
- // Rings are rotated to a consistent, but arbitrary, start point.
- // This is necessary to detect when a ring and a rotated copy are dupes.
- function findMinimumOffset(arc) {
- var start = arc[0],
- end = arc[1],
- mid = start,
- minimum = mid,
- minimumPoint = coordinates[mid];
- while (++mid < end) {
- var point = coordinates[mid];
- if (point[0] < minimumPoint[0] || point[0] === minimumPoint[0] && point[1] < minimumPoint[1]) {
- minimum = mid;
- minimumPoint = point;
- }
- }
- return minimum - start;
- }
- return topology;
- }
|