/**
   * Compute the position of a sprite if it is not attached.
   *
   * @param sprite The sprite.
   * @param pos Where to stored the computed position, if null, the position is created.
   * @param units The units the computed position must be given into.
   * @return The same instance as pos, or a new one if pos was null.
   */
  protected Point2D.Double getSpritePositionFree(
      GraphicSprite sprite, Point2D.Double pos, Units units) {
    if (pos == null) pos = new Point2D.Double();

    if (sprite.getUnits() == units) {
      pos.x = sprite.getX();
      pos.y = sprite.getY();
    } else if (units == Units.GU && sprite.getUnits() == Units.PX) {
      pos.x = sprite.getX();
      pos.y = sprite.getY();

      xT.transform(pos, pos);
    } else if (units == Units.PX && sprite.getUnits() == Units.GU) {
      pos.x = sprite.getX();
      pos.y = sprite.getY();

      Tx.transform(pos, pos);
    } else if (units == Units.GU && sprite.getUnits() == Units.PERCENTS) {
      pos.x = metrics.lo.x + (sprite.getX() / 100f) * metrics.graphWidthGU();
      pos.y = metrics.lo.y + (sprite.getY() / 100f) * metrics.graphHeightGU();
    } else if (units == Units.PX && sprite.getUnits() == Units.PERCENTS) {
      pos.x = (sprite.getX() / 100f) * metrics.viewport[2];
      pos.y = (sprite.getY() / 100f) * metrics.viewport[3];
    } else {
      throw new RuntimeException("Unhandled yet sprite positioning.");
    }

    return pos;
  }
  /**
   * Is the given sprite visible in the given area.
   *
   * @param sprite The sprite to check.
   * @param X1 The min abscissa of the area.
   * @param Y1 The min ordinate of the area.
   * @param X2 The max abscissa of the area.
   * @param Y2 The max ordinate of the area.
   * @return True if the node lies in the given area.
   */
  protected boolean isSpriteIn(GraphicSprite sprite, double X1, double Y1, double X2, double Y2) {
    if (sprite.isAttachedToNode() && nodeInvisible.contains(sprite.getNodeAttachment().getId())) {
      return false;
    } else if (sprite.isAttachedToEdge() && !isEdgeVisible(sprite.getEdgeAttachment())) {
      return false;
    } else {
      Values size = sprite.getStyle().getSize();
      double w2 = metrics.lengthToPx(size, 0) / 2;
      double h2 = size.size() > 1 ? metrics.lengthToPx(size, 1) / 2 : w2;
      Point2D.Double src = spritePositionPx(sprite); // new Point2D.Double(
      // sprite.getX(),
      // sprite.getY() );

      // Tx.transform( src, src );

      double x1 = src.x - w2;
      double x2 = src.x + w2;
      double y1 = src.y - h2;
      double y2 = src.y + h2;

      if (x2 < X1) return false;
      if (y2 < Y1) return false;
      if (x1 > X2) return false;
      if (y1 > Y2) return false;

      return true;
    }
  }
  /**
   * Check if a sprite contains the given point (x,y).
   *
   * @param elt The sprite.
   * @param x The point abscissa.
   * @param y The point ordinate.
   * @return True if (x,y) is in the given element.
   */
  protected boolean spriteContains(GraphicElement elt, double x, double y) {
    Values size = elt.getStyle().getSize();
    double w2 = metrics.lengthToPx(size, 0) / 2;
    double h2 = size.size() > 1 ? metrics.lengthToPx(size, 1) / 2 : w2;
    Point2D.Double dst = spritePositionPx((GraphicSprite) elt); // new
    // Point2D.Double(
    // elt.getX(),
    // elt.getY()
    // );
    // Point2D.Double dst = new Point2D.Double();

    // Tx.transform( src, dst );
    dst.x -= metrics.viewport[0];
    dst.y -= metrics.viewport[1];

    double x1 = dst.x - w2;
    double x2 = dst.x + w2;
    double y1 = dst.y - h2;
    double y2 = dst.y + h2;

    if (x < x1) return false;
    if (y < y1) return false;
    if (x > x2) return false;
    if (y > y2) return false;

    return true;
  }
  /**
   * Compute the position of a sprite if attached to an edge.
   *
   * @param sprite The sprite.
   * @param pos Where to stored the computed position, if null, the position is created.
   * @param units The units the computed position must be given into.
   * @return The same instance as pos, or a new one if pos was null.
   */
  protected Point2D.Double getSpritePositionEdge(
      GraphicSprite sprite, Point2D.Double pos, Units units) {
    if (pos == null) pos = new Point2D.Double();

    GraphicEdge edge = sprite.getEdgeAttachment();

    if (edge.isCurve()) {
      double ctrl[] = edge.getControlPoints();
      Point2 p0 = new Point2(edge.from.getX(), edge.from.getY());
      Point2 p1 = new Point2(ctrl[0], ctrl[1]);
      Point2 p2 = new Point2(ctrl[1], ctrl[2]);
      Point2 p3 = new Point2(edge.to.getX(), edge.to.getY());
      Vector2 perp = CubicCurve.perpendicular(p0, p1, p2, p3, sprite.getX());
      double y = metrics.lengthToGu(sprite.getY(), sprite.getUnits());

      perp.normalize();
      perp.scalarMult(y);

      pos.x = CubicCurve.eval(p0.x, p1.x, p2.x, p3.x, sprite.getX()) - perp.data[0];
      pos.y = CubicCurve.eval(p0.y, p1.y, p2.y, p3.y, sprite.getX()) - perp.data[1];
    } else {
      double x = ((GraphicNode) edge.getSourceNode()).x;
      double y = ((GraphicNode) edge.getSourceNode()).y;
      double dx = ((GraphicNode) edge.getTargetNode()).x - x;
      double dy = ((GraphicNode) edge.getTargetNode()).y - y;
      double d = sprite.getX(); // Percent on the edge.
      double o = metrics.lengthToGu(sprite.getY(), sprite.getUnits());
      // Offset from the position given by percent, perpendicular to the
      // edge.

      d = d > 1 ? 1 : d;
      d = d < 0 ? 0 : d;

      x += dx * d;
      y += dy * d;

      d = (double) Math.sqrt(dx * dx + dy * dy);
      dx /= d;
      dy /= d;

      x += -dy * o;
      y += dx * o;

      pos.x = x;
      pos.y = y;

      if (units == Units.PX) {
        Tx.transform(pos, pos);
      }
    }

    return pos;
  }
  /**
   * Is the given node visible in the given area.
   *
   * @param node The node to check.
   * @param X1 The min abscissa of the area.
   * @param Y1 The min ordinate of the area.
   * @param X2 The max abscissa of the area.
   * @param Y2 The max ordinate of the area.
   * @return True if the node lies in the given area.
   */
  protected boolean isNodeIn(GraphicNode node, double X1, double Y1, double X2, double Y2) {
    Values size = node.getStyle().getSize();
    double w2 = metrics.lengthToPx(size, 0) / 2;
    double h2 = size.size() > 1 ? metrics.lengthToPx(size, 1) / 2 : w2;
    Point2D.Double src = new Point2D.Double(node.getX(), node.getY());
    boolean vis = true;

    Tx.transform(src, src);

    double x1 = src.x - w2;
    double x2 = src.x + w2;
    double y1 = src.y - h2;
    double y2 = src.y + h2;

    if (x2 < X1) vis = false;
    else if (y2 < Y1) vis = false;
    else if (x1 > X2) vis = false;
    else if (y1 > Y2) vis = false;

    return vis;
  }
  /**
   * Compute a transformation matrix that pass from graph units (user space) to pixel units (device
   * space) so that the whole graph is visible.
   *
   * @param g2 The Swing graphics.
   */
  protected void autoFitView(Graphics2D g2) {
    double sx, sy;
    double tx, ty;
    double padXgu = getPaddingXgu() * 2;
    double padYgu = getPaddingYgu() * 2;
    double padXpx = getPaddingXpx() * 2;
    double padYpx = getPaddingYpx() * 2;

    sx = (metrics.viewport[2] - padXpx) / (metrics.size.data[0] + padXgu); // Ratio
    // along
    // X
    sy = (metrics.viewport[3] - padYpx) / (metrics.size.data[1] + padYgu); // Ratio
    // along
    // Y
    tx = metrics.lo.x + (metrics.size.data[0] / 2); // Centre of graph in X
    ty = metrics.lo.y + (metrics.size.data[1] / 2); // Centre of graph in Y

    if (sx <= 0) {
      sx =
          (metrics.viewport[2] - Math.min(metrics.viewport[2] - 1, padXpx))
              / (metrics.size.data[0] + padXgu);
    }

    if (sy <= 0) {
      sy =
          (metrics.viewport[3] - Math.min(metrics.viewport[3] - 1, padYpx))
              / (metrics.size.data[1] + padYgu);
    }

    if (sx > sy) // The least ratio.
    sx = sy;
    else sy = sx;

    g2.translate(metrics.viewport[2] / 2, metrics.viewport[3] / 2);
    if (rotation != 0) g2.rotate(rotation / (180 / Math.PI));
    g2.scale(sx, -sy);
    g2.translate(-tx, -ty);

    Tx = g2.getTransform();
    xT = new AffineTransform(Tx);
    try {
      xT.invert();
    } catch (NoninvertibleTransformException e) {
      logger.warning("Cannot inverse gu2px matrix.");
    }

    zoom = 1;

    center.set(tx, ty, 0);
    metrics.setRatioPx2Gu(sx);
    metrics.loVisible.copy(metrics.lo);
    metrics.hiVisible.copy(metrics.hi);
  }
  /**
   * Compute a transformation that pass from graph units (user space) to a pixel units (device
   * space) so that the view (zoom and centre) requested by the user is produced.
   *
   * @param g2 The Swing graphics.
   */
  protected void userView(Graphics2D g2) {
    double sx, sy;
    double tx, ty;
    double padXgu = getPaddingXgu() * 2;
    double padYgu = getPaddingYgu() * 2;
    double padXpx = getPaddingXpx() * 2;
    double padYpx = getPaddingYpx() * 2;
    double gw = gviewport != null ? gviewport[2] - gviewport[0] : metrics.size.data[0];
    double gh = gviewport != null ? gviewport[3] - gviewport[1] : metrics.size.data[1];

    sx = (metrics.viewport[2] - padXpx) / ((gw + padXgu) * zoom);
    sy = (metrics.viewport[3] - padYpx) / ((gh + padYgu) * zoom);
    tx = center.x;
    ty = center.y;

    if (sx > sy) // The least ratio.
    sx = sy;
    else sy = sx;

    g2.translate((metrics.viewport[2] / 2), (metrics.viewport[3] / 2));
    if (rotation != 0) g2.rotate(rotation / (180 / Math.PI));
    g2.scale(sx, -sy);
    g2.translate(-tx, -ty);

    Tx = g2.getTransform();
    xT = new AffineTransform(Tx);
    try {
      xT.invert();
    } catch (NoninvertibleTransformException e) {
      logger.log(Level.WARNING, "Cannot inverse gu2px matrix.", e);
    }

    metrics.setRatioPx2Gu(sx);

    double w2 = (metrics.viewport[2] / sx) / 2;
    double h2 = (metrics.viewport[3] / sx) / 2;

    metrics.loVisible.set(center.x - w2, center.y - h2);
    metrics.hiVisible.set(center.x + w2, center.y + h2);
  }
 /**
  * Set the output view port size in pixels.
  *
  * @param viewportWidth The width in pixels of the view port.
  * @param viewportHeight The width in pixels of the view port.
  */
 public void setViewport(
     double viewportX, double viewportY, double viewportWidth, double viewportHeight) {
   metrics.setViewport(viewportX, viewportY, viewportWidth, viewportHeight);
 }
 /*
  * (non-Javadoc)
  *
  * @see org.graphstream.ui.swingViewer.util.Camera#setBounds(double, double,
  * double, double, double, double)
  */
 public void setBounds(
     double minx, double miny, double minz, double maxx, double maxy, double maxz) {
   metrics.setBounds(minx, miny, minz, maxx, maxy, maxz);
 }