예제 #1
0
  /**
   * Creates DisplacementMap effect for the bend
   *
   * @param node target node
   * @param path path that is used to draw the opposite side of a bend Its fill is updated with
   *     linear gradient.
   * @param shadow path that is used to draw the shadow of a bend Its fill is updated with linear
   *     gradient.
   * @param clip path that is used to clip the content of the page either for mouse operations or
   *     for visuals
   */
  public BookBend(final Node node, Path path, Path shadow, Path clip) {
    this.node = node;
    this.p = path;
    this.shadow = shadow;
    this.clip = clip;

    node.setEffect(new DisplacementMap(map));
    node.layoutBoundsProperty()
        .addListener(
            new InvalidationListener() {

              @Override
              public void invalidated(Observable arg0) {
                newWidth = (int) Math.round(node.getLayoutBounds().getWidth());
                newHeight = (int) Math.round(node.getLayoutBounds().getHeight());
                if (newWidth != map.getWidth() || newHeight != map.getHeight()) {
                  setUpdateNeeded(true);
                }
              }
            });
    node.sceneProperty()
        .addListener(
            new ChangeListener<Scene>() {

              @Override
              public void changed(
                  ObservableValue<? extends Scene> ov, Scene oldValue, Scene newValue) {
                if (newValue == null) {
                  stopAnimationTimer();
                }
              }
            });
    newWidth = (int) Math.round(node.getLayoutBounds().getWidth());
    newHeight = (int) Math.round(node.getLayoutBounds().getHeight());
  }
예제 #2
0
  private void init(double firingRadius, double templateWidth) {
    double x0 = templateWidth / 2.0;
    double y0 = -templateWidth / 2.0;

    double x5 = templateWidth / 2.0;
    double y5 = templateWidth / 2.0;

    double x1 = x0 + firingRadius * Math.cos(45.0 / 180.0 * Math.PI);
    double y1 = y0 - firingRadius * Math.sin(45.0 / 180.0 * Math.PI);

    double x2 = x0 + firingRadius;
    double y2 = y0;

    double x3 = x5 + firingRadius;
    double y3 = y5;

    double x4 = x5 + firingRadius * Math.cos(45.0 / 180.0 * Math.PI);
    double y4 = y5 + firingRadius * Math.sin(45.0 / 180.0 * Math.PI);

    this.getElements().add(new MoveTo(x0, y0));
    this.getElements().add(new LineTo(x1, y1));
    this.getElements().add(new ArcTo(firingRadius, firingRadius, 45, x2, y2, false, true));
    this.getElements().add(new LineTo(x3, y3));
    this.getElements().add(new ArcTo(firingRadius, firingRadius, 45, x4, y4, false, true));
    this.getElements().add(new LineTo(x5, y5));
    this.getElements().add(new LineTo(x0, y0));
    this.setFill(Color.rgb(255, 0, 0, 0.35));
    this.setStroke(Color.rgb(255, 0, 0, 0.5));
    this.setFillRule(FillRule.NON_ZERO);
  }
예제 #3
0
 private static double hypot(Point2D p1, Point2D p2) {
   return Math.hypot(p1.getX() - p2.getX(), p1.getY() - p2.getY());
 }
예제 #4
0
  /** Updates DisplacementMap and path for current coordinates. */
  public void update() {
    setUpdateNeeded(false);

    if (newWidth == map.getWidth()
        && newHeight == map.getHeight()
        && targetX == oldTargetX
        && targetY == oldTargetY) {
      return;
    }
    oldTargetX = targetX;
    oldTargetY = targetY;
    if (newWidth != map.getWidth() || newHeight != map.getHeight()) {
      map.setWidth(newWidth);
      map.setHeight(newHeight);
    }

    final double W = node.getLayoutBounds().getWidth();
    final double H = node.getLayoutBounds().getHeight();

    // target point F for folded corner
    final double xF = Math.min(targetX, W - 1);
    final double yF = Math.min(targetY, H - 1);

    final Point2D F = new Point2D(xF, yF);

    // corner point O
    final double xO = W;
    final double yO = H;

    // distance between them
    final double FO = Math.hypot(xF - xO, yF - yO);
    final double AF = FO / 2;

    final double AC = Math.min(AF * 0.5, 200);

    // radius of the fold as seen along the l2 line
    final double R = AC / Math.PI * 1.5;
    final double BC = R;
    final double flat_R = AC;

    // Gradient for the line from target point to corner point
    final double K = (yO - yF) / (xO - xF);

    // angle of a line l1
    final double ANGLE = Math.atan(1 / K);

    // point A (on line l1 - the mirror line of target and corner points)
    final double xA = (xO + xF) / 2;
    final double yA = (yO + yF) / 2;

    // end points of line l1
    final double bottomX = xA - (H - yA) * K;
    final double bottomY = H;
    final double rightX = W;
    final double rightY = yA - (W - xA) / K;

    final Point2D RL1 = new Point2D(rightX, rightY);
    final Point2D BL1 = new Point2D(bottomX, bottomY);

    // point C (on line l2 - the line when distortion begins)
    final double kC = AC / AF;
    final double xC = xA - (xA - xF) * kC;
    final double yC = yA - (yA - yF) * kC;

    final Point2D C = new Point2D(xC, yC);

    final Point2D RL2 = new Point2D(W, yC - (W - xC) / K);
    final Point2D BL2 = new Point2D(xC - (H - yC) * K, H);

    // point B (on line l3 - the line where distortion ends)
    final double kB = BC / AC;
    final double xB = xC + (xA - xC) * kB;
    final double yB = yC + (yA - yC) * kB;

    // Bottom ellipse calculations
    final Point2D BP1 = calcIntersection(F, BL1, BL2, C);
    final Point2D BP3 = BL2;
    final Point2D BP2 = middle(BP1, BP3, 0.5);
    final Point2D BP4 = new Point2D(xB + BP2.getX() - xC, yB + BP2.getY() - yC);

    final double bE_x1 = hypot(BP2, BP3);
    final double bE_y2 = -hypot(BP2, BP4);
    final double bE_yc = -hypot(BP2, BL1);
    final double bE_y0 = bE_y2 * bE_y2 / (2 * bE_y2 - bE_yc);
    final double bE_b = bE_y0 - bE_y2;
    final double bE_a = Math.sqrt(-bE_x1 * bE_x1 / bE_y0 * bE_b * bE_b / bE_yc);

    // Right ellipse calculations
    final Point2D RP1 = calcIntersection(F, RL1, RL2, C);
    final Point2D RP3 = RL2;
    final Point2D RP2 = middle(RP1, RP3, 0.5);
    final Point2D RP4 = new Point2D(xB + RP2.getX() - xC, yB + RP2.getY() - yC);

    final double rE_x1 = hypot(RP2, RP3);
    final double rE_y2 = -hypot(RP2, RP4);
    final double rE_yc = -hypot(RP2, RL1);
    final double rE_y0 = rE_y2 * rE_y2 / (2 * rE_y2 - rE_yc);
    final double rE_b = rE_y0 - rE_y2;
    final double rE_a = Math.sqrt(-rE_x1 * rE_x1 / rE_y0 * rE_b * rE_b / rE_yc);

    p.setFill(
        new LinearGradient(
            xF,
            yF,
            xO,
            yO,
            false,
            CycleMethod.NO_CYCLE,
            new Stop(0, pathColor),
            new Stop((xC - xF) / (xO - xF), bendStartColor),
            new Stop((xB - xF) / (xO - xF), bendEndColor)));

    p.getElements()
        .setAll(
            new MoveTo(BP4.getX(), BP4.getY()),
            ArcToBuilder.create()
                .XAxisRotation(Math.toDegrees(-ANGLE))
                .radiusX(bE_a)
                .radiusY(bE_b)
                .x(BP1.getX())
                .y(BP1.getY())
                .build(),
            new LineTo(xF, yF),
            new LineTo(RP1.getX(), RP1.getY()),
            ArcToBuilder.create()
                .XAxisRotation(Math.toDegrees(-ANGLE))
                .radiusX(rE_a)
                .radiusY(rE_b)
                .x(RP4.getX())
                .y(RP4.getY())
                .build(),
            new ClosePath());

    if (shadow != null) {
      double level0 = (xB - xF) / (xO - xF) - R / FO * 0.5;
      double level1 = (xB - xF) / (xO - xF) + (0.3 + (200 - AC) / 200) * R / FO;
      shadow.setFill(
          new LinearGradient(
              xF,
              yF,
              xO,
              yO,
              false,
              CycleMethod.NO_CYCLE,
              new Stop(level0, Color.rgb(0, 0, 0, 0.7)),
              new Stop(level0 * 0.3 + level1 * 0.7, Color.rgb(0, 0, 0, 0.25)),
              new Stop(level1, Color.rgb(0, 0, 0, 0.0)),
              new Stop(1, Color.rgb(0, 0, 0, 0))));

      shadow
          .getElements()
          .setAll(
              new MoveTo(RP3.getX(), RP3.getY()),
              ArcToBuilder.create()
                  .XAxisRotation(Math.toDegrees(-ANGLE))
                  .radiusX(rE_a)
                  .radiusY(rE_b)
                  .x(RP4.getX())
                  .y(RP4.getY())
                  .sweepFlag(true)
                  .build(),
              new LineTo(BP4.getX(), BP4.getY()),
              ArcToBuilder.create()
                  .XAxisRotation(Math.toDegrees(-ANGLE))
                  .radiusX(bE_a)
                  .radiusY(bE_b)
                  .x(BP3.getX())
                  .y(BP3.getY())
                  .sweepFlag(true)
                  .build(),
              new LineTo(xO, yO),
              new ClosePath());
    }

    if (clip != null) {
      final Point2D RL3 = new Point2D(W, yB - (W - xB) / K);
      final Point2D BL3 = new Point2D(xB - (H - yB) * K, H);

      clip.getElements()
          .setAll(
              new MoveTo(0, 0),
              RL3.getY() > 0 ? new LineTo(W, 0) : new LineTo(0, 0),
              RL3.getY() >= 0
                  ? new LineTo(RL3.getX(), RL3.getY())
                  : new LineTo(xB - (0 - yB) * K, 0),
              BL3.getX() >= 0
                  ? new LineTo(BL3.getX(), BL3.getY())
                  : new LineTo(0, yB - (0 - xB) / K),
              BL3.getX() > 0 ? new LineTo(0, H) : new LineTo(0, 0),
              new ClosePath());
    }

    final double K2 = -K;
    final double C2 = BP3.getX() - K2 * H;

    final double K3 = -K;
    final double C3 = xB - K3 * yB;

    final double STEP = Math.max(0.1, R / (buffer.length - 1));
    final double HYPOT = Math.hypot(1, K);
    final double yR = 1.5 * R;
    double x_1 = 0, y_1 = 0, cur_len = 0;
    for (double len = 0; len <= R; len += STEP) {
      final int index = (int) Math.round(len / STEP);
      final double angle = Math.asin(len / R);
      final double y = yR * Math.cos(angle);
      if (len > 0) {
        cur_len += Math.hypot(y - y_1, len - x_1);
      }
      buffer[index][0] = (float) angle;
      buffer[index][1] = (float) (cur_len * flat_R);
      x_1 = len;
      y_1 = y;
    }
    double total_len = cur_len;
    for (double len = 0; len <= R; len += STEP) {
      final int index = (int) Math.round(len / STEP);
      final double flat_len = buffer[index][1] / total_len;
      final double delta_len = flat_len - len;
      final double xs = delta_len / HYPOT;
      final double ys = K * delta_len / HYPOT;
      buffer[index][0] = (float) (xs / W);
      buffer[index][1] = (float) (ys / H);
    }

    for (int y = 0; y < map.getHeight(); y++) {
      final double lx2 = K2 * (y + 0.5) + C2;
      final double lx3 = K3 * (y + 0.5) + C3;
      for (int x = 0; x < map.getWidth(); x++) {
        if (x + 0.5 < lx2) {
          map.setSamples(x, y, 0, 0);
        } else if (x + 0.5 >= lx3 - 1) {
          map.setSamples(x, y, 1, 0);
        } else {
          final double len = Math.abs((x + 0.5) - K2 * (y + 0.5) - C2) / HYPOT;
          final int index = (int) Math.round(len / STEP);
          map.setSamples(x, y, buffer[index][0], buffer[index][1]);
        }
      }
    }
  }
예제 #5
0
 public double[] rotationCoordinates(double[] old, double angle) {
   return new double[] {
     (old[0] * Math.cos(Math.toRadians(angle)) - old[1] * Math.sin(Math.toRadians(angle))),
     (old[0] * Math.sin(Math.toRadians(angle)) + old[1] * Math.cos(Math.toRadians(angle)))
   };
 }