- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
所以我正在使用 JUNG,这对我来说是新事物。我已经实现了一个简单的 GUI,它显示顶点和边,并且通过使用 Transformer 我可以更改形状等。但是我该如何沿着边缘制作图像动画呢?
如果边缘是直线,那就很容易了,因为我知道起点和终点 X 和 Y 坐标,但边缘也可以是 BentLines、CubicCurves 或 QuadCurves。比如说,我如何让汽车沿着绘制的线移动?
我已经查看了 PathIterator 的文档,但说实话,我不知道它实际上是做什么的以及它是否适合我想要的。
任何正确方向的指针将不胜感激!
最佳答案
事实上,这有点棘手。
首先,为了获得屏幕上绘制的真实边缘形状,需要进行一些扭曲。幸运的是,相关代码已包含在 JUNG 的 ShapePickSupport.java
类中。
然后,必须计算该形状上的一个点(因此隐式假定为一条线)。这涉及到使用 PathIterator 来计算总长度以及线路上的当前位置。
我尝试实现这个(以非常基本且简单的形式)并将其封装在 ImageAtEdgePainter
类中:它接收 VisualizationViewer
用于边缘形状计算,以及应绘制的边缘和图像。它有一个 setImageLocation
方法,接受 0.0 到 1.0 之间的值,其中 0.0 表示图像应位于边缘的开始处,1.0 表示图像位于边缘的末端,分别。
使用虚拟图表和虚拟图像,结果如下所示:
图像在边缘端点之间振荡。这是代码,如 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/
我正在编写一个具有以下签名的 Java 方法。 void Logger(Method method, Object[] args); 如果一个方法(例如 ABC() )调用此方法 Logger,它应该
我是 Java 新手。 我的问题是我的 Java 程序找不到我试图用作的图像文件一个 JButton。 (目前这段代码什么也没做,因为我只是得到了想要的外观第一的)。这是我的主课 代码: packag
好的,今天我在接受采访,我已经编写 Java 代码多年了。采访中说“Java 垃圾收集是一个棘手的问题,我有几个 friend 一直在努力弄清楚。你在这方面做得怎么样?”。她是想骗我吗?还是我的一生都
我的 friend 给了我一个谜语让我解开。它是这样的: There are 100 people. Each one of them, in his turn, does the following
如果我将使用 Java 5 代码的应用程序编译成字节码,生成的 .class 文件是否能够在 Java 1.4 下运行? 如果后者可以工作并且我正在尝试在我的 Java 1.4 应用程序中使用 Jav
有关于why Java doesn't support unsigned types的问题以及一些关于处理无符号类型的问题。我做了一些搜索,似乎 Scala 也不支持无符号数据类型。限制是Java和S
我只是想知道在一个 java 版本中生成的字节码是否可以在其他 java 版本上运行 最佳答案 通常,字节码无需修改即可在 较新 版本的 Java 上运行。它不会在旧版本上运行,除非您使用特殊参数 (
我有一个关于在命令提示符下执行 java 程序的基本问题。 在某些机器上我们需要指定 -cp 。 (类路径)同时执行java程序 (test为java文件名与.class文件存在于同一目录下) jav
我已经阅读 StackOverflow 有一段时间了,现在我才鼓起勇气提出问题。我今年 20 岁,目前在我的家乡(罗马尼亚克卢日-纳波卡)就读 IT 大学。足以介绍:D。 基本上,我有一家提供簿记应用
我有 public JSONObject parseXML(String xml) { JSONObject jsonObject = XML.toJSONObject(xml); r
我已经在 Java 中实现了带有动态类型的简单解释语言。不幸的是我遇到了以下问题。测试时如下代码: def main() { def ks = Map[[1, 2]].keySet()
一直提示输入 1 到 10 的数字 - 结果应将 st、rd、th 和 nd 添加到数字中。编写一个程序,提示用户输入 1 到 10 之间的任意整数,然后以序数形式显示该整数并附加后缀。 public
我有这个 DownloadFile.java 并按预期下载该文件: import java.io.*; import java.net.URL; public class DownloadFile {
我想在 GUI 上添加延迟。我放置了 2 个 for 循环,然后重新绘制了一个标签,但这 2 个 for 循环一个接一个地执行,并且标签被重新绘制到最后一个。 我能做什么? for(int i=0;
我正在对对象 Student 的列表项进行一些测试,但是我更喜欢在 java 类对象中创建硬编码列表,然后从那里提取数据,而不是连接到数据库并在结果集中选择记录。然而,自从我这样做以来已经很长时间了,
我知道对象创建分为三个部分: 声明 实例化 初始化 classA{} classB extends classA{} classA obj = new classB(1,1); 实例化 它必须使用
我有兴趣使用 GPRS 构建车辆跟踪系统。但是,我有一些问题要问以前做过此操作的人: GPRS 是最好的技术吗?人们意识到任何问题吗? 我计划使用 Java/Java EE - 有更好的技术吗? 如果
我可以通过递归方法反转数组,例如:数组={1,2,3,4,5} 数组结果={5,4,3,2,1}但我的结果是相同的数组,我不知道为什么,请帮助我。 public class Recursion { p
有这样的标准方式吗? 包括 Java源代码-测试代码- Ant 或 Maven联合单元持续集成(可能是巡航控制)ClearCase 版本控制工具部署到应用服务器 最后我希望有一个自动构建和集成环境。
我什至不知道这是否可能,我非常怀疑它是否可能,但如果可以,您能告诉我怎么做吗?我只是想知道如何从打印机打印一些文本。 有什么想法吗? 最佳答案 这里有更简单的事情。 import javax.swin
我是一名优秀的程序员,十分优秀!