package org.geogebra.common.util.clipper;
import org.geogebra.common.kernel.Kernel;
import org.geogebra.common.kernel.arithmetic.MyDouble;
import org.geogebra.common.util.clipper.Clipper.ClipType;
import org.geogebra.common.util.clipper.Clipper.Direction;
import org.geogebra.common.util.clipper.Clipper.PolyFillType;
import org.geogebra.common.util.clipper.Clipper.PolyType;
import org.geogebra.common.util.clipper.Point.DoublePoint;
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());
}
return e1.top.getX() > topX(e2, e1.top.getY());
}
return e2.current.getX() < e1.current.getX();
}
static boolean slopesEqual(Edge e1, Edge e2) {
return Kernel.isEqual(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;
}
/**
* modified to be compatible with double
*/
static double topX(Edge edge, double currentY) {
if (currentY == edge.getTop().getY()) {
return edge.getTop().getX();
}
return edge.getBot().getX()
+ edge.deltaX * (currentY - edge.getBot().getY());
}
// private final LongPoint bot;
private final DoublePoint bot;
// private final LongPoint current;
private final DoublePoint current;
// private final LongPoint top;
private final DoublePoint top;
// private final LongPoint delta;
private final DoublePoint 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;
/**
* modified to be compatible with double
*/
public Edge() {
delta = new DoublePoint();
top = new DoublePoint();
bot = new DoublePoint();
current = new DoublePoint();
}
public Edge findNextLocMin() {
Edge e = this;
Edge e2;
boolean go = true;
while (go) {
while (!e.bot.equals(e.prev.bot) || e.current.equals(e.top)) {
e = e.next;
}
if (!isEdgeHorizontal(e.deltaX)
&& !isEdgeHorizontal(e.prev.deltaX)) {
break;
}
while (isEdgeHorizontal(e.prev.deltaX)) {
e = e.prev;
}
e2 = e;
while (isEdgeHorizontal(e.deltaX)) {
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;
}
go = false;
}
return e;
}
/**
* modified to be compatible with double
*/
public DoublePoint getBot() {
return bot;
}
/**
* modified to be compatible with double
*/
public DoublePoint getCurrent() {
return current;
}
/**
* modified to be compatible with double
*/
public DoublePoint getDelta() {
return delta;
}
/**
* modified to be compatible with double
*/
public DoublePoint getTop() {
return top;
}
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 boolean isContributing(PolyFillType clipFillType,
PolyFillType subjFillType, ClipType clipType) {
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;
}
}
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;
}
}
return true;
}
return true;
}
public boolean isEvenOddAltFillType(PolyFillType clipFillType,
PolyFillType subjFillType) {
if (polyTyp == PolyType.SUBJECT) {
return clipFillType == PolyFillType.EVEN_ODD;
}
return subjFillType == PolyFillType.EVEN_ODD;
}
public boolean isEvenOddFillType(PolyFillType clipFillType,
PolyFillType subjFillType) {
if (polyTyp == PolyType.SUBJECT) {
return subjFillType == PolyFillType.EVEN_ODD;
}
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.]
// long temp = top.getX();
double temp = top.getX();
top.setX(bot.getX());
bot.setX(temp);
temp = top.getZ();
top.setZ(bot.getZ());
bot.setZ(temp);
}
/**
* modified to be compatible with double
*/
public void setBot(DoublePoint bot) {
this.bot.set(bot);
}
/**
* modified to be compatible with double
*/
public void setCurrent(DoublePoint current) {
this.current.set(current);
}
/**
* modified to be compatible with double
*/
public void setTop(DoublePoint 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 = delta.getX() / delta.getY();
}
}
private static boolean isEdgeHorizontal(double d) {
return MyDouble.exactEqual(d, Edge.HORIZONTAL);
}
}