Stretching Polygon to other Polygon with Java

眉间皱痕 提交于 2019-12-22 09:39:33

问题


My problem is that I have a rectangle presented with a small perspective, and I would like to stretch it back to be presented as a rectangle again.

To represent it visually, I currently have within my image something like the red shape, and I have 4 Points (each corner of this shape). As result I would like to have something like the blue shape, and I already have the Rectangle object for it.

I was wondering if there is a method to copy a polygon and draw it as another polygon stretched. I found something for Android (setPolyToPoly), but I couldn't find something like this for java.

Is there some reference or code sample that performs this operation, or maybe some idea how can I solve this problem?


回答1:


I think I understand what you need: a so-called perspective transformation that can be applied to an image. Java has the built-in AffineTransform, but an affine transform always preserves the "parallelness" of lines, so you cannot use that.

Now if you search the web for "java perspective transformation", you will find lots of options like the JavaFX PerspectiveTransform, the JAI PerspectiveTransform. If you only need to stretch images, you can also use the JHLabs PerspectiveFilter and there are other options as well.




回答2:


Here is some code that will turn stretch a polygon with four points to a rectangle.

public static Rectangle2D polyToRect(Polygon polygon) {
    if (polygon.xpoints.length != 4 || polygon.ypoints.length != 4)
        throw new IllegalArgumentException(
                "More than four points, this cannot be fitted to a rectangle");


    Rectangle2D rect = new Rectangle2D.Double();
    for (int i = 0; i < 4; i++) {
        Point2D point = new Point2D.Double(polygon.xpoints[i],
                polygon.ypoints[i]);
        rect.add(point);
    }
    return rect;
}

public static Polygon rectangleToPolygon(Rectangle2D rect) {
    Polygon poly = new Polygon();
    poly.addPoint((int) rect.getX(), (int) rect.getY());
    poly.addPoint((int) (rect.getX() + rect.getWidth()), (int) rect.getY());
    poly.addPoint((int) (rect.getX() + rect.getWidth()),
            (int) (rect.getY() + rect.getHeight()));
    poly.addPoint((int) rect.getX(), (int) (rect.getY() + rect.getHeight()));
    return poly;
}


public static class drawPolyAndRect extends JPanel {
    Polygon poly = new Polygon();

    public drawPolyAndRect() {
        poly.addPoint(0, 0);
        poly.addPoint(400, 40);
        poly.addPoint(400, 250);
        poly.addPoint(0, 400);

    }

    @Override
    @Transient
    public Dimension getPreferredSize() {
        return new Dimension(1000, 1000);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g.create();
        g2d.setColor(Color.green);
        g2d.fill(poly);
        Composite c = AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
                0.5f);
        g2d.setColor(Color.blue);
        g2d.setComposite(c);
        Rectangle2D polyToRect = polyToRect(poly);
        g2d.fill(polyToRect);

        // displace for drawing
        polyToRect.setFrame(polyToRect.getX() + 100,
                polyToRect.getY() + 100, polyToRect.getWidth(),
                polyToRect.getHeight());

        Polygon polyToRectToPoly = rectangleToPolygon(polyToRect);
        g2d.fill(polyToRectToPoly);

        g2d.dispose();
    }

}

public static void main(String[] args) {
    JFrame frame = new JFrame("Poly to rect");
    frame.getContentPane().add(new drawPolyAndRect());
    frame.pack();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setVisible(true);
}

Essentially this uses how adding points to an empty rectangle2d works, it constructs the smallest possible rectangle containing all four points, stretching the polygon. Check the second static method if you want the returned rectangle as a polygon. Picture:




回答3:


A JavaFX based solution using a PerspectiveTransform as suggested by @lbalazscs answer.

  • Toggle Perspective switches on and off the perspective effect on the content.
  • Morph Perspective smoothly animates a transition between the perspective transformed and non-perspective transformed content.

  

import javafx.animation.*;
import javafx.application.*;
import javafx.beans.value.*;
import javafx.geometry.Pos;
import javafx.scene.*;
import javafx.scene.control.ToggleButton;
import javafx.scene.effect.PerspectiveTransform;
import javafx.scene.image.*;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.*;
import javafx.stage.Stage;
import javafx.util.Duration;

public class PerspectiveMovement extends Application {
  // perspective transformed group width and height.
  private final int W = 280;
  private final int H = 96;

  // upper right and lower right co-ordinates of perspective transformed group.
  private final int URY = 35;
  private final int LRY = 65;

  @Override public void start(Stage stage) {
    final PerspectiveTransform perspectiveTransform = createPerspectiveTransform();

    final Group group = new Group();
    group.setCache(true);
    setContent(group);

    final ToggleButton perspectiveToggle = createToggle(
      group, 
      perspectiveTransform
    );

    VBox layout = new VBox(10);
    layout.setAlignment(Pos.CENTER);
    layout.getChildren().setAll(
      perspectiveToggle,
      createMorph(perspectiveToggle, group, perspectiveTransform),
      group
    );
    layout.setStyle("-fx-padding: 10px; -fx-background-color: rgb(17, 20, 25);");

    stage.setScene(new Scene(layout));
    stage.show();
  }

  private void setContent(Group group) {
    Rectangle rect = new Rectangle(0, 5, W, 80);
    rect.setFill(Color.web("0x3b596d"));

    Text text = new Text();
    text.setX(4.0);
    text.setY(60.0);
    text.setText("A long time ago");
    text.setFill(Color.ALICEBLUE);
    text.setFont(Font.font(null, FontWeight.BOLD, 36));

    Image image = new Image(
      "http://icons.iconarchive.com/icons/danrabbit/elementary/96/Star-icon.png"
    );
    ImageView imageView = new ImageView(image);
    imageView.setX(50);

    group.getChildren().addAll(rect, imageView, text);
  }

  private PerspectiveTransform createPerspectiveTransform() {
    PerspectiveTransform perspectiveTransform = new PerspectiveTransform();

    perspectiveTransform.setUlx(0.0);
    perspectiveTransform.setUly(0.0);
    perspectiveTransform.setUrx(W);
    perspectiveTransform.setUry(URY);
    perspectiveTransform.setLrx(W);
    perspectiveTransform.setLry(LRY);
    perspectiveTransform.setLlx(0.0);
    perspectiveTransform.setLly(H);

    return perspectiveTransform;
  }

  private ToggleButton createToggle(final Group group, final PerspectiveTransform perspectiveTransform) {
    final ToggleButton toggle = new ToggleButton("Toggle Perspective");
    toggle.selectedProperty().addListener(new ChangeListener<Boolean>() {
      @Override public void changed(ObservableValue<? extends Boolean> observable, Boolean wasSelected, Boolean selected) {
        if (selected) {
          perspectiveTransform.setUry(URY);
          perspectiveTransform.setLry(LRY);
          group.setEffect(perspectiveTransform);
        } else {
          group.setEffect(null);
        }
      }
    });

    return toggle;
  }

  private ToggleButton createMorph(final ToggleButton perspectiveToggle, final Group group, final PerspectiveTransform perspectiveTransform) {
    final Timeline distorter = new Timeline(
      new KeyFrame(
        Duration.seconds(0), 
        new KeyValue(perspectiveTransform.uryProperty(), 0,  Interpolator.LINEAR),
        new KeyValue(perspectiveTransform.lryProperty(), H, Interpolator.LINEAR)
      ),
      new KeyFrame(
        Duration.seconds(3), 
        new KeyValue(perspectiveTransform.uryProperty(), URY, Interpolator.LINEAR),
        new KeyValue(perspectiveTransform.lryProperty(), LRY, Interpolator.LINEAR)
      )
    );

    final ToggleButton morphToggle = new ToggleButton("Morph Perspective");
    morphToggle.selectedProperty().addListener(new ChangeListener<Boolean>() {
      @Override public void changed(ObservableValue<? extends Boolean> observable, Boolean wasSelected, Boolean selected) {
        if (!perspectiveToggle.isSelected()) {
          perspectiveToggle.fire();
        }
        if (selected) {
          distorter.setRate(1);
          distorter.play();
        } else {
          distorter.setRate(-1);
          distorter.play();
        }
      }
    });

    return morphToggle;
  }
}



回答4:


I don't know if this could help you but let me give you what i made with what i understand:

import javax.swing.*;
import java.awt.*;

public class PolyToRectangle extends JPanel {
public static final int SPEED = 50; //less = more fast.
private int ax = 0, bx = 800, cx = 800, dx = 0, 
            ay = 0, by = 40, cy = 250, dy = 400;
private Polygon poly;


public PolyToRectangle() {
    setPreferredSize(new Dimension(1200, 720));
    poly = new Polygon(new int[]{ax, bx, cx, dx}, new int[]{ay, by, cy, dy}, 4);
}

@Override
public void paintComponent(Graphics g) {

    Graphics2D g2d = (Graphics2D) g;
    g2d.draw(poly);
    g2d.fill(poly);
}

public void polyToRectangle() throws InterruptedException {
    int flag = 0;
    for (int i = 0; i < 150; i++) {
        flag++;
        poly.addPoint(ax, ay);
        poly.addPoint(bx, (by = flag % 3 == 0 ? --by : by));
        poly.addPoint(cx, cy++);
        poly.addPoint(dx, dy);
        Thread.sleep(SPEED);
        repaint();
    }
}

protected void clear(Graphics g) {
    super.paintComponent(g);
}

public static void main(String[] args) throws InterruptedException {
    Frame frame = new JFrame();
    PolyToRectangle se = new PolyToRectangle();
    frame.add(se);
    frame.pack();
    frame.setVisible(true);
    se.polyToRectangle();

}
}

Ok as you can see this code is more a "PolyToSquare" more than PolyToRectangle, but the main was to show the "effect" of repainting with a Thread.sleep in a for, maybe that's the "visual stretch" you are talking about, note that the numbers of iterations on the for depends on numbers of pixels from point 1 and 2 to "stretch" from polygon to rectangle, this is a "hand made" effect, maybe what @lbalazscs is suggesting is the best solution, hope to be helpful, regards.

EDIT: edited the code to be more clean and to follow in a more specific way your goal (now is more a PolyToRectangle, fixed the bx and cx values).



来源:https://stackoverflow.com/questions/16747951/stretching-polygon-to-other-polygon-with-java

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!