1 回答
TA贡献1811条经验 获得超4个赞
这至少与Texture deforming密切相关,4分,但我不会说它可以被认为是重复的。
你在那里做了很多数学工作。但也许这不是必需的。使用正确的方法,问题本身是相当微不足道的。考虑二维仿射变换的含义:它将一个空间变换到另一个空间。这里的关键点是:
矩阵列是将矩阵应用于单位向量的结果
现在,当你有 3 个点时,你可以从它们计算向量:
double dx1 = p1.getX() - p0.getX();
double dy1 = p1.getY() - p0.getY();
double dx2 = p2.getX() - p0.getX();
double dy2 = p2.getY() - p0.getY();
然后您可以简单地将这些值插入AffineTransform
. 最后一列AffineTransform
包含由 给出的翻译p0
。结果是AffineTransform
将点 (0,0)、(1,0) 和 (0,1) 分别转换为点p0
、p1
和p2
。当您反转此变换时,它会将点p0
、p1
和转换p2
为点 (0,0)、(1,0) 和 (0,1)。
所以你所要做的就是
创建将源点转换为单位向量的变换
创建将单位向量转换为目标点的变换
将两者连接起来
伪代码 (!) 真的很简单
AffineTransform unitToSrc = computeTransform(src[0], src[1], src[2]);
AffineTransform unitToDst = computeTransform(dst[0], dst[1], dst[2]);
AffineTransform at = new AffineTransform();
at.concatenate(unitToDst);
at.concatenate(unitToSrc.inverted());
整个事情都在这里实现,作为 MCVE。红色点是“源”点,绿色点是“目的地”点。你可以用鼠标拖动它们:
蓝色圆圈表示将变换应用于源点的结果,您可以看到它们最终到达了所需的目标位置。
实际计算是通过computeTransform方法完成的。请注意,这是基于java.awt.geom.Point2D类(而不是Vector2d您省略的类)实现的,但这应该很容易更改:点或矢量类唯一使用的是 x/y 坐标。除此之外,实现中根本不涉及(自定义)数学。唯一的数学是反转仿射变换,但有一个内置的功能。
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import java.util.Arrays;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class AffineTransformFromPoints
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(() -> createAndShowGUI());
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
AffineTransformFromPointsPanel panel =
new AffineTransformFromPointsPanel();
f.getContentPane().setLayout(new BorderLayout());
f.getContentPane().add(panel, BorderLayout.CENTER);
f.setSize(1200,900);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class AffineTransformFromPointsPanel extends JPanel
implements MouseListener, MouseMotionListener
{
private Point2D draggedPoint;
// the position of the points before the transformation
Point2D[] src = new Point2D[] {
new Point2D.Double(486, 191),
new Point2D.Double(456, 565),
new Point2D.Double(149, 353)
};
// the position of the points after the transformation
Point2D[] dst = new Point2D[] {
new Point2D.Double(0, 0),
new Point2D.Double(0, 600),
new Point2D.Double(600, 600)
};
public AffineTransformFromPointsPanel()
{
addMouseListener(this);
addMouseMotionListener(this);
}
@Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
g.setColor(Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(Color.RED);
for (Point2D v : src)
{
paint(g, v);
}
g.setColor(Color.GREEN);
for (Point2D v : dst)
{
paint(g, v);
}
g.setColor(Color.BLUE);
AffineTransform at = computeTransform(src, dst);
for (Point2D v : src)
{
draw(g, v, at);
}
}
private static AffineTransform computeTransform(
Point2D src[], Point2D dst[])
{
AffineTransform unitToSrc = computeTransform(src[0], src[1], src[2]);
AffineTransform unitToDst = computeTransform(dst[0], dst[1], dst[2]);
AffineTransform srcToUnit = null;
try
{
srcToUnit = unitToSrc.createInverse();
}
catch (NoninvertibleTransformException e)
{
System.out.println(e.getMessage());
return new AffineTransform();
}
AffineTransform at = new AffineTransform();
at.concatenate(unitToDst);
at.concatenate(srcToUnit);
return at;
}
private static AffineTransform computeTransform(
Point2D p0, Point2D p1, Point2D p2)
{
AffineTransform at = new AffineTransform();
double dx1 = p1.getX() - p0.getX();
double dy1 = p1.getY() - p0.getY();
double dx2 = p2.getX() - p0.getX();
double dy2 = p2.getY() - p0.getY();
at.setTransform(dx1, dy1, dx2, dy2, p0.getX(), p0.getY());
return at;
}
private static void paint(Graphics2D g, Point2D p)
{
double r = 6;
g.fill(new Ellipse2D.Double(
p.getX() - r, p.getY() - r, r + r, r + r));
}
private static void draw(Graphics2D g, Point2D v, AffineTransform at)
{
double r = 8;
Point2D p = new Point2D.Double(v.getX(), v.getY());
at.transform(p, p);
g.draw(new Ellipse2D.Double(
p.getX() - r, p.getY() - r, r + r, r + r));
}
@Override
public void mouseDragged(MouseEvent e)
{
if (draggedPoint != null)
{
draggedPoint.setLocation(e.getPoint());
repaint();
}
}
@Override
public void mousePressed(MouseEvent e)
{
draggedPoint = closest(e.getPoint(), Arrays.asList(src));
if (draggedPoint == null)
{
draggedPoint = closest(e.getPoint(), Arrays.asList(dst));
}
}
private static Point2D closest(
Point2D p, Iterable<? extends Point2D> points)
{
final double threshold = 10;
Point2D closestPoint = null;
double minDistance = Double.MAX_VALUE;
for (Point2D point : points)
{
double dd = point.distance(p);
if (dd < threshold && dd < minDistance)
{
minDistance = dd;
closestPoint = point;
}
}
return closestPoint;
}
@Override
public void mouseReleased(MouseEvent e)
{
draggedPoint = null;
}
@Override
public void mouseMoved(MouseEvent e)
{
// Nothing to do here
}
@Override
public void mouseClicked(MouseEvent e)
{
// Nothing to do here
}
@Override
public void mouseEntered(MouseEvent e)
{
// Nothing to do here
}
@Override
public void mouseExited(MouseEvent e)
{
// Nothing to do here
}
}
添加回答
举报