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

eu.hansolo.fx.geometry.FlatteningPathIterator Maven / Gradle / Ivy

/*
 * Copyright (c) 2017 by Gerrit Grunwald
 *
 * 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.
 */

package eu.hansolo.fx.geometry;

import eu.hansolo.fx.geometry.Path.WindingRule;

import java.util.NoSuchElementException;


public class FlatteningPathIterator implements PathIterator {
    static final int GROW_SIZE = 24;              // Multiple of bezierCurve & quadCurve curve size
    volatile     double hold[] = new double[14];  // The cache of interpolated coords

    PathIterator src;           // The source iterator
    double       flatness;      // Flatness parameter
    double       squareflat;    // Square of the flatness parameter for testing against squared lengths
    int          limit;         // Maximum number of recursion levels hold field
    double       curx, cury;    // The ending x,y of the last segment
    double       movx, movy;    // The x,y of the last move segment
    int          holdType;      // The type of the curve being held for interpolation
    int          holdEnd;       // The index of the last curve segment being held for interpolation
    int          holdIndex;     // The index of the curve segment that was last interpolated.  This is the curve segment ready to be returned in the next call to currentSegment().
    int          levels[];      // The recursion level at which each curve being held in storage was generated.
    int          levelIndex;    // The index of the entry in the levels array of the curve segment at the holdIndex
    boolean      done;          // True when iteration is done


    public FlatteningPathIterator(PathIterator src, double flatness) {
        this(src, flatness, 10);
    }
    public FlatteningPathIterator(PathIterator src, double flatness, int limit) {
        if (flatness < 0) { throw new IllegalArgumentException("flatness must be >= 0"); }
        if (limit < 0)    { throw new IllegalArgumentException("limit must be >= 0"); }
        this.src = src;
        this.flatness   = flatness;
        this.squareflat = flatness * flatness;
        this.limit      = limit;
        this.levels     = new int[limit + 1];
        // prime the first path segment
        next(false);
    }

    public double getFlatness() { return Math.sqrt(squareflat); }

    public int getRecursionLimit() { return limit; }

    void ensureHoldCapacity(final int WANT) {
        if (holdIndex - WANT < 0) {
            int    have      = hold.length - holdIndex;
            int    newsize   = hold.length + GROW_SIZE;
            double newhold[] = new double[newsize];
            System.arraycopy(hold, holdIndex, newhold, holdIndex + GROW_SIZE, have);
            hold = newhold;
            holdIndex += GROW_SIZE;
            holdEnd += GROW_SIZE;
        }
    }

    public boolean isDone() { return done; }

    public void next() { next(true); }
    private void next(final boolean DO_NEXT) {
        int level;

        if (holdIndex >= holdEnd) {
            if (DO_NEXT) { src.next(); }
            if (src.isDone()) {
                done = true;
                return;
            }
            holdType   = src.currentSegment(hold);
            levelIndex = 0;
            levels[0]  = 0;
        }

        switch (holdType) {
            case MOVE_TO:
            case LINE_TO:
                curx = hold[0];
                cury = hold[1];
                if (holdType == MOVE_TO) {
                    movx = curx;
                    movy = cury;
                }
                holdIndex = 0;
                holdEnd   = 0;
                break;
            case CLOSE:
                curx = movx;
                cury = movy;
                holdIndex = 0;
                holdEnd   = 0;
                break;
            case QUAD_TO:
                if (holdIndex >= holdEnd) {
                    holdIndex = hold.length - 6;
                    holdEnd   = hold.length - 2;
                    hold[holdIndex + 0] = curx;
                    hold[holdIndex + 1] = cury;
                    hold[holdIndex + 2] = hold[0];
                    hold[holdIndex + 3] = hold[1];
                    hold[holdIndex + 4] = curx = hold[2];
                    hold[holdIndex + 5] = cury = hold[3];
                }

                level = levels[levelIndex];
                while (level < limit) {
                    if (QuadCurve.getFlatnessSq(hold, holdIndex) < squareflat) { break; }

                    ensureHoldCapacity(4);
                    QuadCurve.subdivide(hold, holdIndex, hold, holdIndex - 4, hold, holdIndex);
                    holdIndex -= 4;

                    level++;
                    levels[levelIndex] = level;
                    levelIndex++;
                    levels[levelIndex] = level;
                }

                holdIndex += 4;
                levelIndex--;
                break;
            case BEZIER_TO:
                if (holdIndex >= holdEnd) {
                    holdIndex           = hold.length - 8;
                    holdEnd             = hold.length - 2;
                    hold[holdIndex + 0] = curx;
                    hold[holdIndex + 1] = cury;
                    hold[holdIndex + 2] = hold[0];
                    hold[holdIndex + 3] = hold[1];
                    hold[holdIndex + 4] = hold[2];
                    hold[holdIndex + 5] = hold[3];
                    hold[holdIndex + 6] = curx = hold[4];
                    hold[holdIndex + 7] = cury = hold[5];
                }

                level = levels[levelIndex];
                while (level < limit) {
                    if (BezierCurve.getFlatnessSq(hold, holdIndex) < squareflat) { break; }

                    ensureHoldCapacity(6);
                    BezierCurve.subdivide(hold, holdIndex, hold, holdIndex - 6, hold, holdIndex);
                    holdIndex -= 6;

                    level++;
                    levels[levelIndex] = level;
                    levelIndex++;
                    levels[levelIndex] = level;
                }

                holdIndex += 6;
                levelIndex--;
                break;
        }
    }

    public WindingRule getWindingRule() { return src.getWindingRule(); }

    public int currentSegment(final double[] COORDS) {
        if (isDone()) { throw new NoSuchElementException("flattening iterator out of bounds"); }
        int type = holdType;
        if (type != CLOSE) {
            COORDS[0] = hold[holdIndex + 0];
            COORDS[1] = hold[holdIndex + 1];
            if (type != MOVE_TO) { type = LINE_TO; }
        }
        return type;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy