/** 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;
  }
Пример #2
0
  /**
   * 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;
  }
Пример #3
0
 @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);
 }
Пример #4
0
 @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;
 }
Пример #5
0
 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;
 }
Пример #7
0
 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);
   }
 }
Пример #8
0
 @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();
 }
Пример #10
0
 /** 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;
   }
 }
Пример #11
0
 /*     */ public static boolean LEQ(Leq leq, Object x, Object y) /*     */ {
   /* 111 */ return Geom.VertLeq((GLUvertex) x, (GLUvertex) y);
   /*     */ }
Пример #12
0
  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();
  }
Пример #13
0
  /* __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;
  }
Пример #14
0
 public Position getRotatedPosition(Position p) {
   Vector v = Geom.getToVector(p);
   Vector vr = getRotatedVector(v);
   return Geom.endPosition(vr);
 }
Пример #15
0
 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);
 }