gpt4 book ai didi

java - JUNG 沿边缘的动画图像

转载 作者:行者123 更新时间:2023-12-01 12:16:23 32 4
gpt4 key购买 nike

所以我正在使用 JUNG,这对我来说是新事物。我已经实现了一个简单的 GUI,它显示顶点和边,并且通过使用 Transformer 我可以更改形状等。但是我该如何沿着边缘制作图像动画呢?

如果边缘是直线,那就很容易了,因为我知道起点和终点 X 和 Y 坐标,但边缘也可以是 BentLines、CubicCurves 或 QuadCurves。比如说,我如何让汽车沿着绘制的线移动?

我已经查看了 PathIterator 的文档,但说实话,我不知道它实际上是做什么的以及它是否适合我想要的。

任何正确方向的指针将不胜感激!

最佳答案

事实上,这有点棘手。

首先,为了获得屏幕上绘制的真实边缘形状,需要进行一些扭曲。幸运的是,相关代码已包含在 JUNG 的 ShapePickSupport.java 类中。

然后,必须计算该形状上的一个点(因此隐式假定为一条线)。这涉及到使用 PathIterator 来计算总长度以及线路上的当前位置。

我尝试实现这个(以非常基本且简单的形式)并将其封装在 ImageAtEdgePainter 类中:它接收 VisualizationViewer用于边缘形状计算,以及应绘制的边缘和图像。它有一个 setImageLocation 方法,接受 0.0 到 1.0 之间的值,其中 0.0 表示图像应位于边缘的开始处,1.0 表示图像位于边缘的末端,分别。

使用虚拟图表和虚拟图像,结果如下所示:

ImageAtEdge

图像在边缘端点之间振荡。这是代码,如 MCVE :

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;

import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

import edu.uci.ics.jung.algorithms.layout.FRLayout;
import edu.uci.ics.jung.algorithms.layout.Layout;
import edu.uci.ics.jung.graph.DirectedSparseGraph;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.util.Context;
import edu.uci.ics.jung.graph.util.Pair;
import edu.uci.ics.jung.visualization.Layer;
import edu.uci.ics.jung.visualization.MultiLayerTransformer;
import edu.uci.ics.jung.visualization.RenderContext;
import edu.uci.ics.jung.visualization.VisualizationViewer;

public class JUNGEdgePathTest
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
createAndShowGUI();
}
});
}

private static BufferedImage createDummyImage()
{
int w = 100;
int h = 30;
BufferedImage image =
new BufferedImage(w,h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = image.createGraphics();
g.setColor(Color.BLACK);
g.fillRect(0,0,w,h);
g.setColor(Color.WHITE);
g.drawString("Image", 10, 20);
g.dispose();
return image;
}


private static void createAndShowGUI()
{
JFrame f = new JFrame();
final Graph<String, String> graph = getGraph();
final VisualizationViewer<String, String> vv =
new VisualizationViewer<String, String>(
new FRLayout<String, String>(graph));
final BufferedImage image = createDummyImage();

String edge = graph.getEdges().iterator().next();
final ImageAtEdgePainter<String, String> imageAtEdgePainter =
new ImageAtEdgePainter<String, String>(vv, edge, image);

Timer t = new Timer(20, new ActionListener()
{
long prevMillis = 0;
@Override
public void actionPerformed(ActionEvent e)
{
if (prevMillis == 0)
{
prevMillis = System.currentTimeMillis();
}
long dtMs = System.currentTimeMillis() - prevMillis;
double dt = dtMs / 1000.0;
double phase = 0.5 + Math.sin(dt) * 0.5;
imageAtEdgePainter.setImageLocation(phase);
vv.repaint();
}
});
t.start();

vv.addPostRenderPaintable(imageAtEdgePainter);


f.getContentPane().add(vv);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setVisible(true);
}

static class ImageAtEdgePainter<V, E> implements VisualizationViewer.Paintable
{
private final VisualizationViewer<V, E> vv;
private final E edge;
private final BufferedImage image;
private double imageLocation;

ImageAtEdgePainter(
VisualizationViewer<V, E> vv,
E edge,
BufferedImage image)
{
this.vv = vv;
this.edge = edge;
this.image = image;
}

public void setImageLocation(double imageLocation)
{
this.imageLocation = imageLocation;
}

@Override
public void paint(Graphics gr)
{
Graphics2D g = (Graphics2D)gr;
Shape shape = getTransformedEdgeShape(vv, vv.getGraphLayout(), edge);
Point2D p = computePointAt(shape, 0.2, imageLocation);
//g.setColor(Color.BLUE);
//g.draw(shape);
//System.out.println(p);
gr.drawImage(image, (int)p.getX(), (int)p.getY(), null);
}
@Override
public boolean useTransform()
{
return true;
}

}



private static double computeLength(Shape shape, double flatness)
{
double length = 0;
PathIterator pi = shape.getPathIterator(null, flatness);
double[] coords = new double[6];
double previous[] = new double[2];
while (!pi.isDone())
{
int segment = pi.currentSegment(coords);
switch (segment)
{
case PathIterator.SEG_MOVETO:
previous[0] = coords[0];
previous[1] = coords[1];
break;

case PathIterator.SEG_LINETO:
double dx = previous[0]-coords[0];
double dy = previous[1]-coords[1];
length += Math.sqrt(dx*dx+dy*dy);
previous[0] = coords[0];
previous[1] = coords[1];
break;
}
pi.next();
}
return length;
}

public static Point2D computePointAt(
Shape shape, double flatness, double alpha)
{
alpha = Math.min(1.0, Math.max(0.0, alpha));
double totalLength = computeLength(shape, flatness);
double targetLength = alpha * totalLength;
double currentLength = 0;
PathIterator pi = shape.getPathIterator(null, flatness);
double[] coords = new double[6];
double previous[] = new double[2];
while (!pi.isDone())
{
int segment = pi.currentSegment(coords);
switch (segment)
{
case PathIterator.SEG_MOVETO:
previous[0] = coords[0];
previous[1] = coords[1];
break;

case PathIterator.SEG_LINETO:
double dx = previous[0]-coords[0];
double dy = previous[1]-coords[1];
double segmentLength = Math.sqrt(dx*dx+dy*dy);
double nextLength = currentLength + segmentLength;
if (nextLength >= targetLength)
{
double localAlpha =
(currentLength - targetLength) / segmentLength;
//System.out.println("current "+currentLength+" target "+targetLength+" seg "+segmentLength);
double x = previous[0] + localAlpha * dx;
double y = previous[1] + localAlpha * dy;
return new Point2D.Double(x,y);
}
previous[0] = coords[0];
previous[1] = coords[1];
currentLength = nextLength;
break;
}
pi.next();
}
return null;
}


// This method is take from JUNG ShapePickSupport.java
private static <V, E> Shape getTransformedEdgeShape(
VisualizationViewer<V, E> vv, Layout<V, E> layout, E e) {
Pair<V> pair = layout.getGraph().getEndpoints(e);
V v1 = pair.getFirst();
V v2 = pair.getSecond();
boolean isLoop = v1.equals(v2);
RenderContext<V, E> rc = vv.getRenderContext();
MultiLayerTransformer multiLayerTransformer =
rc.getMultiLayerTransformer();
Point2D p1 = multiLayerTransformer.transform(
Layer.LAYOUT, layout.transform(v1));
Point2D p2 = multiLayerTransformer.transform(
Layer.LAYOUT, layout.transform(v2));
if(p1 == null || p2 == null)
return null;
float x1 = (float) p1.getX();
float y1 = (float) p1.getY();
float x2 = (float) p2.getX();
float y2 = (float) p2.getY();
AffineTransform xform = AffineTransform.getTranslateInstance(x1, y1);
Shape edgeShape =
rc.getEdgeShapeTransformer().transform(
Context.<Graph<V,E>,E>getInstance(
vv.getGraphLayout().getGraph(),e));
if(isLoop) {
Shape s2 = rc.getVertexShapeTransformer().transform(v2);
Rectangle2D s2Bounds = s2.getBounds2D();
xform.scale(s2Bounds.getWidth(),s2Bounds.getHeight());
xform.translate(0, -edgeShape.getBounds2D().getHeight()/2);
} else {
float dx = x2 - x1;
float dy = y2 - y1;
double theta = Math.atan2(dy,dx);
xform.rotate(theta);
float dist = (float) Math.sqrt(dx*dx + dy*dy);
xform.scale(dist, 1.0f);
}
edgeShape = xform.createTransformedShape(edgeShape);
return edgeShape;
}


public static Graph<String, String> getGraph()
{
Graph<String, String> g = new DirectedSparseGraph<String, String>();
g.addVertex("v0");
g.addVertex("v1");
g.addVertex("v2");
g.addVertex("v3");
g.addVertex("v4");
g.addEdge("e0", "v0", "v1");
g.addEdge("e1", "v1", "v2");
g.addEdge("e2", "v2", "v3");
g.addEdge("e3", "v3", "v4");
g.addEdge("e4", "v4", "v0");
g.addEdge("e5", "v1", "v3");
g.addEdge("e6", "v2", "v4");
return g;
}
}

当您说要沿线移动汽车时,我可以想象您还希望将汽车的图像与边缘对齐 - 即旋转 图像,使汽车始终指向边缘的末端。这不会太困难。但如果这是一个问题,您可能应该首先看看其他问题(例如 Java: Rotate image towards mouse position? ),看看那里的答案是否可以......“启发”您,或者将其作为一个单独的(非 JUNG 特定的)提出) 问题。

关于java - JUNG 沿边缘的动画图像,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26977847/

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com