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

xyz.luan.geometry.de.lighti.clipper.Edge Maven / Gradle / Ivy

package xyz.luan.geometry.de.lighti.clipper;

import java.util.logging.Logger;

import xyz.luan.geometry.OpType;
import xyz.luan.geometry.Point;
import xyz.luan.geometry.de.lighti.clipper.Clipper.Direction;
import xyz.luan.geometry.de.lighti.clipper.Clipper.PolyFillType;
import xyz.luan.geometry.de.lighti.clipper.Clipper.PolyType;

class Edge {
    static enum Side {
        LEFT, RIGHT
    }

    static boolean doesE2InsertBeforeE1(Edge e1, Edge e2) {
        if (e2.current.getX() == e1.current.getX()) {
            if (e2.top.getY() > e1.top.getY()) {
                return e2.top.getX() < topX(e1, e2.top.getY());
            } else {
                return e1.top.getX() > topX(e2, e1.top.getY());
            }
        } else {
            return e2.current.getX() < e1.current.getX();
        }
    }

    static boolean slopesEqual(Edge e1, Edge e2) {
        return e1.getDelta().getY() * e2.getDelta().getX() == e1.getDelta().getX() * e2.getDelta().getY();

    }

    static void swapPolyIndexes(Edge edge1, Edge edge2) {
        final int outIdx = edge1.outIdx;
        edge1.outIdx = edge2.outIdx;
        edge2.outIdx = outIdx;
    }

    static void swapSides(Edge edge1, Edge edge2) {
        final Edge.Side side = edge1.side;
        edge1.side = edge2.side;
        edge2.side = side;
    }

    static double topX(Edge edge, double currentY) {
        if (currentY == edge.getTop().getY()) {
            return edge.getTop().getX();
        }
        return (edge.getBot().getX() + Math.round(edge.deltaX * (currentY - edge.getBot().getY())));
    }

    private final Point bot;

    private final Point current;

    private final Point top;

    private final Point delta;
    double deltaX;

    PolyType polyTyp;

    Edge.Side side;

    int windDelta; // 1 or -1 depending on winding direction

    int windCnt;
    int windCnt2; // winding count of the opposite polytype
    int outIdx;
    Edge next;
    Edge prev;
    Edge nextInLML;
    Edge nextInAEL;
    Edge prevInAEL;
    Edge nextInSEL;
    Edge prevInSEL;

    protected final static int SKIP = -2;

    protected final static int UNASSIGNED = -1;

    protected final static double HORIZONTAL = -3.4E+38;

    private final static Logger LOGGER = Logger.getLogger(Edge.class.getName());

    public Edge() {
        delta = new Point();
        top = new Point();
        bot = new Point();
        current = new Point();
    }

    public Edge findNextLocMin() {
        Edge e = this;
        Edge e2;
        for (;;) {
            while (!e.bot.equals(e.prev.bot) || e.current.equals(e.top)) {
                e = e.next;
            }
            if (e.deltaX != Edge.HORIZONTAL && e.prev.deltaX != Edge.HORIZONTAL) {
                break;
            }
            while (e.prev.deltaX == Edge.HORIZONTAL) {
                e = e.prev;
            }
            e2 = e;
            while (e.deltaX == Edge.HORIZONTAL) {
                e = e.next;
            }
            if (e.top.getY() == e.prev.bot.getY()) {
                continue; // ie just an intermediate horz.
            }
            if (e2.prev.bot.getX() < e.bot.getX()) {
                e = e2;
            }
            break;
        }
        return e;
    }

    public Point getBot() {
        return bot;
    }

    public Point getCurrent() {
        return current;
    }

    public Point getDelta() {
        return delta;
    }

    public Edge getMaximaPair() {
        Edge result = null;
        if (next.top.equals(top) && next.nextInLML == null) {
            result = next;
        } else if (prev.top.equals(top) && prev.nextInLML == null) {
            result = prev;
        }
        if (result != null
                && (result.outIdx == Edge.SKIP || result.nextInAEL == result.prevInAEL && !result.isHorizontal())) {
            return null;
        }
        return result;
    }

    public Edge getNextInAEL(Direction direction) {
        return direction == Direction.LEFT_TO_RIGHT ? nextInAEL : prevInAEL;
    }

    public Point getTop() {
        return top;
    }

    public boolean isContributing(PolyFillType clipFillType, PolyFillType subjFillType, OpType clipType) {
        LOGGER.entering(Edge.class.getName(), "isContributing");

        PolyFillType pft, pft2;
        if (polyTyp == PolyType.SUBJECT) {
            pft = subjFillType;
            pft2 = clipFillType;
        } else {
            pft = clipFillType;
            pft2 = subjFillType;
        }

        switch (pft) {
        case EVEN_ODD:
            // return false if a subj line has been flagged as inside a subj
            // polygon
            if (windDelta == 0 && windCnt != 1) {
                return false;
            }
            break;
        case NON_ZERO:
            if (Math.abs(windCnt) != 1) {
                return false;
            }
            break;
        case POSITIVE:
            if (windCnt != 1) {
                return false;
            }
            break;
        default: // PolyFillType.pftNegative
            if (windCnt != -1) {
                return false;
            }
            break;
        }

        switch (clipType) {
        case INTERSECTION:
            switch (pft2) {
            case EVEN_ODD:
            case NON_ZERO:
                return windCnt2 != 0;
            case POSITIVE:
                return windCnt2 > 0;
            default:
                return windCnt2 < 0;
            }
        case UNION:
            switch (pft2) {
            case EVEN_ODD:
            case NON_ZERO:
                return windCnt2 == 0;
            case POSITIVE:
                return windCnt2 <= 0;
            default:
                return windCnt2 >= 0;
            }
        case DIFFERENCE:
            if (polyTyp == PolyType.SUBJECT) {
                switch (pft2) {
                case EVEN_ODD:
                case NON_ZERO:
                    return windCnt2 == 0;
                case POSITIVE:
                    return windCnt2 <= 0;
                default:
                    return windCnt2 >= 0;
                }
            } else {
                switch (pft2) {
                case EVEN_ODD:
                case NON_ZERO:
                    return windCnt2 != 0;
                case POSITIVE:
                    return windCnt2 > 0;
                default:
                    return windCnt2 < 0;
                }
            }
        case XOR:
            if (windDelta == 0) {
                switch (pft2) {
                case EVEN_ODD:
                case NON_ZERO:
                    return windCnt2 == 0;
                case POSITIVE:
                    return windCnt2 <= 0;
                default:
                    return windCnt2 >= 0;
                }
            } else {
                return true;
            }
        }
        return true;
    }

    public boolean isEvenOddAltFillType(PolyFillType clipFillType, PolyFillType subjFillType) {
        if (polyTyp == PolyType.SUBJECT) {
            return clipFillType == PolyFillType.EVEN_ODD;
        } else {
            return subjFillType == PolyFillType.EVEN_ODD;
        }
    }

    public boolean isEvenOddFillType(PolyFillType clipFillType, PolyFillType subjFillType) {
        if (polyTyp == PolyType.SUBJECT) {
            return subjFillType == PolyFillType.EVEN_ODD;
        } else {
            return clipFillType == PolyFillType.EVEN_ODD;
        }
    }

    public boolean isHorizontal() {
        return delta.getY() == 0;
    }

    public boolean isIntermediate(double y) {
        return top.getY() == y && nextInLML != null;
    }

    public boolean isMaxima(double Y) {
        return top.getY() == Y && nextInLML == null;
    }

    public void reverseHorizontal() {
        // swap horizontal edges' top and bottom x's so they follow the natural
        // progression of the bounds - ie so their xbots will align with the
        // adjoining lower edge. [Helpful in the ProcessHorizontal() method.]
        double temp = top.getX();
        top.setX(bot.getX());
        bot.setX(temp);
    }

    public void setBot(Point bot) {
        this.bot.set(bot);
    }

    public void setCurrent(Point current) {
        this.current.set(current);
    }

    public void setTop(Point top) {
        this.top.set(top);
    }

    @Override
    public String toString() {
        return "TEdge [Bot=" + bot + ", Curr=" + current + ", Top=" + top + ", Delta=" + delta + ", Dx=" + deltaX
                + ", PolyTyp=" + polyTyp + ", Side=" + side + ", WindDelta=" + windDelta + ", WindCnt=" + windCnt
                + ", WindCnt2=" + windCnt2 + ", OutIdx=" + outIdx + ", Next=" + next + ", Prev=" + prev + ", NextInLML="
                + nextInLML + ", NextInAEL=" + nextInAEL + ", PrevInAEL=" + prevInAEL + ", NextInSEL=" + nextInSEL
                + ", PrevInSEL=" + prevInSEL + "]";
    }

    public void updateDeltaX() {

        delta.setX(top.getX() - bot.getX());
        delta.setY(top.getY() - bot.getY());
        if (delta.getY() == 0) {
            deltaX = Edge.HORIZONTAL;
        } else {
            deltaX = (double) delta.getX() / delta.getY();
        }
    }

};




© 2015 - 2025 Weber Informatics LLC | Privacy Policy