# JFreechart: ValueMarker mit der Maus ziehen?



## SuperFamicom (25. Jul 2015)

Hallo,
wie lässt sich innerhalb von ChartPanel Drag'&'Drop von bereits platzierten ValueMarkern zum Repositionieren realisieren? Insofern ich das überblicken konnte, bietet JFreechart nichts derart Vorgefertigtes an, sodass ein solches Feature recht aufwändig (?) from scratch geschrieben werden müsste.

Cheers,
Stefan


----------



## Harry Kane (28. Jul 2015)

Aufwand ist sicher nötig, aber er sollte sich in Grenzen halten.

Um später an gerenderte Objekte noch ranzukommen, nutzt JFreeChart das Konzept der Entities. Eine Entity ist im Wesentlichen eine Shape (also eine Form). Beim Zeichnen kann eine Entity in einer EntityCollection abgelegt werden. Bei einem Mausklick auf den fertigen Chart kann dann geschaut werden, ob an den Mauskoordinaten eine Entity liegt.
Marker unterstützen leider soweit ich das gesehen habe, keine Entities, und das einzubauen, dürfte nicht so ohne weiteres gehen. Es gibt aber einen Satz an Annotations, von denen einige die Rolle eines ValueMarker erfüllen könne. Den Quellcode gibts auch auf Sourceforge (http://sourceforge.net/p/jfreechart/patches/316/). Annotations untersützen zwar entities, aber die erzeugten entities halten keine Referenz auf die annotation. Das heisst, selbst wenn du durch eine Mausaktion an die Entity rankommst, hast du noch lange nicht die Annotation.
Um das zu ändern, könntest du
a)  Eine neue Entity-Klasse schreiben, die eine Referenz auf eine Annotation hält und
b)  Den Quellcode der o.g. genannten Annotation-Klassen so umschreiben, dass eine Annotation-Instanz beim rendern eine neuen Entity-Instanz mit einer Referenz auf die Annotation in der Entitycollection ablegt.
Dann stellt sich noch die Frage, wie du bei einem mouse drag die Koordinaten der Annotation verändern kannst. Wenn du sowieso eine neue Entity-Klasse anlegen musst, kannst du sie auch so schreiben, dass sie nicht nur eine Referenz auf die Annotation-Klasse bekommt, sondern auch Referenzen auf den XYPlot und die x- und y-Achse (bzw. domain und range axis).
Schliesslich hängst du an den ChartPanel einen neuen MouseListener, der auf mouseDragged-Events reagiert. Dann kannst du mit Hilfe der Koordinaten des MouseEvents über ChartPanel.getEntityForPoint(int viewX, int viewY) und mit instanceof prüfen, ob an den Koordinaten eine der o.g. Entities liegt. Wenn ja, übergibst du die Mauskoordinaten an die Entity. Die Koordinaten des letzten MouseEvents bzw. die Koordinaten eines anfänglichen MousePressed-Events muss die Entity sich natürlich „merken“. Die Bildschirmkoordinaten kannst du dann innerhalb der Entity mit Hilfe des XYPlots und der x und y Achse in Datenkoordinaten umrechnen und dann die Position der Annotation (auf die die Entity ja auch eine Referenz hält) entsprechend anpassen.


----------



## SuperFamicom (28. Jul 2015)

Danke erstmal für deine Antwort.
Das scheint mir recht kompliziert. Wie sähe denn eine neu geschriebene Entity bzw. umgeschriebene Annotation-Klassen im konkreten aus?


----------



## Harry Kane (28. Jul 2015)

SuperFamicom hat gesagt.:


> Das scheint mir recht kompliziert.


Das sieht nur so aus. Es klingt leider immer etwas kompliziert, wenn ich versuche möglichst genau zu beschreiben was ein Code tun soll.
Ich habe mir gerade den Patch runtergeladen, entpackt und angeschaut. Er enthält zusätzlich zu den neuen Annotation-Klassen auch eine neue Entity-Klasse (AxisAnnotationEntity), die teilweise bereits das macht was ich oben geschildert habe, nämlich eine Referenz auf die Annotation zu halten. Eine AxisAnnotationEntity hat aber keine Referenz auf einen XYPlot und die x und y Achse, was zur Umrechnung der Bildschirm- in Datenkoordinaten notwendig wäre.
Dan gibt es zwei Möglichkeiten:
1. Entweder die Klassen so anpassen, dass das möglich wird oder
2. Versuchen, die Informationan auf einem anderen Wege zu bekommen.
Wenn du ein Chartpanel Instanz hast, kannst du über Chartpanel.getChart() an den JFreeChart rankommen und über JFreeChart.getPlot() an den Plot. Die AxisAnnotationEntity hält ausserdem eine Referenz auf einen rendererIndex.
An alles andere notwendige sollte man über das ChartPanel Objekt kommen, und zwar so oder so ähnlich. Note: es wird beim Casten keinerlei Typüberprüfung durchgeführt und auch keine Prüfung auf != null. Das müsstest du einfügen.

```
public void mouseDragged(MouseEvent me) {
   Point mp  = me.getPoint();
   Point java2DPoint = chartPanel.translateScreenToJava2D(mp);
   Rectangle2D dataArea = chartPanel.getScreenDataArea(mp.getX(), mp.getY());
   AxisAnnotation axisAnnotation = (AxisAnnotation)(chartPanel.getEntityForPoint(mp.getX(), mp.getY()));
   JFreeChart chart = chartPanel.getChart();
   XYPlot plot = (XYPlot)(chart.getPlot());
   ValueAxis domainAxis = plot.getDomainAxis(axisAnnotation.getRendererIndex());
   ValueAxis rangeAxis = plot.getRangeAxis(axisAnnotation.getRendererIndex());
   RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
   RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
   double java2dX = domainAxis.java2DToValue(mouseEvent.getX(), dataArea, xAxisLocation);
   double java2dY = rangeAxis.java2DToValue(mouseEvent.getY(), dataArea, yAxisLocation);
}
```


----------



## SuperFamicom (28. Jul 2015)

Okay, wenn ich dich richtig verstanden habe gilt es anstelle von Markern z.B. XYDomainValueAnnotation per Mausklick zu platzieren. Diese werden mit plot.addAnnotation(annotation) direkt dem jeweiligen Plot übergeben.
Ich hab mir mal das Beispiel CombinedXYPLotDemo1 hergenommen und über MouseAdapter das Hinzufügen der XYDomainValueAnnotation ermöglicht, welche jedoch nicht dargestellt wird. Habe ich hier irgendwas missverstanden oder übersehen?:


```
import gui.jfc.ano.XYDomainValueAnnotation;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import javax.swing.JPanel;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.labels.StandardXYToolTipGenerator;
import org.jfree.chart.plot.CombinedDomainXYPlot;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.*;
import org.jfree.data.time.*;
import org.jfree.data.xy.IntervalXYDataset;
import org.jfree.data.xy.XYDataset;
import org.jfree.ui.*;

public final class CombinedXYPlotDemo1 extends ApplicationFrame {

    public CombinedXYPlotDemo1(String s) {
        super(s);
        JPanel jpanel = createDemoPanel();
        jpanel.setPreferredSize(new Dimension(500, 270));    
        jpanel.addMouseListener(new MouseAdapter() {

            @Override
            public void mouseClicked(MouseEvent e) {
            
                CombinedDomainXYPlot plot = (CombinedDomainXYPlot) ((ChartPanel) jpanel).getChart().getPlot();
            
                XYDomainValueAnnotation annotation = new XYDomainValueAnnotation();
                annotation.setValue(1995);            
                plot.addAnnotation(annotation);
            
            }                    
        
        });
        setContentPane(jpanel);
    }

    private JFreeChart createCombinedChart() {
    
        IntervalXYDataset intervalDataset = createDataset1();
    
        XYLineAndShapeRenderer lineAndShapeRenderer = new XYLineAndShapeRenderer(true, false);
        lineAndShapeRenderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator("{0}: ({1}, {2})", new SimpleDateFormat("d-MMM-yyyy"), new DecimalFormat("0.00")));
        lineAndShapeRenderer.setSeriesStroke(0, new BasicStroke(4F, 1, 2));
        lineAndShapeRenderer.setSeriesPaint(0, Color.blue);
    
        DateAxis dateAxis = new DateAxis("Year");
        dateAxis.setLowerMargin(0.0D);
        dateAxis.setUpperMargin(0.02D);
    
        NumberAxis numberAxis = new NumberAxis("$billion");
    
        XYPlot plot = new XYPlot(intervalDataset, null, numberAxis, lineAndShapeRenderer);
        plot.setBackgroundPaint(Color.lightGray);
        plot.setDomainGridlinePaint(Color.white);
        plot.setRangeGridlinePaint(Color.white);
    
        IntervalXYDataset intervalDataset1 = createDataset2();
    
        XYBarRenderer barRenderer = new XYBarRenderer() {

            @Override
            public Paint getItemPaint(int i, int j) {
                XYDataset dataset = getPlot().getDataset();
                if (dataset.getYValue(i, j) >= 0.0D) {
                    return Color.red;
                } else {
                    return Color.green;
                }
            }

        };
        barRenderer.setSeriesPaint(0, Color.red);
        barRenderer.setDrawBarOutline(false);
        barRenderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator("{0}: ({1}, {2})", new SimpleDateFormat("d-MMM-yyyy"), new DecimalFormat("0.00")));
    
        XYPlot plot1 = new XYPlot(intervalDataset1, null, new NumberAxis("$billion"), barRenderer);
        plot1.setBackgroundPaint(Color.lightGray);
        plot1.setDomainGridlinePaint(Color.white);
        plot1.setRangeGridlinePaint(Color.white);
    
        CombinedDomainXYPlot combinedDomainPlot = new CombinedDomainXYPlot(dateAxis);
        combinedDomainPlot.add(plot, 3);
        combinedDomainPlot.add(plot1, 2);
        combinedDomainPlot.setGap(8D);
        combinedDomainPlot.setDomainGridlinePaint(Color.white);
        combinedDomainPlot.setDomainGridlinesVisible(true);
    
        JFreeChart freeChart = new JFreeChart("United States Public Debt", JFreeChart.DEFAULT_TITLE_FONT, combinedDomainPlot, false);
        freeChart.setBackgroundPaint(Color.white);    
                            
        return freeChart;
    }

    private static IntervalXYDataset createDataset1(){
        TimeSeries timeseries = new TimeSeries("Public Debt Outstanding", org.jfree.data.time.Month.class);
        timeseries.add(new Month(1, 1990), 2974.5839999999998D);    
        timeseries.add(new Month(1, 2007), 8707.5609999999997D);
        return new TimeSeriesCollection(timeseries);
    }

    private static IntervalXYDataset createDataset2() {
        TimeSeriesCollection timeseriescollection = new TimeSeriesCollection();
        TimeSeries timeseries = new TimeSeries("Change from previous year", org.jfree.data.time.Month.class);
        timeseries.add(new Month(1, 1990), 276.62700000000001D);    
        timeseries.add(new Month(1, 2007), 511.49099999999999D);
        timeseriescollection.addSeries(timeseries);
        return timeseriescollection;
    }

    public JPanel createDemoPanel() {
        JFreeChart freeChart = createCombinedChart();
        return new ChartPanel(freeChart);
    }

    public static void main(String args[]) {
        CombinedXYPlotDemo1 combinedxyplotdemo1 = new CombinedXYPlotDemo1("JFreeChart : CombinedXYPlotDemo1");
        combinedxyplotdemo1.pack();
        RefineryUtilities.centerFrameOnScreen(combinedxyplotdemo1);
        combinedxyplotdemo1.setVisible(true);
    }
```


----------



## Harry Kane (28. Jul 2015)

Du verwendest eine DateAxis als x-Achse, d. h. alle Zahlenwerte, die du eingibst, werden als Millisekunden seit dem 1.1.1970 interpretiert. Deine XYDomainValueAnnotation sollte also am 1.1.1970 um 2 Sekunden nach Mitternacht angezeigt werden. Dies ist ausserhalb des Wertebereiches von deinen datasets.
Für das Jahr 1995 solltest du z. B. new Year(1995).getFirstMillisecond() verwenden können.


----------



## SuperFamicom (28. Jul 2015)

Selbst new Year(1995).getFirstMilliseconds() funktioniert nicht.
In meinem eigentlichen Programm, dass ich hier leider aufgrund des Umfangs nicht posten kann, benutze ich double-Werte innerhalb einer double-range, die angezeigt wird. Auch dort sine keine Annotations zu sehen.


----------



## Harry Kane (28. Jul 2015)

Nach etwas rumprobieren ist klar woran es liegt: der plot mit dem du arbeitest ist ein CombinedDomainXYPlot. Und dieser dient eigentlich nur als Container für verschiedene (Sub)-XYPlots und als Besitzer der gemeinsamen x-Achse. Man kan einem CombinedDomainXYPlot zwar Annotations hinzufügen (auch renderer und datasets), aber sie werden nicht gezeichnet, sondern das eigentliche Zeichnen  erfolgt in den subplots. Deshalb muss die Annoation dem subplot hinzugefügt werden.
Mit dem Code hier gehts. Du musst lediglich den Rückgabewert von createDemoPanel und den Typ von jpanel auf ChartPanel ändern.


```
public void mouseClicked(MouseEvent me) {

  Point mp = me.getPoint();
  Point2D java2DPoint = chartPanel.translateScreenToJava2D(mp);
  Rectangle2D dataArea = chartPanel.getScreenDataArea((int)mp.getX(), (int)mp.getY());
  JFreeChart chart = chartPanel.getChart();
  ChartRenderingInfo cri = chartPanel.getChartRenderingInfo();
  PlotRenderingInfo pri = null;
  if(cri != null){
  pri = cri.getPlotInfo();
  }
  XYPlot plot = (XYPlot) (chart.getPlot());
  if(plot instanceof CombinedDomainXYPlot){
  CombinedDomainXYPlot comb = (CombinedDomainXYPlot)plot;
  plot = comb.findSubplot(pri, java2DPoint);
  }
  ValueAxis domainAxis = plot.getDomainAxis();//*axisAnnotation.getRendererIndex());
  ValueAxis rangeAxis = plot.getRangeAxis();//axisAnnotation.getRendererIndex());
  RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
  RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
  double java2dX = domainAxis.java2DToValue(me.getX(), dataArea, xAxisLocation);
  double java2dY = rangeAxis.java2DToValue(me.getY(), dataArea, yAxisLocation);

  XYDomainValueAnnotation annotation = new XYDomainValueAnnotation();
  annotation.setPaint(Color.red);
  annotation.setStroke(new BasicStroke(5.0f));
  annotation.setValue(java2dX);
  plot.addAnnotation(annotation);
}
```


----------



## SuperFamicom (28. Jul 2015)

Sowohl die Darstellung, das Platzieren und das Ziehen von Annotations über die Maus funktionieren nach deiner Anleitung schon ziemlich gut. Hier der Code:

```
import gui.jfc.ano.AxisAnnotationEntity;
import gui.jfc.ano.XYDomainValueAnnotation;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import javax.swing.JPanel;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.ChartRenderingInfo;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.labels.StandardXYToolTipGenerator;
import org.jfree.chart.plot.CombinedDomainXYPlot;
import org.jfree.chart.plot.PlotRenderingInfo;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.*;
import org.jfree.data.time.*;
import org.jfree.data.xy.IntervalXYDataset;
import org.jfree.data.xy.XYDataset;
import org.jfree.ui.*;

public final class CombinedXYPlotDemo1 extends ApplicationFrame {          
  
    public CombinedXYPlotDemo1(String s) {
        super(s);
        JPanel jpanel = createDemoPanel();
        ((ChartPanel) jpanel).setDomainZoomable(false);
        ((ChartPanel) jpanel).setRangeZoomable(false);
        jpanel.setPreferredSize(new Dimension(500, 270));
      
        jpanel.addMouseMotionListener(new MouseAdapter() {
            @Override
            public void mouseDragged(MouseEvent e) {
              
                Point p = e.getPoint(); 
                ChartPanel chartPanel = (ChartPanel) jpanel;
                if (chartPanel.getEntityForPoint(p.x, p.y) instanceof AxisAnnotationEntity) {
                    AxisAnnotationEntity entity = (AxisAnnotationEntity) chartPanel.getEntityForPoint(p.x, p.y);
                    XYDomainValueAnnotation annotation = (XYDomainValueAnnotation) entity.getAnnotation();                                              
                    Point2D p2D = chartPanel.translateScreenToJava2D(p);
                    Rectangle2D dataArea = chartPanel.getScreenDataArea((int) p.getX(), (int) p.getY());
                    JFreeChart chart = chartPanel.getChart();
                    ChartRenderingInfo cInfo = chartPanel.getChartRenderingInfo();
                    PlotRenderingInfo pInfo = null;              
                    if (cInfo != null) {
                        pInfo = cInfo.getPlotInfo();
                    }
                    XYPlot plot = (XYPlot) chart.getPlot();
                    if (plot instanceof CombinedDomainXYPlot) {
                        CombinedDomainXYPlot combinedPlot = (CombinedDomainXYPlot) plot;
                        plot = combinedPlot.findSubplot(pInfo, p2D);
                    }
                    ValueAxis domainAxis = plot.getDomainAxis();
                    ValueAxis rangeAxis = plot.getRangeAxis();
                    RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
                    RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
                    double java2dX = domainAxis.java2DToValue(e.getX(), dataArea, xAxisLocation);
                    double java2dY = rangeAxis.java2DToValue(e.getY(), dataArea, yAxisLocation);

                    annotation.setValue(java2dX);
                    repaint();
                }
              
            }
          
        });
      
        jpanel.addMouseListener(new MouseAdapter() {

            @Override
            public void mouseClicked(MouseEvent e) {
              
                ChartPanel chartPanel = (ChartPanel) jpanel;
                Point p = e.getPoint();
                Point2D p2D = chartPanel.translateScreenToJava2D(p);
                Rectangle2D dataArea = chartPanel.getScreenDataArea((int) p.getX(), (int) p.getY());
                JFreeChart chart = chartPanel.getChart();
                ChartRenderingInfo cInfo = chartPanel.getChartRenderingInfo();
                PlotRenderingInfo pInfo = null;              
                if (cInfo != null) {
                    pInfo = cInfo.getPlotInfo();
                }
              
                XYPlot plot = (XYPlot) chart.getPlot();
                if (plot instanceof CombinedDomainXYPlot) {
                    CombinedDomainXYPlot combinedPlot = (CombinedDomainXYPlot) plot;
                    plot = combinedPlot.findSubplot(pInfo, p2D);
                }
                ValueAxis domainAxis = plot.getDomainAxis();
                ValueAxis rangeAxis = plot.getRangeAxis();
                RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
                RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
                double java2dX = domainAxis.java2DToValue(e.getX(), dataArea, xAxisLocation);
                double java2dY = rangeAxis.java2DToValue(e.getY(), dataArea, yAxisLocation);
              
                XYDomainValueAnnotation annotation = new XYDomainValueAnnotation();
                annotation.setPaint(Color.RED);
                annotation.setStroke(new BasicStroke(0.5f));
                annotation.setValue(java2dX);
                plot.addAnnotation(annotation);
              
            }            
          
        });
        setContentPane(jpanel);
    }

    private JFreeChart createCombinedChart() {
      
        IntervalXYDataset intervalDataset = createDataset1();
      
        XYLineAndShapeRenderer lineAndShapeRenderer = new XYLineAndShapeRenderer(true, false);
        lineAndShapeRenderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator("{0}: ({1}, {2})", new SimpleDateFormat("d-MMM-yyyy"), new DecimalFormat("0.00")));
        lineAndShapeRenderer.setSeriesStroke(0, new BasicStroke(4F, 1, 2));
        lineAndShapeRenderer.setSeriesPaint(0, Color.blue);
      
        DateAxis dateAxis = new DateAxis("Year");
        dateAxis.setLowerMargin(0.0D);
        dateAxis.setUpperMargin(0.02D);
      
        NumberAxis numberAxis = new NumberAxis("$billion");
      
        XYPlot plot = new XYPlot(intervalDataset, null, numberAxis, lineAndShapeRenderer);
        plot.setBackgroundPaint(Color.lightGray);
        plot.setDomainGridlinePaint(Color.white);
        plot.setRangeGridlinePaint(Color.white);
      
        IntervalXYDataset intervalDataset1 = createDataset2();
      
        XYBarRenderer barRenderer = new XYBarRenderer() {

            @Override
            public Paint getItemPaint(int i, int j) {
                XYDataset dataset = getPlot().getDataset();
                if (dataset.getYValue(i, j) >= 0.0D) {
                    return Color.red;
                } else {
                    return Color.green;
                }
            }

        };
        barRenderer.setSeriesPaint(0, Color.red);
        barRenderer.setDrawBarOutline(false);
        barRenderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator("{0}: ({1}, {2})", new SimpleDateFormat("d-MMM-yyyy"), new DecimalFormat("0.00")));
      
        XYPlot plot1 = new XYPlot(intervalDataset1, null, new NumberAxis("$billion"), barRenderer);
        plot1.setBackgroundPaint(Color.lightGray);
        plot1.setDomainGridlinePaint(Color.white);
        plot1.setRangeGridlinePaint(Color.white);
      
        CombinedDomainXYPlot combinedDomainPlot = new CombinedDomainXYPlot(dateAxis);
        combinedDomainPlot.add(plot, 3);
        combinedDomainPlot.add(plot1, 2);
        combinedDomainPlot.setGap(8D);
        combinedDomainPlot.setDomainGridlinePaint(Color.white);
        combinedDomainPlot.setDomainGridlinesVisible(true);
      
        JFreeChart freeChart = new JFreeChart("United States Public Debt", JFreeChart.DEFAULT_TITLE_FONT, combinedDomainPlot, false);
        freeChart.setBackgroundPaint(Color.white);      

        return freeChart;
    }

    private static IntervalXYDataset createDataset1(){
        TimeSeries timeseries = new TimeSeries("Public Debt Outstanding", org.jfree.data.time.Month.class);
        timeseries.add(new Month(1, 1990), 2974.5839999999998D);      
        timeseries.add(new Month(1, 2007), 8707.5609999999997D);
        return new TimeSeriesCollection(timeseries);
    }

    private static IntervalXYDataset createDataset2() {
        TimeSeriesCollection timeseriescollection = new TimeSeriesCollection();
        TimeSeries timeseries = new TimeSeries("Change from previous year", org.jfree.data.time.Month.class);
        timeseries.add(new Month(1, 1990), 276.62700000000001D);      
        timeseries.add(new Month(1, 2007), 511.49099999999999D);
        timeseriescollection.addSeries(timeseries);
        return timeseriescollection;
    }

    public JPanel createDemoPanel() {
        JFreeChart freeChart = createCombinedChart();      
        return new ChartPanel(freeChart);
    }

    public static void main(String args[]) {
        CombinedXYPlotDemo1 combinedxyplotdemo1 = new CombinedXYPlotDemo1("JFreeChart : CombinedXYPlotDemo1");
        combinedxyplotdemo1.pack();
        RefineryUtilities.centerFrameOnScreen(combinedxyplotdemo1);
        combinedxyplotdemo1.setVisible(true);
    }
}
```

Die einzigen zwei Schönheitsfehler, die jetzt noch ausgemerzt werden müssten, sind:
1- Die Annotation folgt der Maus lediglich, wenn die Maus sehr langsam bewegt wird. Bewegt sich die Maus zu schnell, bleibt die Annotation auf der Strecke und wird nicht mehr neuplatziert.
2- Die Annotation muss sehr pixelgenau mit dem Mauscursor getroffen werden. Momentan benutze ich noch den Default-Cursor, werde später aber die übliche Grabbing-Hand einbauen. Diese sollte in einem bestimmten Toleranzbereich von meinetwegen +/- 4 Pixel eine Entity erkennen. Problem ist nur, dass hier mehrer Entities auf einem Haufen/innerhalb des Toleranzbereiches auftreten können. Hier müsste man man wahrscheinlich die Entity wählen, die den geringsten Abstand zum Zentrum der Maus (oder anderen Referenzpunkt der Maus) hat.​Was ich nicht so ganz verstehe, du meintest ...


> Die Koordinaten des letzten MouseEvents bzw. die Koordinaten eines anfänglichen MousePressed-Events muss die Entity sich natürlich „merken“.


 In meiner Umsetzung brauche ich die vorigen Koordinaten nicht. Kannst du erläutern, was du damit meintest?


----------



## Harry Kane (29. Jul 2015)

Meinen Hinweis zum "merken der Koordinaten" kannst du ignorieren. Da war ich gedanklich woanders.
Zu Schönheitsfehler 1: Das kommt daher weil du bei jedem mouse drag neu nach der Annotation suchst. Wenn du die Maus langsam bewegst, sind die Mauskoordinaten des "neuen" Events noch in dem Bereich, in den du die Entity beim "alten" Event hinbewegt hast. Wenn du die Maus zu schnell bewegst, liegen beim nächsten Abfragen der Mauskoordinaten diese nicht mehr im Bereich der Entity, und der instanceof AxisAnnotationEntity Test schlägt fehl.
Ich würds so machen:
1. Erweitere deinen MouseAdapter um eine Referenz auf eine AxisAnnotation.
2. Bei einem mousePressed Event nach einer AxisAnnotation suchen. Wenn an den exakten Mauskoordinaten keine AxisEntity gefunden werden kann, müsstest du halt ausgehend von den Koordinaten in positiver und negativer x-Richtung nach einer suchen. Die Koordinaten die du an die getEntityForPoint-Methode übergibst, müssen ja keine Mouseevent-Koordinaten sein, sondern können auch was berechnetest sein. Die gefundene Annotation kannst du im MouseAdapter speichern.
3. Bei einem mouseDragged nicht mehr nach einer Annotation suchen, sondern die in 2 gespeicherte an die neuen  Koordinaten schieben.
4. Bei einem mouseReleased im MouseAdapter die AxisAnnotation-Referenz auf null setzen.
Zu Schönheitsfehler 2: Sollte mit den obigen Hinweisen auch teilweise behoben sein. Schwierig bzw. grundsätzlich unmöglich wird es wenn es mehrere Annotations innerhalb eines physikalischen Pixels gibt.


----------



## SuperFamicom (29. Jul 2015)

Works like a charm

Bzgl. ...


Harry Kane hat gesagt.:


> Schwierig bzw. grundsätzlich unmöglich wird es wenn es mehrere Annotations innerhalb eines physikalischen Pixels gibt.


Habe ich getestet und glücklicherweise muss ich mich hierrum nicht kümmern. Wenn mehrere Annotations auf ein und denselben Pixel fallen, wird einfach irgendeine Annotation gegriffen (ich nehme mal an, die in der draw-Reihenfolge zuletzt gezeichnet wurde), die dann verschoben werden kann. Die anderen Annotations bleiben auf der Stelle liegen. Das passt sehr gut zum vorgesehenen Workflow meiner Software.


----------



## SuperFamicom (4. Aug 2015)

Noch ein kurzes Anliegen:
Der workflow sieht vor, dass dynamisch XYPlots hinzugefügt oder entfernt werden. Die vertikalen Plots liegen direkt nebeneinander (gap=0). Es ist recht unelegant jedesmal, wenn eine Annotation hinzugefügt wird, diese allen Plots zuzuweisen, beim Verschieben einer Annotation die anderen Annotations mit verschieben zu lassen bzw. den äußersten Plot abzufragen um dort ein Annotation-Label darzustellen. 
Du meintest Annotations können CombinedDomainXYPlot hinzugefügt, aber nicht dargestellt werden. Lässt sich das Problem nicht doch irgendwie dahingehend tweaken, dass lediglich eine Annotation innerhalb CombinedDomainXYPlot dargestellt wird, die über alle angezeigten Plots verläuft?


----------



## Harry Kane (4. Aug 2015)

Du könntest CombinedDomainXYPlot erweitern, die draw-methode überschreiben und in deiner überschriebenen draw-Methode drawAnnotations aufrufen.


----------

