/** Constrains a point to the current grid. */ protected Point constrainPoint(Point p) { // constrain to view size Dimension size = getSize(); // p.x = Math.min(size.width, Math.max(1, p.x)); // p.y = Math.min(size.height, Math.max(1, p.y)); p.x = Geom.range(1, size.width, p.x); p.y = Geom.range(1, size.height, p.y); if (fConstrainer != null) { return fConstrainer.constrainPoint(p); } return p; }
/** * Returns a path which is cappedPath at the ends, to prevent it from drawing under the end caps. */ protected BezierPath getCappedPath() { if (cappedPath == null) { cappedPath = path.clone(); if (isClosed()) { cappedPath.setClosed(true); } else { if (cappedPath.size() > 1) { if (get(START_DECORATION) != null) { BezierPath.Node p0 = cappedPath.get(0); BezierPath.Node p1 = cappedPath.get(1); Point2D.Double pp; if ((p0.getMask() & BezierPath.C2_MASK) != 0) { pp = p0.getControlPoint(2); } else if ((p1.getMask() & BezierPath.C1_MASK) != 0) { pp = p1.getControlPoint(1); } else { pp = p1.getControlPoint(0); } double radius = get(START_DECORATION).getDecorationRadius(this); double lineLength = Geom.length(p0.getControlPoint(0), pp); cappedPath.set( 0, 0, Geom.cap(pp, p0.getControlPoint(0), -Math.min(radius, lineLength))); } if (get(END_DECORATION) != null) { BezierPath.Node p0 = cappedPath.get(cappedPath.size() - 1); BezierPath.Node p1 = cappedPath.get(cappedPath.size() - 2); Point2D.Double pp; if ((p0.getMask() & BezierPath.C1_MASK) != 0) { pp = p0.getControlPoint(1); } else if ((p1.getMask() & BezierPath.C2_MASK) != 0) { pp = p1.getControlPoint(2); } else { pp = p1.getControlPoint(0); } double radius = get(END_DECORATION).getDecorationRadius(this); double lineLength = Geom.length(p0.getControlPoint(0), pp); cappedPath.set( cappedPath.size() - 1, 0, Geom.cap(pp, p0.getControlPoint(0), -Math.min(radius, lineLength))); } cappedPath.invalidatePath(); } } } return cappedPath; }
@Override public Geom less(Geom other, Linearizer linearizer, Tolerance accuracy) throws NullPointerException { if (getBounds().relate(other.getBounds(), accuracy) == Relation.DISJOINT) { return this; } return toLineString().less(other, linearizer, accuracy); }
@Override public boolean contains(Point2D.Double p) { double tolerance = Math.max(2f, AttributeKeys.getStrokeTotalWidth(this) / 2d); if (isClosed() || get(FILL_COLOR) != null && get(UNCLOSED_PATH_FILLED)) { if (path.contains(p)) { return true; } double grow = AttributeKeys.getPerpendicularHitGrowth(this) * 2d; GrowStroke gs = new GrowStroke(grow, AttributeKeys.getStrokeTotalWidth(this) * get(STROKE_MITER_LIMIT)); if (gs.createStrokedShape(path).contains(p)) { return true; } else { if (isClosed()) { return false; } } } if (!isClosed()) { if (getCappedPath().outlineContains(p, tolerance)) { return true; } if (get(START_DECORATION) != null) { BezierPath cp = getCappedPath(); Point2D.Double p1 = path.get(0, 0); Point2D.Double p2 = cp.get(0, 0); // FIXME - Check here, if caps path contains the point if (Geom.lineContainsPoint(p1.x, p1.y, p2.x, p2.y, p.x, p.y, tolerance)) { return true; } } if (get(END_DECORATION) != null) { BezierPath cp = getCappedPath(); Point2D.Double p1 = path.get(path.size() - 1, 0); Point2D.Double p2 = cp.get(path.size() - 1, 0); // FIXME - Check here, if caps path contains the point if (Geom.lineContainsPoint(p1.x, p1.y, p2.x, p2.y, p.x, p.y, tolerance)) { return true; } } } return false; }
protected Point2D.Double chop(Figure target, Point2D.Double from) { target = getConnectorTarget(target); Rectangle2D.Double r = target.getBounds(); if (STROKE_COLOR.get(target) != null) { double grow; switch (STROKE_PLACEMENT.get(target)) { case CENTER: default: grow = AttributeKeys.getStrokeTotalWidth(target) / 2d; break; case OUTSIDE: grow = AttributeKeys.getStrokeTotalWidth(target); break; case INSIDE: grow = 0d; break; } Geom.grow(r, grow, grow); } return Geom.angleToPoint(r, Geom.pointToAngle(r, from)); }
public Rectangle2D.Double getDrawingArea() { double strokeTotalWidth = AttributeKeys.getStrokeTotalWidth(this); double width = strokeTotalWidth / 2d; if (STROKE_JOIN.get(this) == BasicStroke.JOIN_MITER) { width *= STROKE_MITER_LIMIT.get(this); } else if (STROKE_CAP.get(this) != BasicStroke.CAP_BUTT) { width += strokeTotalWidth * 2; } width++; Rectangle2D.Double r = getBounds(); Geom.grow(r, width, width); return r; }
public Point2D.Double chop(Point2D.Double p) { if (isClosed()) { double grow = AttributeKeys.getPerpendicularHitGrowth(this); if (grow == 0d) { return path.chop(p); } else { GrowStroke gs = new GrowStroke(grow, AttributeKeys.getStrokeTotalWidth(this) * get(STROKE_MITER_LIMIT)); return Geom.chop(gs.createStrokedShape(path), p); } } else { return path.chop(p); } }
@Override public Rectangle2D.Double getDrawingArea() { if (cachedDrawingArea == null) { Rectangle2D rx = getBounds(); Rectangle2D.Double r = (rx instanceof Rectangle2D.Double) ? (Rectangle2D.Double) rx : new Rectangle2D.Double(rx.getX(), rx.getY(), rx.getWidth(), rx.getHeight()); double g = SVGAttributeKeys.getPerpendicularHitGrowth(this); Geom.grow(r, g, g); if (TRANSFORM.get(this) == null) { cachedDrawingArea = r; } else { cachedDrawingArea = new Rectangle2D.Double(); cachedDrawingArea.setRect(TRANSFORM.get(this).createTransformedShape(r).getBounds2D()); } } return (Rectangle2D.Double) cachedDrawingArea.clone(); }
@Override public Rectangle2D.Double getDrawingArea() { if (cachedDrawingArea == null) { if (get(TRANSFORM) == null) { cachedDrawingArea = path.getBounds2D(); } else { BezierPath p2 = (BezierPath) path.clone(); p2.transform(get(TRANSFORM)); cachedDrawingArea = p2.getBounds2D(); } double strokeTotalWidth = AttributeKeys.getStrokeTotalWidth(this); double width = strokeTotalWidth / 2d; if (get(STROKE_JOIN) == BasicStroke.JOIN_MITER) { width *= get(STROKE_MITER_LIMIT); } else if (get(STROKE_CAP) != BasicStroke.CAP_BUTT) { width += strokeTotalWidth * 2; } Geom.grow(cachedDrawingArea, width, width); } return (Rectangle2D.Double) cachedDrawingArea.clone(); }
/** Gets the drawing area without taking the decorator into account. */ @Override protected Rectangle2D.Double getFigureDrawingArea() { if (getText() == null) { return getBounds(); } else { TextLayout layout = getTextLayout(); Rectangle2D.Double r = new Rectangle2D.Double(origin.x, origin.y, layout.getAdvance(), layout.getAscent()); Rectangle2D lBounds = layout.getBounds(); if (!lBounds.isEmpty() && !Double.isNaN(lBounds.getX())) { r.add( new Rectangle2D.Double( lBounds.getX() + origin.x, (lBounds.getY() + origin.y + layout.getAscent()), lBounds.getWidth(), lBounds.getHeight())); } // grow by two pixels to take antialiasing into account Geom.grow(r, 2d, 2d); return r; } }
/* */ public static boolean LEQ(Leq leq, Object x, Object y) /* */ { /* 111 */ return Geom.VertLeq((GLUvertex) x, (GLUvertex) y); /* */ }
public void lineout(ConnectionFigure figure) { BezierPath path = ((LineConnectionFigure) figure).getBezierPath(); Connector start = figure.getStartConnector(); Connector end = figure.getEndConnector(); if (start == null || end == null || path == null) { return; } // Special treatment if the connection connects the same figure if (figure.getStartFigure() == figure.getEndFigure()) { // Ensure path has exactly 4 nodes while (path.size() < 4) { path.add(1, new BezierPath.Node(0, 0)); } while (path.size() > 4) { path.remove(1); } Point2D.Double sp = start.findStart(figure); Point2D.Double ep = end.findEnd(figure); Rectangle2D.Double sb = start.getBounds(); Rectangle2D.Double eb = end.getBounds(); int soutcode = sb.outcode(sp); if (soutcode == 0) { soutcode = Geom.outcode(sb, eb); } int eoutcode = eb.outcode(ep); if (eoutcode == 0) { eoutcode = Geom.outcode(sb, eb); } path.get(0).moveTo(sp); path.get(path.size() - 1).moveTo(ep); switch (soutcode) { case Geom.OUT_TOP: eoutcode = Geom.OUT_LEFT; break; case Geom.OUT_RIGHT: eoutcode = Geom.OUT_TOP; break; case Geom.OUT_BOTTOM: eoutcode = Geom.OUT_RIGHT; break; case Geom.OUT_LEFT: eoutcode = Geom.OUT_BOTTOM; break; default: eoutcode = Geom.OUT_TOP; soutcode = Geom.OUT_RIGHT; break; } // path.get(0).moveTo(sp.x + shoulderSize, sp.y); path.get(0).mask = BezierPath.C2_MASK; if ((soutcode & Geom.OUT_RIGHT) != 0) { path.get(0).x[2] = sp.x + shoulderSize; path.get(0).y[2] = sp.y; } else if ((soutcode & Geom.OUT_LEFT) != 0) { path.get(0).x[2] = sp.x - shoulderSize; path.get(0).y[2] = sp.y; } else if ((soutcode & Geom.OUT_BOTTOM) != 0) { path.get(0).x[2] = sp.x; path.get(0).y[2] = sp.y + shoulderSize; } else { path.get(0).x[2] = sp.x; path.get(0).y[2] = sp.y - shoulderSize; } path.get(1).mask = BezierPath.C2_MASK; path.get(1).moveTo(sp.x + shoulderSize, (sp.y + ep.y) / 2); path.get(1).x[2] = sp.x + shoulderSize; path.get(1).y[2] = ep.y - shoulderSize; path.get(2).mask = BezierPath.C1_MASK; path.get(2).moveTo((sp.x + ep.x) / 2, ep.y - shoulderSize); path.get(2).x[1] = sp.x + shoulderSize; path.get(2).y[1] = ep.y - shoulderSize; path.get(3).mask = BezierPath.C1_MASK; if ((eoutcode & Geom.OUT_RIGHT) != 0) { path.get(3).x[1] = ep.x + shoulderSize; path.get(3).y[1] = ep.y; } else if ((eoutcode & Geom.OUT_LEFT) != 0) { path.get(3).x[1] = ep.x - shoulderSize; path.get(3).y[1] = ep.y; } else if ((eoutcode & Geom.OUT_BOTTOM) != 0) { path.get(3).x[1] = ep.x; path.get(3).y[1] = ep.y + shoulderSize; } else { path.get(3).x[1] = ep.x; path.get(3).y[1] = ep.y - shoulderSize; } } else { Point2D.Double sp = start.findStart(figure); Point2D.Double ep = end.findEnd(figure); path.clear(); if (sp.x == ep.x || sp.y == ep.y) { path.add(new BezierPath.Node(ep.x, ep.y)); } else { Rectangle2D.Double sb = start.getBounds(); sb.x += 5d; sb.y += 5d; sb.width -= 10d; sb.height -= 10d; Rectangle2D.Double eb = end.getBounds(); eb.x += 5d; eb.y += 5d; eb.width -= 10d; eb.height -= 10d; int soutcode = sb.outcode(sp); if (soutcode == 0) { soutcode = Geom.outcode(sb, eb); } int eoutcode = eb.outcode(ep); if (eoutcode == 0) { eoutcode = Geom.outcode(eb, sb); } if ((soutcode & (Geom.OUT_TOP | Geom.OUT_BOTTOM)) != 0 && (eoutcode & (Geom.OUT_TOP | Geom.OUT_BOTTOM)) != 0) { path.add( new BezierPath.Node( BezierPath.C2_MASK, sp.x, sp.y, sp.x, sp.y, sp.x, (sp.y + ep.y) / 2)); path.add( new BezierPath.Node( BezierPath.C1_MASK, ep.x, ep.y, ep.x, (sp.y + ep.y) / 2, ep.x, ep.y)); } else if ((soutcode & (Geom.OUT_LEFT | Geom.OUT_RIGHT)) != 0 && (eoutcode & (Geom.OUT_LEFT | Geom.OUT_RIGHT)) != 0) { path.add( new BezierPath.Node( BezierPath.C2_MASK, sp.x, sp.y, sp.x, sp.y, (sp.x + ep.x) / 2, sp.y)); path.add( new BezierPath.Node( BezierPath.C1_MASK, ep.x, ep.y, (sp.x + ep.x) / 2, ep.y, ep.x, ep.y)); } else if (soutcode == Geom.OUT_BOTTOM || soutcode == Geom.OUT_TOP) { path.add(new BezierPath.Node(BezierPath.C2_MASK, sp.x, sp.y, sp.x, sp.y, sp.x, ep.y)); path.add(new BezierPath.Node(ep.x, ep.y)); } else { path.add(new BezierPath.Node(BezierPath.C2_MASK, sp.x, sp.y, sp.x, sp.y, ep.x, sp.y)); path.add(new BezierPath.Node(ep.x, ep.y)); } } } path.invalidatePath(); }
/* __gl_meshTessellateMonoRegion( face ) tessellates a monotone region * (what else would it do??) The region must consist of a single * loop of half-edges (see mesh.h) oriented CCW. "Monotone" in this * case means that any vertical line intersects the interior of the * region in a single interval. * * Tessellation consists of adding interior edges (actually pairs of * half-edges), to split the region into non-overlapping triangles. * * The basic idea is explained in Preparata and Shamos (which I don''t * have handy right now), although their implementation is more * complicated than this one. The are two edge chains, an upper chain * and a lower chain. We process all vertices from both chains in order, * from right to left. * * The algorithm ensures that the following invariant holds after each * vertex is processed: the untessellated region consists of two * chains, where one chain (say the upper) is a single edge, and * the other chain is concave. The left vertex of the single edge * is always to the left of all vertices in the concave chain. * * Each step consists of adding the rightmost unprocessed vertex to one * of the two chains, and forming a fan of triangles from the rightmost * of two chain endpoints. Determining whether we can add each triangle * to the fan is a simple orientation test. By making the fan as large * as possible, we restore the invariant (check it yourself). */ static boolean __gl_meshTessellateMonoRegion(GLUface face, boolean avoidDegenerateTris) { GLUhalfEdge up, lo; /* All edges are oriented CCW around the boundary of the region. * First, find the half-edge whose origin vertex is rightmost. * Since the sweep goes from left to right, face->anEdge should * be close to the edge we want. */ up = face.anEdge; assert (up.Lnext != up && up.Lnext.Lnext != up); for (; Geom.VertLeq(up.Sym.Org, up.Org); up = up.Onext.Sym) ; for (; Geom.VertLeq(up.Org, up.Sym.Org); up = up.Lnext) ; lo = up.Onext.Sym; boolean mustConnect = false; // hack for avoidDegenerateTris while (up.Lnext != lo) { if (avoidDegenerateTris && !mustConnect) { // Skip over regions where several vertices are collinear, // to try to avoid producing degenerate (zero-area) triangles // // The "mustConnect" flag is a hack to try to avoid // skipping too large regions and causing incorrect // triangulations. This entire modification is overall // not robust and needs more work if (Geom.EdgeCos(lo.Lnext.Org, lo.Org, lo.Lnext.Lnext.Org) <= -Geom.ONE_MINUS_EPSILON) { // Lines around lo do { lo = lo.Onext.Sym; mustConnect = true; } while (up.Lnext != lo && Geom.EdgeCos(lo.Lnext.Org, lo.Org, lo.Lnext.Lnext.Org) <= -Geom.ONE_MINUS_EPSILON); } else if (Geom.EdgeCos(up.Onext.Sym.Org, up.Org, up.Onext.Sym.Onext.Sym.Org) <= -Geom.ONE_MINUS_EPSILON) { // Lines around up do { up = up.Lnext; mustConnect = true; } while (up.Lnext != lo && Geom.EdgeCos(up.Onext.Sym.Org, up.Org, up.Onext.Sym.Onext.Sym.Org) <= -Geom.ONE_MINUS_EPSILON); } if (up.Lnext == lo) break; } if (Geom.VertLeq(up.Sym.Org, lo.Org)) { /* up.Sym.Org is on the left. It is safe to form triangles from lo.Org. * The EdgeGoesLeft test guarantees progress even when some triangles * are CW, given that the upper and lower chains are truly monotone. */ while (lo.Lnext != up && (Geom.EdgeGoesLeft(lo.Lnext) || Geom.EdgeSign(lo.Org, lo.Sym.Org, lo.Lnext.Sym.Org) <= 0)) { GLUhalfEdge tempHalfEdge = Mesh.__gl_meshConnect(lo.Lnext, lo); mustConnect = false; if (tempHalfEdge == null) return false; lo = tempHalfEdge.Sym; } lo = lo.Onext.Sym; } else { /* lo.Org is on the left. We can make CCW triangles from up.Sym.Org. */ while (lo.Lnext != up && (Geom.EdgeGoesRight(up.Onext.Sym) || Geom.EdgeSign(up.Sym.Org, up.Org, up.Onext.Sym.Org) >= 0)) { GLUhalfEdge tempHalfEdge = Mesh.__gl_meshConnect(up, up.Onext.Sym); mustConnect = false; if (tempHalfEdge == null) return false; up = tempHalfEdge.Sym; } up = up.Lnext; } } /* Now lo.Org == up.Sym.Org == the leftmost vertex. The remaining region * can be tessellated in a fan from this leftmost vertex. */ assert (lo.Lnext != up); while (lo.Lnext.Lnext != up) { GLUhalfEdge tempHalfEdge = Mesh.__gl_meshConnect(lo.Lnext, lo); if (tempHalfEdge == null) return false; lo = tempHalfEdge.Sym; } return true; }
public Position getRotatedPosition(Position p) { Vector v = Geom.getToVector(p); Vector vr = getRotatedVector(v); return Geom.endPosition(vr); }
public Position getRotatedPosition(Position p, Position pcenter) { Vector v = Geom.fromToVector(pcenter, p); Vector vnew = getRotatedVector(v); Translation trans = new GTranslation(vnew); return trans.getTranslated(pcenter); }