From 0dcb7c6b0f77aebbeceae8873075962667b5b436 Mon Sep 17 00:00:00 2001 From: Mateusz Nabywaniec Date: Mon, 6 Oct 2025 10:16:16 +0200 Subject: [PATCH 1/2] Fix annotation moving to the left when X axis PV changes When on plot the X axis PV changes the way that there is no anymore the same x value, the annotation was choosing the last index before. When X axis PV is changing every time, it was causing the issue that annotation was slowly going to the left and finally ending at the beginning. Finding the closest sample instead should fix the issue. --- .../javafx/rtplot/data/PlotDataSearch.java | 66 +++++++++++++++++++ .../rtplot/internal/AnnotationImpl.java | 2 +- 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/app/rtplot/src/main/java/org/csstudio/javafx/rtplot/data/PlotDataSearch.java b/app/rtplot/src/main/java/org/csstudio/javafx/rtplot/data/PlotDataSearch.java index e5382360ac..29cec7d9b7 100644 --- a/app/rtplot/src/main/java/org/csstudio/javafx/rtplot/data/PlotDataSearch.java +++ b/app/rtplot/src/main/java/org/csstudio/javafx/rtplot/data/PlotDataSearch.java @@ -7,6 +7,9 @@ ******************************************************************************/ package org.csstudio.javafx.rtplot.data; +import java.util.Date; +import java.time.Instant; + /** Search for samples in a haystack. * @author Kay Kasemir */ @@ -125,4 +128,67 @@ final public int findSampleGreaterThan(final PlotDataProvider data, final } return -1; } + + /** Find the sample closest to the given value. + * @param data Data, must already be locked + * @param x The value to look for. + * @return Index of sample closest to x, or -1 if data is empty or type unsupported. + */ + public int findClosestSample(final PlotDataProvider data, final XTYPE x) { + int low = 0; + int high = data.size() - 1; + + if (high < 0) return -1; + + int bestIndex = -1; + double bestDistance = Double.MAX_VALUE; + + double target; + try { + target = toDouble(x); + } catch (IllegalArgumentException e) { + return -1; + } + + double distance; + + while (low <= high) { + int mid = (low + high) / 2; + PlotDataItem sample = data.get(mid); + XTYPE sampleX = sample.getPosition(); + int cmp = sampleX.compareTo(x); + + try{ + distance = Math.abs(toDouble(sampleX) - target); + } catch (IllegalArgumentException e) { + return -1; + } + + if (distance < bestDistance) { + bestDistance = distance; + bestIndex = mid; + } + + if (cmp == 0) { + return mid; + } else if (cmp < 0) { + low = mid + 1; + } else { + high = mid - 1; + } + } + + return bestIndex; + } + + /** Convert supported XTYPE to double for distance comparison */ + private double toDouble(XTYPE x) { + if (x instanceof Number) + return ((Number) x).doubleValue(); + if (x instanceof Instant) + return ((Instant) x).toEpochMilli(); + if (x instanceof Date) + return ((Date) x).getTime(); + throw new IllegalArgumentException("Unsupported XTYPE: " + x.getClass()); + } } diff --git a/app/rtplot/src/main/java/org/csstudio/javafx/rtplot/internal/AnnotationImpl.java b/app/rtplot/src/main/java/org/csstudio/javafx/rtplot/internal/AnnotationImpl.java index c2935a81b7..275776a53d 100644 --- a/app/rtplot/src/main/java/org/csstudio/javafx/rtplot/internal/AnnotationImpl.java +++ b/app/rtplot/src/main/java/org/csstudio/javafx/rtplot/internal/AnnotationImpl.java @@ -179,7 +179,7 @@ boolean updateValue(final XTYPE location) throws Exception throw new TimeoutException("Cannot update annotation, no lock on " + data); try { - final int index = search.findSampleLessOrEqual(data, location); + final int index = search.findClosestSample(data, location); if (index < 0) return false; final PlotDataItem sample = data.get(index); From eea5f74ea96bc94535daeecf81858be6b10f4e50 Mon Sep 17 00:00:00 2001 From: Mateusz Nabywaniec Date: Tue, 7 Oct 2025 09:11:43 +0200 Subject: [PATCH 2/2] Alternate between less-or-equal and greater-or-equal sample searches That is possibly easier solution - instead of searching closest sample every update call we first search for less-or-equal sample and then greater-or-equal. Thanks to that we should keep more or less the same position as in the beginning. --- .../javafx/rtplot/internal/AnnotationImpl.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/app/rtplot/src/main/java/org/csstudio/javafx/rtplot/internal/AnnotationImpl.java b/app/rtplot/src/main/java/org/csstudio/javafx/rtplot/internal/AnnotationImpl.java index 275776a53d..ae519e0d04 100644 --- a/app/rtplot/src/main/java/org/csstudio/javafx/rtplot/internal/AnnotationImpl.java +++ b/app/rtplot/src/main/java/org/csstudio/javafx/rtplot/internal/AnnotationImpl.java @@ -43,6 +43,8 @@ public class AnnotationImpl> extends Annotation< private static final Stroke DASH = new BasicStroke(1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10.0f, new float[] { 3f, 3f }, 1.0f); + private boolean searchLessOrEqual; + /** What part of this annotation has been selected by the mouse? */ public static enum Selection { /** Nothing */ @@ -65,6 +67,7 @@ public static enum Selection public AnnotationImpl(final boolean internal, final Trace trace, final XTYPE position, final double value, final Point2D offset, final String text) { super(internal, trace, position, value, offset, text); + searchLessOrEqual = true; } /** Set to new position @@ -179,7 +182,15 @@ boolean updateValue(final XTYPE location) throws Exception throw new TimeoutException("Cannot update annotation, no lock on " + data); try { - final int index = search.findClosestSample(data, location); + final int index; + if (searchLessOrEqual){ + index = search.findSampleLessOrEqual(data, location); + } + else{ + index = search.findSampleGreaterOrEqual(data, location); + } + searchLessOrEqual = !searchLessOrEqual; + if (index < 0) return false; final PlotDataItem sample = data.get(index);