2014-11-17 4 views
0

그래서 저는 새로운 것을 알고 있습니다. 저는 이미 간단한 GUI를 구현했습니다.이 GUI는 정점과 모서리를 표시하며, 트랜스포머를 사용하여 모양과 모든 것을 변경할 수 있습니다. 그러나 가장자리를 따라 이미지를 어떻게 애니메이트 할 수 있습니까?정은 가장자리를 따라 이미지를 움직입니다.

가장자리가 직선이라면 시작 및 끝 X 및 Y 좌표를 알면 쉽게되지만 가장자리는 BentLines, CubicCurves 또는 QuadCurves 일 수도 있습니다. 말하자면 나는 그려진 선을 따라 차를 움직이게 만들까요?

PathIterator에 대한 문서를 살펴 봤지만 솔직히 말해서 내가 실제로하는 일과 내가 원하는 것에 적합하지 않은지에 대한 단서가 없습니다.

올바른 방향으로 어떤 포인터를 주시면 감사하겠습니다!

답변

1

사실 이것은 약간 까다 롭습니다.

우선, 화면에 그려진 가장자리 모양을 실제 얻으려면 약간의 왜곡이 필요합니다. 다행히 관련 코드는 이미 JUNG의 ShapePickSupport.java 클래스에 포함되어 있습니다.

그러면이 모양의 점을 계산해야합니다 (따라서 암시 적으로 선으로 가정 됨). 이것은 전체 길이와 라인의 현재 위치를 계산하기 위해 PathIterator을 가지고 놀아 다니는 것을 포함합니다. 그것은 에지 형상 계산 용 VisualizationViewer뿐만 아니라 에지 그린되어야하는 이미지를 수신한다 :

난 (a 매우 기본적이고 간단한 형태로)이를 구현하고 ImageAtEdgePainter 클래스이 캡슐화하려고 . 이 메서드는 0.0에서 1.0 사이의 값을 받아들이는 setImageLocation 메서드를 사용합니다. 여기서 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 특정이 아닌) 질문으로 부탁해야합니다 .

+0

내가 실제로 차를 정렬해야한다고 생각하지는 않았다. 지금은 언급했지만, 내가 나중에 필요할 것 같았다고 생각한다. 그러나 어쨌든 고마워요 D 답변에 관해서는 매우 도움이되는 것처럼 보입니다 만, 늦었으니 잠깐 살펴 보겠습니다. 나중에 더 많은 피드백을 얻으십시오. – tiansivive

+0

아름답게, 정확하게 내가 의도 한대로 작동하고 PathIterator로 작업하는 방법을 알아 냈습니다! 감사! – tiansivive

관련 문제