Move the `Unclosed` inner class to a top-level class, renamed `Fragments`.
There is no code change (other than move/renaming) in this commit.
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/processing/isoline/Fragments.java b/core/sis-feature/src/main/java/org/apache/sis/internal/processing/isoline/Fragments.java
new file mode 100644
index 0000000..da97430
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/processing/isoline/Fragments.java
@@ -0,0 +1,270 @@
+/*
+ * 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.sis.internal.processing.isoline;
+
+import java.awt.Point;
+import java.util.Map;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collections;
+import org.apache.sis.internal.util.Numerics;
+
+
+/**
+ * List of {@code PolylineBuffer} coordinates that have not yet been closed.
+ * Each {@code double[]} in this list is a copy of a {@link PolylineBuffer} used by {@link Tracer.Level}.
+ * Those copies are performed for saving data before they are overwritten by next iterated cell.
+ *
+ * <h2>List indices and ordering of points</h2>
+ * For a given {@code Fragments} list, all {@code double[]} arrays at even indices shall have their points read
+ * in reverse order and all {@code double[]} arrays at odd indices shall have their points read in forward order.
+ * The list size must be even and the list may contain null elements when there is no data in the corresponding
+ * iteration order. This convention makes easy to reverse the order of all points, simply by reversing the order
+ * of {@code double[]} arrays: because even indices become odd and odd indices become even, points order are
+ * implicitly reverted without the need to rewrite all {@code double[]} array contents.
+ *
+ * @see Tracer.Level#partialPaths
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.3
+ * @since   1.1
+ * @module
+ */
+@SuppressWarnings({"CloneableImplementsClone", "serial"})           // Not intended to be cloned or serialized.
+final class Fragments extends ArrayList<double[]> {
+    /**
+     * The first points and last point in this list of polylines. By convention the coordinate having fraction
+     * digits has all its bits inverted by the {@code ~} operator. May be {@code null} if a coordinate is NaN.
+     * Do not modify {@link Point} field values, because those instances are keys in {@link Tracer.Level#partialPaths}.
+     */
+    private Point firstPoint, lastPoint;
+
+    /**
+     * Creates a list of polylines initialized to the given items.
+     * The given polylines and their opposite directions are cleared by this method.
+     *
+     * @param  polylineOnLeft  first polyline with points in forward order. Shall not be null.
+     * @param  polylineOnTop    next polyline with points in reverse order, or {@code null} if none.
+     */
+    Fragments(final PolylineBuffer polylineOnLeft, final PolylineBuffer polylineOnTop) {
+        /*
+         * Search for first and last point by inspecting `PolylineBuffer` instances in the order shown below.
+         * The first 4 rows and the last 4 rows search for first point and last point respectively.
+         * The empty rows in the middle are an intentional gap for creating a regular pattern that
+         * we can exploit for 3 decisions that need to be done during the loop:
+         *
+         *     ✓ (index & 2) = 0    if using `polylineOnLeft` (otherwise `polylineOnTop`).
+         *     ✓ (index % 3) = 0    if using `opposite` value of polyline (may be null).
+         *     ✓ (index & 1) = 0    if fetching last point (otherwise fetch first point).
+         *
+         *  Index   PolylineBuffer        (order) !(i & 2)  !(i % 3)  !(i & 1)   Comment
+         *  ────────────────────────────────────────────────────────────────────────────
+         *   [0]    polylineOnLeft.opposite  (←)      ✓         ✓         ✓        (1)
+         *   [1]    polylineOnLeft           (→)      ✓                            (2)
+         *   [2]    polylineOnTop            (←)                          ✓        (1)
+         *   [3]    polylineOnTop.opposite   (→)                ✓                  (2)
+         *   [4]                                      ✓                   ✓
+         *   |5]                                      ✓
+         *   [6]    polylineOnTop.opposite   (→)                ✓         ✓        (3)
+         *   [7]    polylineOnTop            (←)                                   (4)
+         *   [8]    polylineOnLeft           (→)      ✓                   ✓        (3)
+         *   [9]    polylineOnLeft.opposite  (←)      ✓         ✓                  (4)
+         *
+         * Comments:
+         *   (1) Last  `PolylineBuffer` point is first `Fragments` point because of reverse iteration order.
+         *   (2) First `PolylineBuffer` point is first `Fragments` point because of forward iteration order.
+         *   (3) Last  `PolylineBuffer` point is last  `Fragments` point because of forward iteration order.
+         *   (4) First `PolylineBuffer` point is last  `Fragments` point because of reverse iteration order.
+         */
+        int index = 0;
+        do {
+            PolylineBuffer polyline = ((index & 2) == 0) ? polylineOnLeft : polylineOnTop;  // See above table (column 4).
+            if (index % 3 == 0 && polyline != null) polyline = polyline.opposite;           // See above table (column 5).
+            if (polyline != null) {
+                int n = polyline.size;
+                if (n != 0) {
+                    final double[] coordinates = polyline.coordinates;
+                    final double x, y;
+                    if (((index & 1) == 0)) {                          // See above table in comment (column 6).
+                        y = coordinates[--n];
+                        x = coordinates[--n];
+                    } else {
+                        x = coordinates[0];
+                        y = coordinates[1];
+                    }
+                    final boolean isLastPoint = (index >= 6);          // See row [6] in above table.
+                    if (Double.isFinite(x) && Double.isFinite(y)) {
+                        final Point p = new Point((int) x, (int) y);
+                        if (!Numerics.isInteger(x)) p.x = ~p.x;
+                        if (!Numerics.isInteger(y)) p.y = ~p.y;
+                        if (isLastPoint) {
+                            lastPoint = p;
+                            break;                                     // Done searching both points.
+                        }
+                        firstPoint = p;
+                    } else if (isLastPoint) {
+                        /*
+                         * If the last point was NaN, check if it was also the case of first point.
+                         * If yes, we will not be able to store this `Fragments` in `partialPaths`
+                         * because we have no point that we can use as key (it would be pointless
+                         * to search for another point further in the `coordinates` array because
+                         * that point could never be matched with another `Fragments`). Leave this
+                         * list empty for avoiding the copies done by `take(…)` calls. Instead,
+                         * callers should write polylines in `Tracer.Level.path` immediately.
+                         */
+                        if (firstPoint == null) return;
+                        break;
+                    }
+                    /*
+                     * Done searching the first point (may still be null if that point is NaN).
+                     * Row [6] in above table is the first row for the search of last point.
+                     */
+                    index = 6;
+                    continue;
+                }
+            }
+            if (++index == 4) {
+                // Found no non-empty polylines during search for first point. No need to continue searching.
+                return;
+            }
+        } while (index <= 9);
+        /*
+         * Copies coordinates only if at least one of `firstPoint` or `lastPoint` is a valid point.
+         */
+        take(polylineOnLeft.opposite);          // Point will be iterated in reverse order.
+        take(polylineOnLeft);                   // Point will be iterated in forward order.
+        if (polylineOnTop != null) {
+            PolylineBuffer suffix = polylineOnTop.opposite;
+            take(polylineOnTop);                // Inverse order. Set `polylineOnTop.opposite` to null.
+            take(suffix);                       // Forward order.
+        }
+    }
+
+    /**
+     * Takes a copy of coordinate values of given polyline, then clears that polyline.
+     */
+    private void take(final PolylineBuffer polyline) {
+        if (polyline != null && polyline.size != 0) {
+            add(Arrays.copyOf(polyline.coordinates, polyline.size));
+            polyline.clear();
+        } else {
+            add(null);                  // No data for iteration order at this position.
+        }
+    }
+
+    /**
+     * Returns {@code true} if the given point is equal to the start point or end point.
+     * This is used in assertions for checking key validity in {@link Tracer.Level#partialPaths}.
+     */
+    final boolean isExtremity(final Point key) {
+        return key.equals(firstPoint) || key.equals(lastPoint);
+    }
+
+    /**
+     * Associates this polyline to its two extremities in the given map. If other polylines already exist
+     * for one or both extremities, then this polyline will be merged with previously existing polylines.
+     * This method returns {@code true} if the polyline has been closed, in which case caller should store
+     * the coordinates in {@link Tracer.Level#path} immediately.
+     *
+     * @param  partialPaths  where to add or merge polylines.
+     * @return {@code true} if this polyline became a closed polygon as a result of merge operation.
+     */
+    final boolean addOrMerge(final Map<Point,Fragments> partialPaths) {
+        final Fragments before = partialPaths.remove(firstPoint);
+        final Fragments after  = partialPaths.remove(lastPoint);
+        if (before != null) partialPaths.remove(addAll(before, true));
+        if (after  != null) partialPaths.remove(addAll(after, false));
+        if (firstPoint != null && firstPoint.equals(lastPoint)) {       // First/last points may have changed.
+            partialPaths.remove(firstPoint);
+            partialPaths.remove(lastPoint);
+            return true;
+        } else {
+            // Intentionally replace previous values.
+            if (firstPoint != null) partialPaths.put(firstPoint, this);
+            if (lastPoint  != null) partialPaths.put(lastPoint,  this);
+            return false;
+        }
+    }
+
+    /**
+     * Prepends or appends the given polylines to this list of polylines.
+     * Points order will be changed as needed in order to match extremities.
+     * The {@code other} instance should be forgotten after this method call.
+     *
+     * @param  other    the other polyline to append or prepend to this polyline.
+     * @param  prepend  {@code true} for prepend operation, {@code false} for append.
+     * @return extremity of {@code other} which has not been assigned to {@code this}.
+     */
+    private Point addAll(final Fragments other, final boolean prepend) {
+        assert ((size() | other.size()) & 1) == 0;      // Must have even number of elements in both lists.
+        /*
+         * In figures below, ● are the extremities to attach together.
+         * `r` is a bitmask telling which polylines to reverse:
+         * 1=this, 2=other, together with combinations 0=none and 3=other.
+         */
+        int r; if ( lastPoint != null &&  lastPoint.equals(other.firstPoint)) r = 0;    // ○──────● ●──────○
+        else   if (firstPoint != null && firstPoint.equals(other.firstPoint)) r = 1;    // ●──────○ ●──────○
+        else   if ( lastPoint != null &&  lastPoint.equals(other. lastPoint)) r = 2;    // ○──────● ○──────●
+        else   if (firstPoint != null && firstPoint.equals(other. lastPoint)) r = 3;    // ●──────○ ○──────●
+        else {
+            // Should never happen because `other` has been obtained using a point of `this`.
+            throw new AssertionError();
+        }
+        if (prepend) r ^= 3;                      // Swap order in above  ○──○ ○──○  figures.
+        if ((r & 1) != 0)  this.reverse();
+        if ((r & 2) != 0) other.reverse();
+        if (prepend) {
+            addAll(0, other);
+            firstPoint = other.firstPoint;
+            return other.lastPoint;
+        } else {
+            addAll(other);
+            lastPoint = other.lastPoint;
+            return other.firstPoint;
+        }
+    }
+
+    /**
+     * Reverse the order of all points. The last polyline will become the first polyline and vice-versa.
+     * For each polyline, points will be iterated in opposite order. The trick on point order is done by
+     * moving polylines at even indices to odd indices, and conversely (see class javadoc for convention
+     * about even/odd indices).
+     */
+    private void reverse() {
+        Collections.reverse(this);
+        final Point swap = firstPoint;
+        firstPoint = lastPoint;
+        lastPoint = swap;
+    }
+
+    /**
+     * Returns the content of this list as an array of {@link PolylineBuffer} instances.
+     * {@code PolylineBuffer} instances at even index should be written with their points in reverse order.
+     *
+     * @see #writeTo(Joiner, PolylineBuffer[], boolean)
+     */
+    final PolylineBuffer[] toPolylines() {
+        final PolylineBuffer[] polylines = new PolylineBuffer[size()];
+        for (int i=0; i<polylines.length; i++) {
+            final double[] coordinates = get(i);
+            if (coordinates != null) {
+                polylines[i] = new PolylineBuffer(coordinates);
+            }
+        }
+        return polylines;
+    }
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/processing/isoline/PolylineBuffer.java b/core/sis-feature/src/main/java/org/apache/sis/internal/processing/isoline/PolylineBuffer.java
index 384c765..5da2768 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/processing/isoline/PolylineBuffer.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/processing/isoline/PolylineBuffer.java
@@ -70,7 +70,7 @@
     }
 
     /**
-     * Creates a new polyline wrapping the given coordinates. Used for wrapping {@link Unclosed}
+     * Creates a new polyline wrapping the given coordinates. Used for wrapping {@link Fragments}
      * instances in objects expected by {@link Tracer#writeTo(Joiner, Polyline[], boolean)}.
      * Those {@code Polyline} instances are temporary.
      */
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/processing/isoline/Tracer.java b/core/sis-feature/src/main/java/org/apache/sis/internal/processing/isoline/Tracer.java
index c7b4bb5..17c777e 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/processing/isoline/Tracer.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/processing/isoline/Tracer.java
@@ -16,12 +16,9 @@
  */
 package org.apache.sis.internal.processing.isoline;
 
-import java.util.Arrays;
-import java.util.ArrayList;
 import java.util.Map;
 import java.util.HashMap;
 import java.util.IdentityHashMap;
-import java.util.Collections;
 import java.awt.Point;
 import java.awt.Rectangle;
 import java.awt.Shape;
@@ -29,7 +26,6 @@
 import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.TransformException;
 import org.apache.sis.internal.feature.j2d.PathBuilder;
-import org.apache.sis.internal.util.Numerics;
 import org.apache.sis.util.Debug;
 
 
@@ -215,14 +211,14 @@
          * by the {@code ~} operator. For each point, there is at most one coordinate having such fraction digits.
          *
          * <h4>Map values</h4>
-         * {@code Unclosed} instances are list of {@code double[]} arrays to be concatenated in a single polygon later.
-         * For a given {@code Unclosed} list, all {@code double[]} arrays at even indices shall have their points read
+         * {@code Fragments} instances are list of {@code double[]} arrays to be concatenated in a single polygon later.
+         * For a given {@code Fragments} list, all {@code double[]} arrays at even indices shall have their points read
          * in reverse order and all {@code double[]} arrays at odd indices shall have their points read in forward order.
          * The list may contain null elements when there is no data in the corresponding iteration order.
          *
          * @see #closeLeftWithTop(PolylineBuffer)
          */
-        private final Map<Point,Unclosed> partialPaths;
+        private final Map<Point,Fragments> partialPaths;
 
         /**
          * Builder of isolines as a Java2D shape, created when first needed.
@@ -539,7 +535,7 @@
                  * Joining left and top polylines do not yet create a closed shape. Consequently we may not write
                  * in the `path` now. But maybe we can close the polygon later after more polylines are attached.
                  */
-                final Unclosed fragment = new Unclosed(polylineOnLeft, polylineOnTop);
+                final Fragments fragment = new Fragments(polylineOnLeft, polylineOnTop);
                 if (fragment.isEmpty()) {
                     /*
                      * Fragment starts and ends with NaN values. We will not be able to complete a polygon.
@@ -566,8 +562,8 @@
          * Writes the content of given polyline without closing it as a polygon.
          * The given polyline will become empty after this method call.
          */
-        private void writeUnclosed(final PolylineBuffer polyline) throws TransformException {
-            final Unclosed fragment = new Unclosed(polyline, null);
+        private void writeFragment(final PolylineBuffer polyline) throws TransformException {
+            final Fragments fragment = new Fragments(polyline, null);
             final PolylineBuffer[] polylines;
             final boolean close;
             if (fragment.isEmpty()) {
@@ -591,7 +587,7 @@
          */
         final void finishedRow() throws TransformException {
             if (!polylineOnLeft.transferToOpposite()) {
-                writeUnclosed(polylineOnLeft);
+                writeFragment(polylineOnLeft);
             }
             isDataAbove = 0;
         }
@@ -611,7 +607,7 @@
              * reading the `shape` field.
              */
             for (int i=0; i < polylinesOnTop.length; i++) {
-                writeUnclosed(polylinesOnTop[i]);
+                writeFragment(polylinesOnTop[i]);
                 polylinesOnTop[i] = null;
             }
             assert isConsistent();
@@ -621,7 +617,7 @@
          * Verifies that {@link #partialPaths} consistency. Used for assertions only.
          */
         private boolean isConsistent() {
-            for (final Map.Entry<Point,Unclosed> entry : partialPaths.entrySet()) {
+            for (final Map.Entry<Point,Fragments> entry : partialPaths.entrySet()) {
                 if (!entry.getValue().isExtremity(entry.getKey())) return false;
             }
             return true;
@@ -645,9 +641,9 @@
             other.path = null;
             assert  this.isConsistent();
             assert other.isConsistent();
-            final IdentityHashMap<Unclosed,Boolean> done = new IdentityHashMap<>(other.partialPaths.size() / 2);
-            for (final Map.Entry<Point,Unclosed> entry : other.partialPaths.entrySet()) {
-                final Unclosed fragment = entry.getValue();
+            final IdentityHashMap<Fragments,Boolean> done = new IdentityHashMap<>(other.partialPaths.size() / 2);
+            for (final Map.Entry<Point,Fragments> entry : other.partialPaths.entrySet()) {
+                final Fragments fragment = entry.getValue();
                 if (done.put(fragment, Boolean.TRUE) == null) {
                     assert fragment.isExtremity(entry.getKey());
                     if (fragment.addOrMerge(partialPaths)) {
@@ -667,8 +663,8 @@
          * @throws TransformException if an error occurred during polylines creation.
          */
         final void flush() throws TransformException {
-            for (final Map.Entry<Point,Unclosed> entry : partialPaths.entrySet()) {
-                final Unclosed fragment = entry.getValue();
+            for (final Map.Entry<Point,Fragments> entry : partialPaths.entrySet()) {
+                final Fragments fragment = entry.getValue();
                 assert fragment.isExtremity(entry.getKey());
                 if (!fragment.isEmpty()) {
                     path = writeTo(path, fragment.toPolylines(), false);
@@ -703,246 +699,6 @@
     }
 
     /**
-     * List of {@code PolylineBuffer} coordinates that have not yet been closed. Each {@code double[]} in this list is
-     * a copy of a {@link PolylineBuffer} used by {@link Level}. Those copies are performed for saving data before they
-     * are overwritten by next iterated cell.
-     *
-     * <h2>List indices and ordering of points</h2>
-     * For a given {@code Unclosed} list, all {@code double[]} arrays at even indices shall have their points read
-     * in reverse order and all {@code double[]} arrays at odd indices shall have their points read in forward order.
-     * The list size must be even and the list may contain null elements when there is no data in the corresponding
-     * iteration order. This convention makes easy to reverse the order of all points, simply by reversing the order
-     * of {@code double[]} arrays: because even indices become odd and odd indices become even, points order are
-     * implicitly reverted without the need to rewrite all {@code double[]} array contents.
-     *
-     * @see Level#partialPaths
-     */
-    @SuppressWarnings({"CloneableImplementsClone", "serial"})           // Not intended to be cloned or serialized.
-    private static final class Unclosed extends ArrayList<double[]> {
-        /**
-         * The first points and last point in this list of polylines. By convention the coordinate having fraction
-         * digits has all its bits inverted by the {@code ~} operator. May be {@code null} if a coordinate is NaN.
-         * Do not modify {@link Point} field values, because those instances are keys in {@link Level#partialPaths}.
-         */
-        private Point firstPoint, lastPoint;
-
-        /**
-         * Creates a list of polylines initialized to the given items.
-         * The given polylines and their opposite directions are cleared by this method.
-         *
-         * @param  polylineOnLeft  first polyline with points in forward order. Shall not be null.
-         * @param  polylineOnTop    next polyline with points in reverse order, or {@code null} if none.
-         */
-        Unclosed(final PolylineBuffer polylineOnLeft, final PolylineBuffer polylineOnTop) {
-            /*
-             * Search for first and last point by inspecting `PolylineBuffer` instances in the order shown below.
-             * The first 4 rows and the last 4 rows search for first point and last point respectively.
-             * The empty rows in the middle are an intentional gap for creating a regular pattern that
-             * we can exploit for 3 decisions that need to be done during the loop:
-             *
-             *     ✓ (index & 2) = 0    if using `polylineOnLeft` (otherwise `polylineOnTop`).
-             *     ✓ (index % 3) = 0    if using `opposite` value of polyline (may be null).
-             *     ✓ (index & 1) = 0    if fetching last point (otherwise fetch first point).
-             *
-             *  Index   PolylineBuffer        (order) !(i & 2)  !(i % 3)  !(i & 1)   Comment
-             *  ────────────────────────────────────────────────────────────────────────────
-             *   [0]    polylineOnLeft.opposite  (←)      ✓         ✓         ✓        (1)
-             *   [1]    polylineOnLeft           (→)      ✓                            (2)
-             *   [2]    polylineOnTop            (←)                          ✓        (1)
-             *   [3]    polylineOnTop.opposite   (→)                ✓                  (2)
-             *   [4]                                      ✓                   ✓
-             *   |5]                                      ✓
-             *   [6]    polylineOnTop.opposite   (→)                ✓         ✓        (3)
-             *   [7]    polylineOnTop            (←)                                   (4)
-             *   [8]    polylineOnLeft           (→)      ✓                   ✓        (3)
-             *   [9]    polylineOnLeft.opposite  (←)      ✓         ✓                  (4)
-             *
-             * Comments:
-             *   (1) Last  `PolylineBuffer` point is first `Unclosed` point because of reverse iteration order.
-             *   (2) First `PolylineBuffer` point is first `Unclosed` point because of forward iteration order.
-             *   (3) Last  `PolylineBuffer` point is last  `Unclosed` point because of forward iteration order.
-             *   (4) First `PolylineBuffer` point is last  `Unclosed` point because of reverse iteration order.
-             */
-            int index = 0;
-            do {
-                PolylineBuffer polyline = ((index & 2) == 0) ? polylineOnLeft : polylineOnTop;  // See above table (column 4).
-                if (index % 3 == 0 && polyline != null) polyline = polyline.opposite;           // See above table (column 5).
-                if (polyline != null) {
-                    int n = polyline.size;
-                    if (n != 0) {
-                        final double[] coordinates = polyline.coordinates;
-                        final double x, y;
-                        if (((index & 1) == 0)) {                          // See above table in comment (column 6).
-                            y = coordinates[--n];
-                            x = coordinates[--n];
-                        } else {
-                            x = coordinates[0];
-                            y = coordinates[1];
-                        }
-                        final boolean isLastPoint = (index >= 6);          // See row [6] in above table.
-                        if (Double.isFinite(x) && Double.isFinite(y)) {
-                            final Point p = new Point((int) x, (int) y);
-                            if (!Numerics.isInteger(x)) p.x = ~p.x;
-                            if (!Numerics.isInteger(y)) p.y = ~p.y;
-                            if (isLastPoint) {
-                                lastPoint = p;
-                                break;                                     // Done searching both points.
-                            }
-                            firstPoint = p;
-                        } else if (isLastPoint) {
-                            /*
-                             * If the last point was NaN, check if it was also the case of first point.
-                             * If yes, we will not be able to store this `Unclosed` in `partialPaths`
-                             * because we have no point that we can use as key (it would be pointless
-                             * to search for another point further in the `coordinates` array because
-                             * that point could never be matched with another `Unclosed`). Leave this
-                             * list empty for avoiding the copies done by `take(…)` calls. Instead,
-                             * callers should write polylines in `Level.path` immediately.
-                             */
-                            if (firstPoint == null) return;
-                            break;
-                        }
-                        /*
-                         * Done searching the first point (may still be null if that point is NaN).
-                         * Row [6] in above table is the first row for the search of last point.
-                         */
-                        index = 6;
-                        continue;
-                    }
-                }
-                if (++index == 4) {
-                    // Found no non-empty polylines during search for first point. No need to continue searching.
-                    return;
-                }
-            } while (index <= 9);
-            /*
-             * Copies coordinates only if at least one of `firstPoint` or `lastPoint` is a valid point.
-             */
-            take(polylineOnLeft.opposite);          // Point will be iterated in reverse order.
-            take(polylineOnLeft);                   // Point will be iterated in forward order.
-            if (polylineOnTop != null) {
-                PolylineBuffer suffix = polylineOnTop.opposite;
-                take(polylineOnTop);                // Inverse order. Set `polylineOnTop.opposite` to null.
-                take(suffix);                       // Forward order.
-            }
-        }
-
-        /**
-         * Takes a copy of coordinate values of given polyline, then clears that polyline.
-         */
-        private void take(final PolylineBuffer polyline) {
-            if (polyline != null && polyline.size != 0) {
-                add(Arrays.copyOf(polyline.coordinates, polyline.size));
-                polyline.clear();
-            } else {
-                add(null);                  // No data for iteration order at this position.
-            }
-        }
-
-        /**
-         * Returns {@code true} if the given point is equal to the start point or end point.
-         * This is used in assertions for checking key validity in {@link Level#partialPaths}.
-         */
-        final boolean isExtremity(final Point key) {
-            return key.equals(firstPoint) || key.equals(lastPoint);
-        }
-
-        /**
-         * Associates this polyline to its two extremities in the given map. If other polylines already exist
-         * for one or both extremities, then this polyline will be merged with previously existing polylines.
-         * This method returns {@code true} if the polyline has been closed, in which case caller should store
-         * the coordinates in {@link Level#path} immediately.
-         *
-         * @param  partialPaths  where to add or merge polylines.
-         * @return {@code true} if this polyline became a closed polygon as a result of merge operation.
-         */
-        final boolean addOrMerge(final Map<Point,Unclosed> partialPaths) {
-            final Unclosed before = partialPaths.remove(firstPoint);
-            final Unclosed after  = partialPaths.remove(lastPoint);
-            if (before != null) partialPaths.remove(addAll(before, true));
-            if (after  != null) partialPaths.remove(addAll(after, false));
-            if (firstPoint != null && firstPoint.equals(lastPoint)) {       // First/last points may have changed.
-                partialPaths.remove(firstPoint);
-                partialPaths.remove(lastPoint);
-                return true;
-            } else {
-                // Intentionally replace previous values.
-                if (firstPoint != null) partialPaths.put(firstPoint, this);
-                if (lastPoint  != null) partialPaths.put(lastPoint,  this);
-                return false;
-            }
-        }
-
-        /**
-         * Prepends or appends the given polylines to this list of polylines.
-         * Points order will be changed as needed in order to match extremities.
-         * The {@code other} instance should be forgotten after this method call.
-         *
-         * @param  other    the other polyline to append or prepend to this polyline.
-         * @param  prepend  {@code true} for prepend operation, {@code false} for append.
-         * @return extremity of {@code other} which has not been assigned to {@code this}.
-         */
-        private Point addAll(final Unclosed other, final boolean prepend) {
-            assert ((size() | other.size()) & 1) == 0;      // Must have even number of elements in both lists.
-            /*
-             * In figures below, ● are the extremities to attach together.
-             * `r` is a bitmask telling which polylines to reverse:
-             * 1=this, 2=other, together with combinations 0=none and 3=other.
-             */
-            int r; if ( lastPoint != null &&  lastPoint.equals(other.firstPoint)) r = 0;    // ○──────● ●──────○
-            else   if (firstPoint != null && firstPoint.equals(other.firstPoint)) r = 1;    // ●──────○ ●──────○
-            else   if ( lastPoint != null &&  lastPoint.equals(other. lastPoint)) r = 2;    // ○──────● ○──────●
-            else   if (firstPoint != null && firstPoint.equals(other. lastPoint)) r = 3;    // ●──────○ ○──────●
-            else {
-                // Should never happen because `other` has been obtained using a point of `this`.
-                throw new AssertionError();
-            }
-            if (prepend) r ^= 3;                      // Swap order in above  ○──○ ○──○  figures.
-            if ((r & 1) != 0)  this.reverse();
-            if ((r & 2) != 0) other.reverse();
-            if (prepend) {
-                addAll(0, other);
-                firstPoint = other.firstPoint;
-                return other.lastPoint;
-            } else {
-                addAll(other);
-                lastPoint = other.lastPoint;
-                return other.firstPoint;
-            }
-        }
-
-        /**
-         * Reverse the order of all points. The last polyline will become the first polyline and vice-versa.
-         * For each polyline, points will be iterated in opposite order. The trick on point order is done by
-         * moving polylines at even indices to odd indices, and conversely (see class javadoc for convention
-         * about even/odd indices).
-         */
-        private void reverse() {
-            Collections.reverse(this);
-            final Point swap = firstPoint;
-            firstPoint = lastPoint;
-            lastPoint = swap;
-        }
-
-        /**
-         * Returns the content of this list as an array of {@link PolylineBuffer} instances.
-         * {@code PolylineBuffer} instances at even index should be written with their points in reverse order.
-         *
-         * @see #writeTo(Joiner, PolylineBuffer[], boolean)
-         */
-        final PolylineBuffer[] toPolylines() {
-            final PolylineBuffer[] polylines = new PolylineBuffer[size()];
-            for (int i=0; i<polylines.length; i++) {
-                final double[] coordinates = get(i);
-                if (coordinates != null) {
-                    polylines[i] = new PolylineBuffer(coordinates);
-                }
-            }
-            return polylines;
-        }
-    }
-
-    /**
      * Assembles arbitrary amount of {@link PolylineBuffer}s in a single Java2D {@link Shape} for an isoline level.
      * This class extends {@link PathBuilder} with two additional features: remove spikes caused by ambiguities,
      * then apply a {@link MathTransform} on all coordinate values.