From 4cb45c7b3553843301d0f849e1bc583ef3b5cda1 Mon Sep 17 00:00:00 2001 From: kotik-coder Date: Fri, 9 Sep 2022 16:11:46 +0300 Subject: [PATCH 1/6] Updated splash screen --- src/main/resources/images/splash.png | Bin 67897 -> 67108 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/main/resources/images/splash.png b/src/main/resources/images/splash.png index d8ce89ad9ca02f2e8a043e095b8ab888cbce8813..c9583b6ef18dc2c1130fd57b2d9804f368219730 100644 GIT binary patch literal 67108 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 literal 67897 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 N@O1TaS?83{1OQJbFdhH^ From 475d09b60f8ffbfcdba2fb774e8a59d830dc2874 Mon Sep 17 00:00:00 2001 From: kotik-coder Date: Sun, 25 Dec 2022 12:17:04 +0300 Subject: [PATCH 2/6] - Made an AbstractLogger superclass - Moved text logging capabilities to TextLogPaneExporter - Added reference to specific Calculation in log entries - Separated MVC functions in logging --- ...Exporter.java => TextLogPaneExporter.java} | 24 ++-- .../java/pulse/search/statistics/FTest.java | 2 +- .../java/pulse/tasks/logs/DataLogEntry.java | 1 + src/main/java/pulse/tasks/logs/Log.java | 13 ++ src/main/java/pulse/tasks/logs/LogEntry.java | 9 +- .../pulse/ui/components/AbstractLogger.java | 52 +++++++ .../java/pulse/ui/components/LogPane.java | 136 ------------------ .../java/pulse/ui/components/TextLogPane.java | 108 ++++++++++++++ src/main/java/pulse/ui/frames/LogFrame.java | 28 ++-- .../pulse/ui/frames/TaskControlFrame.java | 4 +- 10 files changed, 212 insertions(+), 165 deletions(-) rename src/main/java/pulse/io/export/{LogPaneExporter.java => TextLogPaneExporter.java} (67%) create mode 100644 src/main/java/pulse/ui/components/AbstractLogger.java delete mode 100644 src/main/java/pulse/ui/components/LogPane.java create mode 100644 src/main/java/pulse/ui/components/TextLogPane.java diff --git a/src/main/java/pulse/io/export/LogPaneExporter.java b/src/main/java/pulse/io/export/TextLogPaneExporter.java similarity index 67% rename from src/main/java/pulse/io/export/LogPaneExporter.java rename to src/main/java/pulse/io/export/TextLogPaneExporter.java index ade0e0f..e6ea39e 100644 --- a/src/main/java/pulse/io/export/LogPaneExporter.java +++ b/src/main/java/pulse/io/export/TextLogPaneExporter.java @@ -4,22 +4,23 @@ import java.io.FileOutputStream; import java.io.IOException; +import javax.swing.JEditorPane; import javax.swing.text.BadLocationException; import javax.swing.text.html.HTMLEditorKit; -import pulse.ui.components.LogPane; +import pulse.ui.components.TextLogPane; /** * Similar to a {@code LogExporter}, except that it works only on the contents * of a {@code LogPane} currently being displayed to the user. * */ -public class LogPaneExporter implements Exporter { +public class TextLogPaneExporter implements Exporter { - private static LogPaneExporter instance = new LogPaneExporter(); + private static TextLogPaneExporter instance = new TextLogPaneExporter(); - private LogPaneExporter() { + private TextLogPaneExporter() { // intentionally blank } @@ -29,10 +30,11 @@ private LogPaneExporter() { * argument is ignored. After exporting, the stream is explicitly closed. */ @Override - public void printToStream(LogPane pane, FileOutputStream fos, Extension extension) { - var kit = (HTMLEditorKit) pane.getEditorKit(); + public void printToStream(TextLogPane pane, FileOutputStream fos, Extension extension) { + var editorPane = (JEditorPane) pane.getGUIComponent(); + var kit = (HTMLEditorKit) editorPane.getEditorKit(); try { - kit.write(fos, pane.getDocument(), 0, pane.getDocument().getLength()); + kit.write(fos, editorPane.getDocument(), 0, editorPane.getDocument().getLength()); } catch (IOException | BadLocationException e) { System.err.println("Could not export the log pane!"); e.printStackTrace(); @@ -50,7 +52,7 @@ public void printToStream(LogPane pane, FileOutputStream fos, Extension extensio * * @return an instance of{@code LogPaneExporter}. */ - public static LogPaneExporter getInstance() { + public static TextLogPaneExporter getInstance() { return instance; } @@ -58,8 +60,8 @@ public static LogPaneExporter getInstance() { * @return {@code LogPane.class}. */ @Override - public Class target() { - return LogPane.class; + public Class target() { + return TextLogPane.class; } /** @@ -70,4 +72,4 @@ public Extension[] getSupportedExtensions() { return new Extension[]{HTML}; } -} +} \ No newline at end of file diff --git a/src/main/java/pulse/search/statistics/FTest.java b/src/main/java/pulse/search/statistics/FTest.java index 4d9da69..4723f5f 100644 --- a/src/main/java/pulse/search/statistics/FTest.java +++ b/src/main/java/pulse/search/statistics/FTest.java @@ -137,4 +137,4 @@ public static Calculation findNested(Calculation a, Calculation b) { return aParams > bParams ? b : a; } -} +} \ No newline at end of file diff --git a/src/main/java/pulse/tasks/logs/DataLogEntry.java b/src/main/java/pulse/tasks/logs/DataLogEntry.java index 2fad022..4e17c87 100644 --- a/src/main/java/pulse/tasks/logs/DataLogEntry.java +++ b/src/main/java/pulse/tasks/logs/DataLogEntry.java @@ -2,6 +2,7 @@ import java.lang.reflect.InvocationTargetException; import java.util.List; +import pulse.Response; import pulse.math.Parameter; import pulse.math.ParameterIdentifier; import pulse.properties.NumericProperties; diff --git a/src/main/java/pulse/tasks/logs/Log.java b/src/main/java/pulse/tasks/logs/Log.java index 5b51953..1153499 100644 --- a/src/main/java/pulse/tasks/logs/Log.java +++ b/src/main/java/pulse/tasks/logs/Log.java @@ -1,6 +1,8 @@ package pulse.tasks.logs; import java.time.LocalTime; +import static java.time.temporal.ChronoUnit.MILLIS; +import static java.time.temporal.ChronoUnit.SECONDS; import java.util.List; import java.util.Objects; import java.util.concurrent.CopyOnWriteArrayList; @@ -186,5 +188,16 @@ public static boolean isVerbose() { public static void setVerbose(boolean verbose) { Log.verbose = verbose; } + + /** + * Time taken where the first array element contains seconds [0] and the second contains milliseconds [1]. + * @return an array of long values that sum um to the time taken to process a task + */ + + public long[] timeTaken() { + var seconds = SECONDS.between(getStart(), getEnd()); + var ms = MILLIS.between(getStart(), getEnd()) - 1000L * seconds; + return new long[] {seconds, ms}; + } } diff --git a/src/main/java/pulse/tasks/logs/LogEntry.java b/src/main/java/pulse/tasks/logs/LogEntry.java index 7841a91..bfd84fb 100644 --- a/src/main/java/pulse/tasks/logs/LogEntry.java +++ b/src/main/java/pulse/tasks/logs/LogEntry.java @@ -3,6 +3,7 @@ import java.time.LocalDateTime; import java.time.LocalTime; import java.util.Objects; +import pulse.Response; import pulse.tasks.Identifier; import pulse.tasks.SearchTask; @@ -21,7 +22,8 @@ public class LogEntry { private Identifier identifier; private LocalTime time; - + private final Response response; + /** *

* Creates a {@code LogEntry} from this {@code SearchTask}. The data of the @@ -34,6 +36,11 @@ public LogEntry(SearchTask t) { Objects.requireNonNull(t, Messages.getString("LogEntry.NullTaskError")); time = LocalDateTime.now().toLocalTime(); identifier = t.getIdentifier(); + this.response = t.getResponse(); + } + + public Response getResponse() { + return response; } public Identifier getIdentifier() { diff --git a/src/main/java/pulse/ui/components/AbstractLogger.java b/src/main/java/pulse/ui/components/AbstractLogger.java new file mode 100644 index 0000000..916e678 --- /dev/null +++ b/src/main/java/pulse/ui/components/AbstractLogger.java @@ -0,0 +1,52 @@ +package pulse.ui.components; + +import java.util.concurrent.ExecutorService; +import static java.util.concurrent.Executors.newSingleThreadExecutor; +import javax.swing.JComponent; +import pulse.tasks.TaskManager; +import pulse.tasks.logs.Log; +import pulse.tasks.logs.LogEntry; +import pulse.util.Descriptive; + +public abstract class AbstractLogger implements Descriptive { + + private final ExecutorService updateExecutor = newSingleThreadExecutor(); + + public synchronized void update() { + var task = TaskManager.getManagerInstance().getSelectedTask(); + + if (task == null) { + return; + } + + var log = task.getLog(); + + if (!log.isStarted()) { + return; + } + + post(log.lastEntry()); + } + + public ExecutorService getUpdateExecutor() { + return updateExecutor; + } + + public synchronized void callUpdate() { + updateExecutor.submit(() -> update()); + } + + public abstract JComponent getGUIComponent(); + public abstract void printTimeTaken(Log log); + public abstract void post(LogEntry logEntry); + public abstract void post(String text); + public abstract void postAll(); + public abstract void clear(); + public abstract boolean isEmpty(); + + @Override + public String describe() { + return "Log_" + TaskManager.getManagerInstance().getSelectedTask().getIdentifier().getValue(); + } + +} \ No newline at end of file diff --git a/src/main/java/pulse/ui/components/LogPane.java b/src/main/java/pulse/ui/components/LogPane.java deleted file mode 100644 index 5af4215..0000000 --- a/src/main/java/pulse/ui/components/LogPane.java +++ /dev/null @@ -1,136 +0,0 @@ -package pulse.ui.components; - -import static java.lang.System.err; -import static java.time.temporal.ChronoUnit.MILLIS; -import static java.time.temporal.ChronoUnit.SECONDS; -import static java.util.concurrent.Executors.newSingleThreadExecutor; -import static javax.swing.text.DefaultCaret.ALWAYS_UPDATE; -import static pulse.tasks.logs.Status.DONE; -import static pulse.ui.Messages.getString; - -import java.io.IOException; -import java.util.concurrent.ExecutorService; - -import javax.swing.JEditorPane; -import javax.swing.text.BadLocationException; -import javax.swing.text.DefaultCaret; -import javax.swing.text.html.HTMLDocument; -import javax.swing.text.html.HTMLEditorKit; - -import pulse.tasks.TaskManager; -import pulse.tasks.logs.Log; -import pulse.tasks.logs.LogEntry; -import pulse.util.Descriptive; - -@SuppressWarnings("serial") -public class LogPane extends JEditorPane implements Descriptive { - - private ExecutorService updateExecutor = newSingleThreadExecutor(); - - public LogPane() { - super(); - setContentType("text/html"); - setEditable(false); - var c = (DefaultCaret) getCaret(); - c.setUpdatePolicy(ALWAYS_UPDATE); - } - - private void post(LogEntry logEntry) { - post(logEntry.toString()); - } - - /* - private void postError(String text) { - var sb = new StringBuilder(); - sb.append(getString("DataLogEntry.FontTagError")); - sb.append(text); - sb.append(getString("DataLogEntry.FontTagClose")); - post(sb.toString()); - }*/ - private void post(String text) { - - final var doc = (HTMLDocument) getDocument(); - final var kit = (HTMLEditorKit) this.getEditorKit(); - try { - kit.insertHTML(doc, doc.getLength(), text, 0, 0, null); - } catch (BadLocationException e) { - err.println(getString("LogPane.InsertError")); //$NON-NLS-1$ - e.printStackTrace(); - } catch (IOException e) { - err.println(getString("LogPane.PrintError")); //$NON-NLS-1$ - e.printStackTrace(); - } - - } - - public void printTimeTaken(Log log) { - var seconds = SECONDS.between(log.getStart(), log.getEnd()); - var ms = MILLIS.between(log.getStart(), log.getEnd()) - 1000L * seconds; - var sb = new StringBuilder(); - sb.append(getString("LogPane.TimeTaken")); //$NON-NLS-1$ - sb.append(seconds + getString("LogPane.Seconds")); //$NON-NLS-1$ - sb.append(ms + getString("LogPane.Milliseconds")); //$NON-NLS-1$ - post(sb.toString()); - } - - public synchronized void callUpdate() { - updateExecutor.submit(() -> update()); - } - - public void printAll() { - clear(); - - var task = TaskManager.getManagerInstance().getSelectedTask(); - - if (task != null) { - - var log = task.getLog(); - - if (log.isStarted()) { - - log.getLogEntries().stream().forEach(entry -> post(entry)); - - if (task.getStatus() == DONE) { - printTimeTaken(log); - } - - } - - } - - } - - private synchronized void update() { - var task = TaskManager.getManagerInstance().getSelectedTask(); - - if (task == null) { - return; - } - - var log = task.getLog(); - - if (!log.isStarted()) { - return; - } - - post(log.lastEntry()); - } - - public void clear() { - try { - getDocument().remove(0, getDocument().getLength()); - } catch (BadLocationException e) { - e.printStackTrace(); - } - } - - public ExecutorService getUpdateExecutor() { - return updateExecutor; - } - - @Override - public String describe() { - return "Log_" + TaskManager.getManagerInstance().getSelectedTask().getIdentifier().getValue(); - } - -} diff --git a/src/main/java/pulse/ui/components/TextLogPane.java b/src/main/java/pulse/ui/components/TextLogPane.java new file mode 100644 index 0000000..aaf318a --- /dev/null +++ b/src/main/java/pulse/ui/components/TextLogPane.java @@ -0,0 +1,108 @@ +package pulse.ui.components; + +import static java.lang.System.err; +import static java.time.temporal.ChronoUnit.MILLIS; +import static java.time.temporal.ChronoUnit.SECONDS; +import static javax.swing.text.DefaultCaret.ALWAYS_UPDATE; +import static pulse.tasks.logs.Status.DONE; +import static pulse.ui.Messages.getString; + +import java.io.IOException; +import javax.swing.JComponent; + +import javax.swing.JEditorPane; +import javax.swing.text.BadLocationException; +import javax.swing.text.DefaultCaret; +import javax.swing.text.html.HTMLDocument; +import javax.swing.text.html.HTMLEditorKit; + +import pulse.tasks.TaskManager; +import pulse.tasks.logs.Log; +import pulse.tasks.logs.LogEntry; + +@SuppressWarnings("serial") +public class TextLogPane extends AbstractLogger { + + private final JEditorPane editor; + + public TextLogPane() { + editor = new JEditorPane(); + editor.setContentType("text/html"); + editor.setEditable(false); + ( (DefaultCaret) editor.getCaret() ).setUpdatePolicy(ALWAYS_UPDATE); + } + + @Override + public void post(LogEntry logEntry) { + post(logEntry.toString()); + } + + @Override + public void post(String text) { + + final var doc = (HTMLDocument) editor.getDocument(); + final var kit = (HTMLEditorKit) editor.getEditorKit(); + try { + kit.insertHTML(doc, doc.getLength(), text, 0, 0, null); + } catch (BadLocationException e) { + err.println(getString("LogPane.InsertError")); //$NON-NLS-1$ + } catch (IOException e) { + err.println(getString("LogPane.PrintError")); //$NON-NLS-1$ + } + + } + + + public void printTimeTaken(Log log) { + var time = log.timeTaken(); + var sb = new StringBuilder(); + sb.append(getString("LogPane.TimeTaken")); //$NON-NLS-1$ + sb.append(time[0]).append(getString("LogPane.Seconds")); //$NON-NLS-1$ + sb.append(time[1]).append(getString("LogPane.Milliseconds")); //$NON-NLS-1$ + post(sb.toString()); + } + + @Override + public void postAll() { + clear(); + + var task = TaskManager.getManagerInstance().getSelectedTask(); + + if (task != null) { + + var log = task.getLog(); + + if (log.isStarted()) { + + log.getLogEntries().stream().forEach(entry -> post(entry)); + + if (task.getStatus() == DONE) { + printTimeTaken(log); + } + + } + + } + + } + + @Override + public void clear() { + try { + editor.getDocument().remove(0, editor.getDocument().getLength()); + } catch (BadLocationException e) { + e.printStackTrace(); + } + } + + @Override + public JComponent getGUIComponent() { + return editor; + } + + @Override + public boolean isEmpty() { + return editor.getDocument().getLength() < 1; + } + +} \ No newline at end of file diff --git a/src/main/java/pulse/ui/frames/LogFrame.java b/src/main/java/pulse/ui/frames/LogFrame.java index 6410fb7..4f45590 100644 --- a/src/main/java/pulse/ui/frames/LogFrame.java +++ b/src/main/java/pulse/ui/frames/LogFrame.java @@ -22,14 +22,15 @@ import pulse.tasks.listeners.LogEntryListener; import pulse.tasks.logs.Log; import pulse.tasks.logs.LogEntry; -import pulse.ui.components.LogPane; +import pulse.ui.components.AbstractLogger; +import pulse.ui.components.TextLogPane; import pulse.ui.components.panels.LogToolbar; import pulse.ui.components.panels.SystemPanel; @SuppressWarnings("serial") public class LogFrame extends JInternalFrame { - private LogPane logTextPane; + private AbstractLogger logger; public LogFrame() { super("Log", true, false, true, true); @@ -39,9 +40,9 @@ public LogFrame() { } private void initComponents() { - logTextPane = new LogPane(); + logger = new TextLogPane(); var logScroller = new JScrollPane(); - logScroller.setViewportView(logTextPane); + logScroller.setViewportView(logger.getGUIComponent()); getContentPane().setLayout(new BorderLayout()); getContentPane().add(logScroller, CENTER); @@ -54,8 +55,8 @@ private void initComponents() { var logToolbar = new LogToolbar(); logToolbar.addLogExportListener(() -> { - if (logTextPane.getDocument().getLength() > 0) { - askToExport(logTextPane, (JFrame) getWindowAncestor(this), + if (!logger.isEmpty()) { + askToExport(logger, (JFrame) getWindowAncestor(this), getString("LogToolBar.FileFormatDescriptor")); } }); @@ -65,7 +66,7 @@ private void initComponents() { private void scheduleLogEvents() { var instance = TaskManager.getManagerInstance(); - instance.addSelectionListener(e -> logTextPane.printAll()); + instance.addSelectionListener(e -> logger.postAll()); instance.addTaskRepositoryListener(event -> { if (event.getState() != TASK_ADDED) { @@ -81,13 +82,12 @@ public void onLogFinished(Log log) { if (instance.getSelectedTask() == task) { try { - logTextPane.getUpdateExecutor().awaitTermination(10, MILLISECONDS); + logger.getUpdateExecutor().awaitTermination(10, MILLISECONDS); } catch (InterruptedException e) { err.println("Log not finished in time"); - e.printStackTrace(); } - logTextPane.printTimeTaken(log); + logger.printTimeTaken(log); } } @@ -95,7 +95,7 @@ public void onLogFinished(Log log) { @Override public void onNewEntry(LogEntry e) { if (instance.getSelectedTask() == task) { - logTextPane.callUpdate(); + logger.callUpdate(); } } @@ -105,8 +105,8 @@ public void onNewEntry(LogEntry e) { }); } - public LogPane getLogTextPane() { - return logTextPane; + public AbstractLogger getLogger() { + return logger; } -} +} \ No newline at end of file diff --git a/src/main/java/pulse/ui/frames/TaskControlFrame.java b/src/main/java/pulse/ui/frames/TaskControlFrame.java index f04f050..880e10d 100644 --- a/src/main/java/pulse/ui/frames/TaskControlFrame.java +++ b/src/main/java/pulse/ui/frames/TaskControlFrame.java @@ -157,13 +157,13 @@ public void onRemoveRequest() { @Override public void onClearRequest() { - logFrame.getLogTextPane().clear(); + logFrame.getLogger().clear(); resultsFrame.getResultTable().clear(); } @Override public void onResetRequest() { - logFrame.getLogTextPane().clear(); + logFrame.getLogger().clear(); resultsFrame.getResultTable().removeAll(); } From 0434e4cb0b25d7104b646b6f09023e06808542f7 Mon Sep 17 00:00:00 2001 From: kotik-coder Date: Sun, 25 Dec 2022 12:22:38 +0300 Subject: [PATCH 3/6] Moved AbstractLogger to logs package --- .../pulse/{ui/components => tasks/logs}/AbstractLogger.java | 4 +--- src/main/java/pulse/ui/components/TextLogPane.java | 1 + src/main/java/pulse/ui/frames/LogFrame.java | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) rename src/main/java/pulse/{ui/components => tasks/logs}/AbstractLogger.java (93%) diff --git a/src/main/java/pulse/ui/components/AbstractLogger.java b/src/main/java/pulse/tasks/logs/AbstractLogger.java similarity index 93% rename from src/main/java/pulse/ui/components/AbstractLogger.java rename to src/main/java/pulse/tasks/logs/AbstractLogger.java index 916e678..bed70f7 100644 --- a/src/main/java/pulse/ui/components/AbstractLogger.java +++ b/src/main/java/pulse/tasks/logs/AbstractLogger.java @@ -1,11 +1,9 @@ -package pulse.ui.components; +package pulse.tasks.logs; import java.util.concurrent.ExecutorService; import static java.util.concurrent.Executors.newSingleThreadExecutor; import javax.swing.JComponent; import pulse.tasks.TaskManager; -import pulse.tasks.logs.Log; -import pulse.tasks.logs.LogEntry; import pulse.util.Descriptive; public abstract class AbstractLogger implements Descriptive { diff --git a/src/main/java/pulse/ui/components/TextLogPane.java b/src/main/java/pulse/ui/components/TextLogPane.java index aaf318a..e8f18ff 100644 --- a/src/main/java/pulse/ui/components/TextLogPane.java +++ b/src/main/java/pulse/ui/components/TextLogPane.java @@ -1,5 +1,6 @@ package pulse.ui.components; +import pulse.tasks.logs.AbstractLogger; import static java.lang.System.err; import static java.time.temporal.ChronoUnit.MILLIS; import static java.time.temporal.ChronoUnit.SECONDS; diff --git a/src/main/java/pulse/ui/frames/LogFrame.java b/src/main/java/pulse/ui/frames/LogFrame.java index 4f45590..996f1f3 100644 --- a/src/main/java/pulse/ui/frames/LogFrame.java +++ b/src/main/java/pulse/ui/frames/LogFrame.java @@ -22,7 +22,7 @@ import pulse.tasks.listeners.LogEntryListener; import pulse.tasks.logs.Log; import pulse.tasks.logs.LogEntry; -import pulse.ui.components.AbstractLogger; +import pulse.tasks.logs.AbstractLogger; import pulse.ui.components.TextLogPane; import pulse.ui.components.panels.LogToolbar; import pulse.ui.components.panels.SystemPanel; From 18b3dd0e401be9a77a09cf4384609f847a7035bf Mon Sep 17 00:00:00 2001 From: kotik-coder Date: Sat, 31 Dec 2022 18:59:57 +0300 Subject: [PATCH 4/6] Graphical log pane & new LAF - New Look and Feel (faster and better-looking) - Graphical logs - No "verbose" log option anymore ("on" by default in text mode) - Fixed glitches with GUI and task execution --- .../listeners/{LogExportListener.java => LogListener.java} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/main/java/pulse/ui/components/listeners/{LogExportListener.java => LogListener.java} (100%) diff --git a/src/main/java/pulse/ui/components/listeners/LogExportListener.java b/src/main/java/pulse/ui/components/listeners/LogListener.java similarity index 100% rename from src/main/java/pulse/ui/components/listeners/LogExportListener.java rename to src/main/java/pulse/ui/components/listeners/LogListener.java From a92b260eb75f56b9553a95fed552d04309f2417d Mon Sep 17 00:00:00 2001 From: kotik-coder Date: Sat, 31 Dec 2022 18:59:57 +0300 Subject: [PATCH 5/6] Graphical log pane & new LAF - New Look and Feel (faster and better-looking) - Graphical logs - No "verbose" log option anymore ("on" by default in text mode) - Fixed glitches with GUI and task execution --- .../java/pulse/math/ParameterIdentifier.java | 31 ++- .../properties/NumericPropertyKeyword.java | 8 +- src/main/java/pulse/search/GeneralTask.java | 1 + .../pulse/search/direction/ComplexPath.java | 1 - .../direction/CompositePathOptimiser.java | 4 +- .../search/direction/IterativeState.java | 1 + .../pulse/search/direction/LMOptimiser.java | 4 +- .../direction/pso/ParticleSwarmOptimiser.java | 3 +- src/main/java/pulse/tasks/Calculation.java | 15 +- src/main/java/pulse/tasks/SearchTask.java | 7 +- .../java/pulse/tasks/logs/AbstractLogger.java | 66 ++++-- .../java/pulse/tasks/logs/DataLogEntry.java | 20 +- src/main/java/pulse/tasks/logs/Log.java | 18 +- .../java/pulse/tasks/processing/Buffer.java | 18 +- src/main/java/pulse/ui/ColorGenerator.java | 45 ++++ src/main/java/pulse/ui/Launcher.java | 8 +- .../java/pulse/ui/components/AuxPlotter.java | 87 +++++--- src/main/java/pulse/ui/components/Chart.java | 19 +- .../pulse/ui/components/GraphicalLogPane.java | 92 ++++++++ .../java/pulse/ui/components/LogChart.java | 199 ++++++++++++++++++ .../java/pulse/ui/components/PulseChart.java | 7 +- .../pulse/ui/components/ResidualsChart.java | 15 +- .../java/pulse/ui/components/TaskBox.java | 4 +- .../java/pulse/ui/components/TextLogPane.java | 34 +-- .../ui/components/buttons/LoaderButton.java | 27 ++- .../controllers/AccessibleTableRenderer.java | 2 - .../controllers/InstanceCellEditor.java | 14 +- .../controllers/NumericPropertyRenderer.java | 17 +- .../controllers/ProblemCellRenderer.java | 8 +- .../controllers/SearchListRenderer.java | 4 +- .../controllers/TaskTableRenderer.java | 3 - .../listeners/LogExportListener.java | 7 - .../ui/components/listeners/LogListener.java | 8 + .../ui/components/panels/ChartToolbar.java | 2 +- .../components/panels/DoubleTablePanel.java | 17 -- .../ui/components/panels/LogToolbar.java | 31 +-- .../ui/components/panels/ModelToolbar.java | 2 +- .../ui/components/panels/ProblemToolbar.java | 1 - src/main/java/pulse/ui/frames/LogFrame.java | 48 +++-- .../java/pulse/ui/frames/PreviewFrame.java | 10 +- .../pulse/ui/frames/SearchOptionsFrame.java | 2 - .../pulse/ui/frames/TaskControlFrame.java | 20 +- 42 files changed, 665 insertions(+), 265 deletions(-) create mode 100644 src/main/java/pulse/ui/ColorGenerator.java create mode 100644 src/main/java/pulse/ui/components/GraphicalLogPane.java create mode 100644 src/main/java/pulse/ui/components/LogChart.java delete mode 100644 src/main/java/pulse/ui/components/listeners/LogExportListener.java create mode 100644 src/main/java/pulse/ui/components/listeners/LogListener.java diff --git a/src/main/java/pulse/math/ParameterIdentifier.java b/src/main/java/pulse/math/ParameterIdentifier.java index b96847d..3fb7bc4 100644 --- a/src/main/java/pulse/math/ParameterIdentifier.java +++ b/src/main/java/pulse/math/ParameterIdentifier.java @@ -1,5 +1,6 @@ package pulse.math; +import java.util.Objects; import pulse.properties.NumericPropertyKeyword; public class ParameterIdentifier { @@ -15,6 +16,14 @@ public ParameterIdentifier(NumericPropertyKeyword keyword, int index) { public ParameterIdentifier(NumericPropertyKeyword keyword) { this(keyword, 0); } + + @Override + public int hashCode() { + int hash = 7; + hash = 29 * hash + Objects.hashCode(this.keyword); + hash = 29 * hash + this.index; + return hash; + } public ParameterIdentifier(int index) { this.index = index; @@ -30,24 +39,28 @@ public int getIndex() { @Override public boolean equals(Object id) { - if(!id.getClass().equals(ParameterIdentifier.class)) { + if(id.getClass() == null) { return false; } - var pid = (ParameterIdentifier) id; - - boolean result = true; + var classA = id.getClass(); + var classB = this.getClass(); - if(keyword != pid.keyword || index != pid.index) - result = false; - - return result; + if(classA != classB) { + return false; + } + var pid = (ParameterIdentifier) id; + return keyword == pid.keyword && Math.abs(index - pid.index) < 1; } @Override public String toString() { - return keyword + " # " + index; + StringBuilder sb = new StringBuilder("").append(keyword); + if(index > 0) { + sb.append(" # ").append(index); + } + return sb.toString(); } } \ No newline at end of file diff --git a/src/main/java/pulse/properties/NumericPropertyKeyword.java b/src/main/java/pulse/properties/NumericPropertyKeyword.java index a657171..36b1666 100644 --- a/src/main/java/pulse/properties/NumericPropertyKeyword.java +++ b/src/main/java/pulse/properties/NumericPropertyKeyword.java @@ -390,7 +390,13 @@ public enum NumericPropertyKeyword { * Heat loss for the gas in the 2T-model. */ - HEAT_LOSS_GAS; + HEAT_LOSS_GAS, + + /** + * Value of objective function. + */ + + OBJECTIVE_FUNCTION; public static Optional findAny(String key) { return Arrays.asList(values()).stream().filter(keys -> keys.toString().equalsIgnoreCase(key)).findAny(); diff --git a/src/main/java/pulse/search/GeneralTask.java b/src/main/java/pulse/search/GeneralTask.java index 44c64ac..3bd42d0 100644 --- a/src/main/java/pulse/search/GeneralTask.java +++ b/src/main/java/pulse/search/GeneralTask.java @@ -64,6 +64,7 @@ public GeneralTask() { @Override public void run() { setDefaultOptimiser(); + best = null; setIterativeState( optimiser.initState(this) ); var errorTolerance = (double) optimiser.getErrorTolerance().getValue(); diff --git a/src/main/java/pulse/search/direction/ComplexPath.java b/src/main/java/pulse/search/direction/ComplexPath.java index e6fc8a3..bfe1ef8 100644 --- a/src/main/java/pulse/search/direction/ComplexPath.java +++ b/src/main/java/pulse/search/direction/ComplexPath.java @@ -4,7 +4,6 @@ import pulse.math.linear.SquareMatrix; import pulse.search.GeneralTask; -import pulse.tasks.SearchTask; /** *

diff --git a/src/main/java/pulse/search/direction/CompositePathOptimiser.java b/src/main/java/pulse/search/direction/CompositePathOptimiser.java index 7976646..48766c5 100644 --- a/src/main/java/pulse/search/direction/CompositePathOptimiser.java +++ b/src/main/java/pulse/search/direction/CompositePathOptimiser.java @@ -61,6 +61,7 @@ public boolean iteration(GeneralTask task) throws SolverException { } else { double initialCost = task.getResponse().objectiveFunction(task); + p.setCost(initialCost); var parameters = task.searchVector(); p.setParameters(parameters); // store current parameters @@ -94,6 +95,7 @@ public boolean iteration(GeneralTask task) throws SolverException { task.storeState(); p.resetFailedAttempts(); this.prepare(task); // update gradients, Hessians, etc. -> for the next step, [k + 1] + p.setCost(newCost); p.incrementStep(); // increment the counter of successful steps } @@ -142,4 +144,4 @@ public GradientGuidedPath initState(GeneralTask t) { return new ComplexPath(t); } -} +} \ No newline at end of file diff --git a/src/main/java/pulse/search/direction/IterativeState.java b/src/main/java/pulse/search/direction/IterativeState.java index 05fbaca..9045d46 100644 --- a/src/main/java/pulse/search/direction/IterativeState.java +++ b/src/main/java/pulse/search/direction/IterativeState.java @@ -41,6 +41,7 @@ public void setCost(double cost) { public void reset() { iteration = 0; + setCost(Double.POSITIVE_INFINITY); } public NumericProperty getIteration() { diff --git a/src/main/java/pulse/search/direction/LMOptimiser.java b/src/main/java/pulse/search/direction/LMOptimiser.java index 4c3247a..86ee9e4 100644 --- a/src/main/java/pulse/search/direction/LMOptimiser.java +++ b/src/main/java/pulse/search/direction/LMOptimiser.java @@ -69,6 +69,7 @@ public boolean iteration(GeneralTask task) throws SolverException { } else { double initialCost = task.objectiveFunction(); + p.setCost(initialCost); var parameters = task.searchVector(); p.setParameters(parameters); // store current parameters @@ -88,7 +89,7 @@ public boolean iteration(GeneralTask task) throws SolverException { parameters, candidate)); // assign new parameters double newCost = task.objectiveFunction(); // calculate the sum of squared residuals - + /* * Delayed gratification */ @@ -103,6 +104,7 @@ public boolean iteration(GeneralTask task) throws SolverException { p.resetFailedAttempts(); p.setLambda(p.getLambda() / 3.0); p.setComputeJacobian(false); + p.setCost(newCost); p.incrementStep(); // increment the counter of successful steps } diff --git a/src/main/java/pulse/search/direction/pso/ParticleSwarmOptimiser.java b/src/main/java/pulse/search/direction/pso/ParticleSwarmOptimiser.java index 6f39f8b..323dfab 100644 --- a/src/main/java/pulse/search/direction/pso/ParticleSwarmOptimiser.java +++ b/src/main/java/pulse/search/direction/pso/ParticleSwarmOptimiser.java @@ -46,7 +46,8 @@ public boolean iteration(GeneralTask task) throws SolverException { swarmState.incrementStep(); task.assign(swarmState.getBestSoFar().getPosition()); - task.objectiveFunction(); + double cost = task.objectiveFunction(); + swarmState.setCost(cost); return true; } diff --git a/src/main/java/pulse/tasks/Calculation.java b/src/main/java/pulse/tasks/Calculation.java index 51bc76e..5c673d3 100644 --- a/src/main/java/pulse/tasks/Calculation.java +++ b/src/main/java/pulse/tasks/Calculation.java @@ -34,6 +34,7 @@ import pulse.util.InstanceDescriptor; import pulse.util.PropertyEvent; import pulse.util.PropertyHolder; +import pulse.util.UpwardsNavigable; public class Calculation extends PropertyHolder implements Comparable, Response { @@ -78,14 +79,14 @@ public Calculation(Calculation c) { instanceDescriptor.addListener(() -> initModelCriterion(rs)); } - public void assumeOwnership() { - problem.setParent(this); - scheme.setParent(this); - rs.setParent(this); - os.setParent(this); - result.setParent(this); + public void conformTo(UpwardsNavigable owner) { + problem.setParent(owner); + scheme.setParent(owner); + rs.setParent(owner); + os.setParent(owner); + result.setParent(owner); } - + public void clear() { this.status = INCOMPLETE; this.problem = null; diff --git a/src/main/java/pulse/tasks/SearchTask.java b/src/main/java/pulse/tasks/SearchTask.java index bc8277a..91633c8 100644 --- a/src/main/java/pulse/tasks/SearchTask.java +++ b/src/main/java/pulse/tasks/SearchTask.java @@ -280,8 +280,7 @@ public void run() { } current.getProblem().parameterListChanged(); // get updated list of parameters - setDefaultOptimiser(); - + super.run(); } @@ -333,9 +332,11 @@ public void storeCalculation() { } public void switchTo(Calculation calc) { + current.setParent(null); + current.conformTo(null); current = new Calculation(calc); - current.assumeOwnership(); current.setParent(this); + calc.conformTo(calc); current.setStatus(Status.READY); var e = new TaskRepositoryEvent(TaskRepositoryEvent.State.TASK_MODEL_SWITCH, this.getIdentifier()); fireRepositoryEvent(e); diff --git a/src/main/java/pulse/tasks/logs/AbstractLogger.java b/src/main/java/pulse/tasks/logs/AbstractLogger.java index bed70f7..f128837 100644 --- a/src/main/java/pulse/tasks/logs/AbstractLogger.java +++ b/src/main/java/pulse/tasks/logs/AbstractLogger.java @@ -4,12 +4,17 @@ import static java.util.concurrent.Executors.newSingleThreadExecutor; import javax.swing.JComponent; import pulse.tasks.TaskManager; +import static pulse.tasks.logs.Status.DONE; import pulse.util.Descriptive; public abstract class AbstractLogger implements Descriptive { - private final ExecutorService updateExecutor = newSingleThreadExecutor(); - + private final ExecutorService updateExecutor; + + public AbstractLogger() { + updateExecutor = newSingleThreadExecutor(); + } + public synchronized void update() { var task = TaskManager.getManagerInstance().getSelectedTask(); @@ -19,32 +24,59 @@ public synchronized void update() { var log = task.getLog(); - if (!log.isStarted()) { - return; + if (log.isStarted()) { + post(log.lastEntry()); } - - post(log.lastEntry()); + } - + public ExecutorService getUpdateExecutor() { return updateExecutor; } - + public synchronized void callUpdate() { updateExecutor.submit(() -> update()); } - + + public void postAll() { + clear(); + + var task = TaskManager.getManagerInstance().getSelectedTask(); + + if (task != null) { + + var log = task.getLog(); + + if (log.isStarted()) { + + log.getLogEntries().stream().forEach(entry -> post(entry)); + + if (task.getStatus() == DONE) { + printTimeTaken(log); + } + + } + + } + + } + + @Override + public String describe() { + var task = TaskManager.getManagerInstance().getSelectedTask(); + return "Log" + (task == null ? "" : "_" + task.getIdentifier().getValue()); + } + public abstract JComponent getGUIComponent(); + public abstract void printTimeTaken(Log log); + public abstract void post(LogEntry logEntry); + public abstract void post(String text); - public abstract void postAll(); + public abstract void clear(); + public abstract boolean isEmpty(); - - @Override - public String describe() { - return "Log_" + TaskManager.getManagerInstance().getSelectedTask().getIdentifier().getValue(); - } - -} \ No newline at end of file + +} diff --git a/src/main/java/pulse/tasks/logs/DataLogEntry.java b/src/main/java/pulse/tasks/logs/DataLogEntry.java index 4e17c87..c3c5ab3 100644 --- a/src/main/java/pulse/tasks/logs/DataLogEntry.java +++ b/src/main/java/pulse/tasks/logs/DataLogEntry.java @@ -2,10 +2,11 @@ import java.lang.reflect.InvocationTargetException; import java.util.List; -import pulse.Response; import pulse.math.Parameter; import pulse.math.ParameterIdentifier; import pulse.properties.NumericProperties; +import static pulse.properties.NumericProperties.def; +import static pulse.properties.NumericPropertyKeyword.OBJECTIVE_FUNCTION; import pulse.tasks.SearchTask; import pulse.tasks.TaskManager; @@ -55,10 +56,19 @@ public DataLogEntry(SearchTask task) { private void fill() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { var task = TaskManager.getManagerInstance().getTask(getIdentifier()); entry = task.searchVector().getParameters(); + //iteration var pval = task.getIterativeState().getIteration(); var pid = new Parameter(new ParameterIdentifier(pval.getType())); - pid.setValue( (int) pval.getValue() ); + pid.setValue((int) pval.getValue()); + //cost + var costId = new Parameter(new ParameterIdentifier(OBJECTIVE_FUNCTION)); + var costval = task.getIterativeState().getCost(); + // entry.add(0, pid); + if (NumericProperties.isValueSensible(def(OBJECTIVE_FUNCTION), costval)) { + costId.setValue(costval); + entry.add(costId); + } } public List getData() { @@ -91,15 +101,15 @@ public String toString() { var def = NumericProperties.def(p.getIdentifier().getKeyword()); boolean b = def.getValue() instanceof Integer; Number val; - if(b) { + if (b) { val = (int) Math.rint(p.getApparentValue()); - } else{ + } else { val = p.getApparentValue(); } def.setValue(val); sb.append(def.getAbbreviation(false)); int index = p.getIdentifier().getIndex(); - if(index > 0) { + if (index > 0) { sb.append(" - ").append(index); } sb.append("<"); diff --git a/src/main/java/pulse/tasks/logs/Log.java b/src/main/java/pulse/tasks/logs/Log.java index 1153499..21a32df 100644 --- a/src/main/java/pulse/tasks/logs/Log.java +++ b/src/main/java/pulse/tasks/logs/Log.java @@ -26,7 +26,7 @@ public class Log extends Group { private LocalTime end; private final Identifier id; private final List listeners; - private static boolean verbose = false; + private static boolean graphical = true; /** * Creates a {@code Log} for this {@code task} that will automatically store @@ -50,7 +50,7 @@ public Log(SearchTask task) { /** * Do these actions each time data has been collected for this task. */ - if (task.getStatus() != Status.INCOMPLETE && verbose) { + if (task.getStatus() != Status.INCOMPLETE) { logEntries.add(le); notifyListeners(le); } @@ -107,7 +107,7 @@ public final Identifier getIdentifier() { * @return {@code true} if the start time is not {@code null} */ public boolean isStarted() { - return start != null; + return logEntries.size() > 0; } /** @@ -175,18 +175,18 @@ public LogEntry lastEntry() { * * @return {@code true} if the verbose flag is on */ - public static boolean isVerbose() { - return verbose; + public static boolean isGraphicalLog() { + return graphical; } /** * Sets the verbose flag to {@code verbose} * * @param verbose the new value of the flag - * @see isVerbose() + * @see #isGraphicalLog() */ - public static void setVerbose(boolean verbose) { - Log.verbose = verbose; + public static void setGraphicalLog(boolean verbose) { + Log.graphical = verbose; } /** @@ -200,4 +200,4 @@ public long[] timeTaken() { return new long[] {seconds, ms}; } -} +} \ No newline at end of file diff --git a/src/main/java/pulse/tasks/processing/Buffer.java b/src/main/java/pulse/tasks/processing/Buffer.java index e3a1684..90ef5bd 100644 --- a/src/main/java/pulse/tasks/processing/Buffer.java +++ b/src/main/java/pulse/tasks/processing/Buffer.java @@ -8,10 +8,14 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.stream.Stream; +import org.apache.commons.math3.stat.regression.SimpleRegression; +import pulse.math.ParameterIdentifier; import pulse.math.ParameterVector; import pulse.properties.NumericProperty; import pulse.properties.NumericPropertyKeyword; +import static pulse.properties.NumericPropertyKeyword.OBJECTIVE_FUNCTION; import pulse.properties.Property; import pulse.search.GeneralTask; import pulse.util.PropertyHolder; @@ -48,7 +52,7 @@ public ParameterVector[] getData() { /* * Re-inits the storage. */ - public void init() { + public final void init() { this.data = new ParameterVector[size]; statistic = new double[size]; } @@ -105,11 +109,17 @@ public double average(NumericPropertyKeyword index) { double av = 0; - for (ParameterVector v : data) { - av += v.getParameterValue(index, 0); + if (index == OBJECTIVE_FUNCTION) { + av = Arrays.stream(statistic).average().getAsDouble(); + } else { + + for (ParameterVector v : data) { + av += v.getParameterValue(index, 0); + } + av /= data.length; } - return av / data.length; + return av; } diff --git a/src/main/java/pulse/ui/ColorGenerator.java b/src/main/java/pulse/ui/ColorGenerator.java new file mode 100644 index 0000000..2e2388d --- /dev/null +++ b/src/main/java/pulse/ui/ColorGenerator.java @@ -0,0 +1,45 @@ +package pulse.ui; + +import java.awt.Color; +import static java.awt.Color.BLUE; +import static java.awt.Color.GREEN; +import static java.awt.Color.RED; +import java.util.ArrayList; +import java.util.Collections; + +public class ColorGenerator { + + private Color a, b, c; + + public ColorGenerator() { + a = RED; + b = GREEN; + c = BLUE; + } + + public Color[] random(int number) { + var list = new ArrayList(); + for(int i = 0; i < number; i++) { + list.add(sample(i/(double)(number - 1))); + } + //Collections.shuffle(list); + return list.toArray(new Color[list.size()]); + } + + public Color sample(double seed) { + return seed < 0.5 ? + mix(a, b, (float) (seed*2)) + : mix(b, c,(float)((seed-0.5)*2)); + } + + private static Color mix(Color a, Color b, float ratio) { + float[] aRgb = a.getRGBComponents(null); + float[] bRgb = b.getRGBComponents(null); + float[] cRgb = new float[3]; + for(int i = 0; i < cRgb.length; i++) { + cRgb[i] = aRgb[i] * (1.0f - ratio) + bRgb[i] * ratio; + } + return new Color(cRgb[0], cRgb[1], cRgb[2]); + } + +} \ No newline at end of file diff --git a/src/main/java/pulse/ui/Launcher.java b/src/main/java/pulse/ui/Launcher.java index de97832..ee238ac 100644 --- a/src/main/java/pulse/ui/Launcher.java +++ b/src/main/java/pulse/ui/Launcher.java @@ -17,8 +17,7 @@ import javax.swing.JOptionPane; import javax.swing.UIManager; -import com.alee.laf.WebLookAndFeel; -import com.alee.skin.dark.WebDarkSkin; +import com.formdev.flatlaf.FlatDarkLaf; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; @@ -65,9 +64,10 @@ public static void main(String[] args) { splashScreen(); - WebLookAndFeel.install(WebDarkSkin.class); + //WebLookAndFeel.install(WebDarkSkin.class); + FlatDarkLaf.setup(); try { - UIManager.setLookAndFeel(new WebLookAndFeel()); + UIManager.setLookAndFeel(new FlatDarkLaf()); } catch (Exception ex) { System.err.println("Failed to initialize LaF"); } diff --git a/src/main/java/pulse/ui/components/AuxPlotter.java b/src/main/java/pulse/ui/components/AuxPlotter.java index 8150c4c..602a3d6 100644 --- a/src/main/java/pulse/ui/components/AuxPlotter.java +++ b/src/main/java/pulse/ui/components/AuxPlotter.java @@ -1,58 +1,85 @@ package pulse.ui.components; +import java.awt.Color; import java.awt.Font; +import javax.swing.JLabel; import javax.swing.UIManager; +import org.jfree.chart.ChartFactory; import org.jfree.chart.ChartPanel; import org.jfree.chart.JFreeChart; +import org.jfree.chart.plot.CombinedDomainXYPlot; +import static org.jfree.chart.plot.PlotOrientation.VERTICAL; import org.jfree.chart.plot.XYPlot; public abstract class AuxPlotter { - + private ChartPanel chartPanel; private JFreeChart chart; private XYPlot plot; - + + public AuxPlotter() { + //empty + } + public AuxPlotter(String xLabel, String yLabel) { - createChart(xLabel, yLabel); - chart.setBackgroundPaint(UIManager.getColor("Panel.background")); + setChart( ChartFactory.createScatterPlot("", xLabel, yLabel, null, VERTICAL, true, true, false) ); + + setPlot( chart.getXYPlot() ); + chart.removeLegend(); - plot = chart.getXYPlot(); setFonts(); - - chart.removeLegend(); - chartPanel = new ChartPanel(chart); } - - public void setFonts() { - var fontLabel = new Font("Arial", Font.PLAIN, 20); - var fontTicks = new Font("Arial", Font.PLAIN, 16); - var plot = getPlot(); - plot.getDomainAxis().setLabelFont(fontLabel); - plot.getDomainAxis().setTickLabelFont(fontTicks); - plot.getRangeAxis().setLabelFont(fontLabel); - plot.getRangeAxis().setTickLabelFont(fontTicks); + + public final void setFonts() { + var jlabel = new JLabel(); + var label = jlabel.getFont().deriveFont(20f); + var ticks = jlabel.getFont().deriveFont(16f); + chart.getTitle().setFont(jlabel.getFont().deriveFont(20f)); + + if (plot instanceof CombinedDomainXYPlot) { + var combinedPlot = (CombinedDomainXYPlot) plot; + combinedPlot.getSubplots().stream().forEach(sp -> setFontsForPlot((XYPlot)sp, label, ticks)); + } else { + setFontsForPlot(plot, label, ticks); + } + } - - public abstract void createChart(String xLabel, String yLabel); - + + private void setFontsForPlot(XYPlot p, Font label, Font ticks) { + var foreColor = UIManager.getColor("Label.foreground"); + var domainAxis = p.getDomainAxis(); + Chart.setAxisFontColor(domainAxis, foreColor); + var rangeAxis = p.getRangeAxis(); + Chart.setAxisFontColor(rangeAxis, foreColor); + } + public abstract void plot(T t); - - public ChartPanel getChartPanel() { + + public final ChartPanel getChartPanel() { return chartPanel; } - - public JFreeChart getChart() { + + public final JFreeChart getChart() { return chart; } - - public XYPlot getPlot() { + + public final XYPlot getPlot() { return plot; } - - public void setChart(JFreeChart chart) { + + public final void setPlot(XYPlot plot) { + this.plot = plot; + plot.setBackgroundPaint(chart.getBackgroundPaint()); + } + + public final void setChart(JFreeChart chart) { this.chart = chart; + var color = UIManager.getLookAndFeelDefaults().getColor("TextPane.background"); + chart.setBackgroundPaint(color); + chartPanel = new ChartPanel(chart); + this.plot = chart.getXYPlot(); } - -} + +} \ No newline at end of file diff --git a/src/main/java/pulse/ui/components/Chart.java b/src/main/java/pulse/ui/components/Chart.java index 68dda2d..7fe372e 100644 --- a/src/main/java/pulse/ui/components/Chart.java +++ b/src/main/java/pulse/ui/components/Chart.java @@ -26,6 +26,7 @@ import org.jfree.chart.ChartPanel; import org.jfree.chart.JFreeChart; import org.jfree.chart.annotations.XYTitleAnnotation; +import org.jfree.chart.axis.Axis; import org.jfree.chart.block.BlockBorder; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; @@ -81,7 +82,7 @@ public Chart() { final TaskManager instance = TaskManager.getManagerInstance(); chart.removeLegend(); - chart.setBackgroundPaint(UIManager.getColor("Panel.background")); + chart.setBackgroundPaint(UIManager.getColor("TextPane.background")); chartPanel = new ChartPanel(chart) { @Override @@ -146,16 +147,18 @@ public double xCoord(MouseEvent e) { } private void setFonts() { - var fontLabel = new Font("Arial", Font.PLAIN, 20); - var fontTicks = new Font("Arial", Font.PLAIN, 14); - plot.getDomainAxis().setLabelFont(fontLabel); - plot.getDomainAxis().setTickLabelFont(fontTicks); - plot.getRangeAxis().setLabelFont(fontLabel); - plot.getRangeAxis().setTickLabelFont(fontTicks); + var foreColor = UIManager.getColor("Label.foreground"); + setAxisFontColor(plot.getDomainAxis(), foreColor); + setAxisFontColor(plot.getRangeAxis(), foreColor); + } + + public static void setAxisFontColor(Axis axis, Color color) { + axis.setLabelPaint(color); + axis.setTickLabelPaint(color); } private void setBackgroundAndGrid() { - // plot.setBackgroundPaint(UIManager.getColor("Panel.background")); + plot.setBackgroundPaint(UIManager.getColor("TextPane.background")); plot.setRangeGridlinesVisible(true); plot.setRangeGridlinePaint(GRAY); diff --git a/src/main/java/pulse/ui/components/GraphicalLogPane.java b/src/main/java/pulse/ui/components/GraphicalLogPane.java new file mode 100644 index 0000000..a50e494 --- /dev/null +++ b/src/main/java/pulse/ui/components/GraphicalLogPane.java @@ -0,0 +1,92 @@ +package pulse.ui.components; + +import javax.swing.JComponent; +import static pulse.properties.NumericPropertyKeyword.ITERATION; +import pulse.tasks.TaskManager; +import pulse.tasks.listeners.TaskRepositoryEvent; +import pulse.tasks.logs.AbstractLogger; +import pulse.tasks.logs.DataLogEntry; +import pulse.tasks.logs.Log; +import pulse.tasks.logs.LogEntry; +import static pulse.tasks.logs.Status.DONE; + +@SuppressWarnings("serial") +public class GraphicalLogPane extends AbstractLogger { + + private final LogChart chart; + + public GraphicalLogPane() { + chart = new LogChart(); + TaskManager.getManagerInstance().addTaskRepositoryListener( e -> { + if(e.getState() == TaskRepositoryEvent.State.TASK_SUBMITTED) { + chart.clear(); + } + }); + } + + @Override + public JComponent getGUIComponent() { + return chart.getChartPanel(); + } + + @Override + public void printTimeTaken(Log log) { + long[] time = log.timeTaken(); + StringBuilder sb = new StringBuilder("Finished in "); + sb.append(time[0]).append(" s ").append(time[1]).append(" ms."); + } + + @Override + public void post(LogEntry logEntry) { + if(logEntry instanceof DataLogEntry) { + var dle = (DataLogEntry) logEntry; + double iteration = dle.getData().stream() + .filter(p -> p.getIdentifier().getKeyword() == ITERATION) + .findAny().get().getApparentValue(); + chart.changeAxis(true); + chart.plot((DataLogEntry)logEntry, iteration); + } + } + + @Override + public void postAll() { + clear(); + + var task = TaskManager.getManagerInstance().getSelectedTask(); + + if (task != null) { + + var log = task.getLog(); + + if (log.isStarted()) { + + chart.clear(); + chart.changeAxis(false); + chart.plot(log); + + if (task.getStatus() == DONE) { + printTimeTaken(log); + } + + } + + } + + } + + @Override + public void post(String text) { + //not supported + } + + @Override + public void clear() { + chart.clear(); + } + + @Override + public boolean isEmpty() { + return false; + } + +} \ No newline at end of file diff --git a/src/main/java/pulse/ui/components/LogChart.java b/src/main/java/pulse/ui/components/LogChart.java new file mode 100644 index 0000000..1f180ca --- /dev/null +++ b/src/main/java/pulse/ui/components/LogChart.java @@ -0,0 +1,199 @@ +package pulse.ui.components; + +import static java.util.Objects.requireNonNull; + +import java.awt.BasicStroke; +import java.awt.Color; +import static java.awt.Color.WHITE; +import static java.awt.Color.black; +import java.time.Duration; +import java.util.HashMap; +import java.util.Map; +import javax.swing.SwingUtilities; + +import org.jfree.chart.JFreeChart; +import org.jfree.chart.annotations.XYTitleAnnotation; +import org.jfree.chart.axis.NumberAxis; +import org.jfree.chart.axis.NumberTickUnit; +import org.jfree.chart.block.BlockBorder; +import org.jfree.chart.plot.CombinedDomainXYPlot; +import org.jfree.chart.plot.Plot; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; +import org.jfree.chart.title.LegendTitle; +import org.jfree.chart.ui.RectangleAnchor; +import org.jfree.chart.ui.RectangleEdge; +import org.jfree.data.Range; +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; +import pulse.Response; +import pulse.math.ParameterIdentifier; + +import static pulse.properties.NumericPropertyKeyword.ITERATION; +import pulse.tasks.SearchTask; +import pulse.tasks.TaskManager; +import pulse.tasks.logs.DataLogEntry; +import pulse.tasks.logs.Log; +import pulse.tasks.logs.Status; +import pulse.tasks.processing.Buffer; +import pulse.ui.ColorGenerator; + +public class LogChart extends AuxPlotter { + + private final Map plots; + private Color[] colors; + private static final ColorGenerator cg = new ColorGenerator(); + private Response r; + + public LogChart() { + var plot = new CombinedDomainXYPlot(new NumberAxis("Iteration")); + plot.setGap(10.0); + plot.setOrientation(PlotOrientation.VERTICAL); + var chart = new JFreeChart("", JFreeChart.DEFAULT_TITLE_FONT, plot, true); + setChart(chart); + plots = new HashMap<>(); + getChart().removeLegend(); + } + + public final void clear() { + var p = (CombinedDomainXYPlot) getPlot(); + p.getDomainAxis().setAutoRange(true); + if (p != null) { + plots.values().stream().forEach(pp -> p.remove(pp)); + } + plots.clear(); + colors = new Color[0]; + r = null; + } + + private void setLegendTitle(Plot plot) { + var lt = new LegendTitle(plot); + lt.setBackgroundPaint(new Color(200, 200, 255, 100)); + lt.setFrame(new BlockBorder(black)); + lt.setPosition(RectangleEdge.RIGHT); + var ta = new XYTitleAnnotation(0.0, 0.8, lt, RectangleAnchor.LEFT); + ta.setMaxWidth(0.58); + ((XYPlot) plot).addAnnotation(ta); + } + + public final void add(ParameterIdentifier key, int no) { + var plot = new XYPlot(); + var axis = new NumberAxis(); + axis.setAutoRangeIncludesZero(false); + plot.setRangeAxis(axis); + + plot.setBackgroundPaint(getChart().getBackgroundPaint()); + + plots.put(key, plot); + ((CombinedDomainXYPlot) getPlot()).add(plot); + + var dataset = new XYSeriesCollection(); + var series = new XYSeries(key.toString()); + + dataset.addSeries(series); + dataset.addSeries(new XYSeries("Running average")); + plot.setDataset(dataset); + setLegendTitle(plot); + + setRenderer(plot, colors[no]); + setFonts(); + } + + private void setRenderer(XYPlot plt, Color clr) { + var renderer = new XYLineAndShapeRenderer(true, false); + renderer.setSeriesPaint(0, clr); + renderer.setSeriesPaint(1, WHITE); + var dashed = new BasicStroke(1.0f, BasicStroke.CAP_BUTT, + BasicStroke.JOIN_MITER, 10.0f, new float[]{10.0f}, 0.0f); + renderer.setSeriesStroke(1, dashed); + renderer.setSeriesVisibleInLegend(1, Boolean.FALSE); + plt.setRenderer(renderer); + } + + public void changeAxis(boolean iterationMode) { + var domainAxis = (NumberAxis) getPlot().getDomainAxis(); + domainAxis.setLabel(iterationMode ? "Iteration" : "Time (ms)"); + domainAxis.setAutoRange(!iterationMode); + if(iterationMode) { + domainAxis.setTickUnit(new NumberTickUnit(1)); + } else { + domainAxis.setAutoTickUnitSelection(true); + } + } + + @Override + public void plot(Log l) { + requireNonNull(l); + + l.getLogEntries().stream() + .filter(le -> le instanceof DataLogEntry) + .forEach(d -> plot((DataLogEntry) d, + Duration.between(l.getStart(), d.getTime()).toMillis())); + } + + private static void adjustRange(XYPlot pl, int iteration, int bufSize) { + int lower = (iteration / bufSize) * bufSize; + + var domainAxis = pl.getDomainAxis(); + var r = domainAxis.getRange(); + var newR = new Range(lower, lower + bufSize); + + if (!r.equals(newR) && iteration > lower) { + ((XYPlot) pl).getDomainAxis().setRange(lower, lower + bufSize); + } + } + + public final void plot(DataLogEntry dle, double iterationOrTime) { + requireNonNull(dle); + + var data = dle.getData(); + int size = data.size(); + + if (colors == null || colors.length < size) { + colors = cg.random(size - 1); + } + + SearchTask task = TaskManager.getManagerInstance().getTask(dle.getIdentifier()); + Buffer buf = task.getBuffer(); + final int bufSize = buf.getData().length; + + for (int i = 0, j = 0; i < size; i++) { + var p = data.get(i); + var np = p.getIdentifier(); + + if (np.getKeyword() == ITERATION) { + continue; + } + + double value = p.getApparentValue(); + + if (!plots.containsKey(np)) { + add(np, j++); + } + + Plot pl = plots.get(np); + + var dataset = (XYSeriesCollection) ((XYPlot) pl).getDataset(); + XYSeries series = (XYSeries) dataset.getSeries(0); + series.add(iterationOrTime, value); + + if (task.getStatus() == Status.IN_PROGRESS) { + + XYSeries runningAverage = dataset.getSeries(1); + if (iterationOrTime > buf.getData().length - 1) { + runningAverage.add(iterationOrTime, buf.average(np.getKeyword())); + } + + SwingUtilities.invokeLater(() -> adjustRange((XYPlot)pl, (int)iterationOrTime, bufSize)); + + } else { + var domainAxis = ((XYPlot) pl).getDomainAxis(); + domainAxis.setAutoRange(true); + } + + } + + } + +} \ No newline at end of file diff --git a/src/main/java/pulse/ui/components/PulseChart.java b/src/main/java/pulse/ui/components/PulseChart.java index bb33646..db6e747 100644 --- a/src/main/java/pulse/ui/components/PulseChart.java +++ b/src/main/java/pulse/ui/components/PulseChart.java @@ -41,16 +41,11 @@ private void setRenderer() { getPlot().setRenderer(rendererPulse); } - @Override - public void createChart(String xLabel, String yLabel) { - setChart(ChartFactory.createScatterPlot("", xLabel, yLabel, null, VERTICAL, true, true, false)); - } - private void setLegendTitle() { var plot = getPlot(); var lt = new LegendTitle(plot); lt.setItemFont(new Font("Dialog", PLAIN, 16)); - //lt.setBackgroundPaint(new Color(200, 200, 255, 100)); + lt.setBackgroundPaint(new Color(200, 200, 255, 100)); lt.setFrame(new BlockBorder(black)); lt.setPosition(RectangleEdge.RIGHT); var ta = new XYTitleAnnotation(0.5, 0.2, lt, RectangleAnchor.CENTER); diff --git a/src/main/java/pulse/ui/components/ResidualsChart.java b/src/main/java/pulse/ui/components/ResidualsChart.java index 7a78ee2..7543707 100644 --- a/src/main/java/pulse/ui/components/ResidualsChart.java +++ b/src/main/java/pulse/ui/components/ResidualsChart.java @@ -1,9 +1,8 @@ package pulse.ui.components; import static java.util.Objects.requireNonNull; -import static org.jfree.chart.plot.PlotOrientation.VERTICAL; - import org.jfree.chart.ChartFactory; +import static org.jfree.chart.plot.PlotOrientation.VERTICAL; import org.jfree.data.statistics.HistogramDataset; import org.jfree.data.statistics.HistogramType; @@ -14,15 +13,13 @@ public class ResidualsChart extends AuxPlotter { private int binCount; public ResidualsChart(String xLabel, String yLabel) { - super(xLabel, yLabel); - binCount = 32; - } - - @Override - public void createChart(String xLabel, String yLabel) { setChart(ChartFactory.createHistogram("", xLabel, yLabel, null, VERTICAL, true, true, false)); + setPlot(getChart().getXYPlot()); + getChart().removeLegend(); + setFonts(); + binCount = 32; } - + @Override public void plot(ResidualStatistic stat) { requireNonNull(stat); diff --git a/src/main/java/pulse/ui/components/TaskBox.java b/src/main/java/pulse/ui/components/TaskBox.java index 9be85ed..13b8445 100644 --- a/src/main/java/pulse/ui/components/TaskBox.java +++ b/src/main/java/pulse/ui/components/TaskBox.java @@ -1,6 +1,5 @@ package pulse.ui.components; -import static java.awt.Color.WHITE; import static java.awt.event.ItemEvent.SELECTED; import static pulse.ui.Messages.getString; @@ -46,11 +45,10 @@ public TaskBox() { }); } - public void init() { + public final void init() { setMaximumSize(new Dimension(32767, 24)); setMinimumSize(new Dimension(250, 20)); setToolTipText(getString("TaskBox.DefaultText")); //$NON-NLS-1$ - setBackground(WHITE); } } diff --git a/src/main/java/pulse/ui/components/TextLogPane.java b/src/main/java/pulse/ui/components/TextLogPane.java index e8f18ff..f1b7dfc 100644 --- a/src/main/java/pulse/ui/components/TextLogPane.java +++ b/src/main/java/pulse/ui/components/TextLogPane.java @@ -2,22 +2,19 @@ import pulse.tasks.logs.AbstractLogger; import static java.lang.System.err; -import static java.time.temporal.ChronoUnit.MILLIS; -import static java.time.temporal.ChronoUnit.SECONDS; import static javax.swing.text.DefaultCaret.ALWAYS_UPDATE; -import static pulse.tasks.logs.Status.DONE; import static pulse.ui.Messages.getString; import java.io.IOException; import javax.swing.JComponent; import javax.swing.JEditorPane; +import javax.swing.JScrollPane; import javax.swing.text.BadLocationException; import javax.swing.text.DefaultCaret; import javax.swing.text.html.HTMLDocument; import javax.swing.text.html.HTMLEditorKit; -import pulse.tasks.TaskManager; import pulse.tasks.logs.Log; import pulse.tasks.logs.LogEntry; @@ -25,12 +22,15 @@ public class TextLogPane extends AbstractLogger { private final JEditorPane editor; + private final JScrollPane pane; public TextLogPane() { editor = new JEditorPane(); editor.setContentType("text/html"); editor.setEditable(false); ( (DefaultCaret) editor.getCaret() ).setUpdatePolicy(ALWAYS_UPDATE); + pane = new JScrollPane(); + pane.setViewportView(editor); } @Override @@ -63,30 +63,6 @@ public void printTimeTaken(Log log) { post(sb.toString()); } - @Override - public void postAll() { - clear(); - - var task = TaskManager.getManagerInstance().getSelectedTask(); - - if (task != null) { - - var log = task.getLog(); - - if (log.isStarted()) { - - log.getLogEntries().stream().forEach(entry -> post(entry)); - - if (task.getStatus() == DONE) { - printTimeTaken(log); - } - - } - - } - - } - @Override public void clear() { try { @@ -98,7 +74,7 @@ public void clear() { @Override public JComponent getGUIComponent() { - return editor; + return pane; } @Override diff --git a/src/main/java/pulse/ui/components/buttons/LoaderButton.java b/src/main/java/pulse/ui/components/buttons/LoaderButton.java index 46c9b43..cad1066 100644 --- a/src/main/java/pulse/ui/components/buttons/LoaderButton.java +++ b/src/main/java/pulse/ui/components/buttons/LoaderButton.java @@ -19,7 +19,6 @@ import java.io.File; import java.io.IOException; -import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JFileChooser; import javax.swing.UIManager; @@ -27,7 +26,6 @@ import org.apache.commons.math3.exception.OutOfRangeException; import pulse.input.InterpolationDataset; -import pulse.ui.Messages; import pulse.util.ImageUtils; @SuppressWarnings("serial") @@ -36,8 +34,8 @@ public class LoaderButton extends JButton { private InterpolationDataset.StandartType dataType; private static File dir; - private final static Color NOT_HIGHLIGHTED = UIManager.getColor("Button.background"); - private final static Color HIGHLIGHTED = ImageUtils.blend(NOT_HIGHLIGHTED, Color.red, 0.75f); + private final static Color NOT_HIGHLIGHTED = UIManager.getColor("Button.background"); + private final static Color HIGHLIGHTED = ImageUtils.blend(NOT_HIGHLIGHTED, Color.red, 0.35f); public LoaderButton() { super(); @@ -49,7 +47,7 @@ public LoaderButton(String str) { init(); } - public void init() { + public final void init() { InterpolationDataset.addListener(e -> { if (dataType == e) { @@ -86,18 +84,17 @@ public void init() { showMessageDialog(getWindowAncestor((Component) arg0.getSource()), getString("LoaderButton.ReadError"), //$NON-NLS-1$ getString("LoaderButton.IOError"), //$NON-NLS-1$ ERROR_MESSAGE); - } - catch(OutOfRangeException ofre) { + } catch (OutOfRangeException ofre) { getDefaultToolkit().beep(); StringBuilder sb = new StringBuilder(getString("TextWrap.0")); - sb.append(getString("LoaderButton.OFRErrorDescriptor") ); + sb.append(getString("LoaderButton.OFRErrorDescriptor")); sb.append(ofre.getMessage()); sb.append(getString("LoaderButton.OFRErrorDescriptor2")); sb.append(getString("TextWrap.1")); - showMessageDialog(getWindowAncestor((Component) arg0.getSource()), - sb.toString(), + showMessageDialog(getWindowAncestor((Component) arg0.getSource()), + sb.toString(), getString("LoaderButton.OFRError"), //$NON-NLS-1$ - ERROR_MESSAGE); + ERROR_MESSAGE); } var size = getDataset(dataType).getData().size(); var label = ""; @@ -113,8 +110,10 @@ public void init() { default: throw new IllegalStateException("Unknown data type: " + dataType); } + StringBuilder sb = new StringBuilder(""); + sb.append(label).append(" data loaded! A total of ").append(size).append(" data points loaded."); showMessageDialog(getWindowAncestor((Component) arg0.getSource()), - "" + label + " data loaded! A total of " + size + " data points loaded.", + sb.toString(), "Data loaded", INFORMATION_MESSAGE); }); } @@ -124,11 +123,11 @@ public void setDataType(InterpolationDataset.StandartType dataType) { } public void highlight(boolean highlighted) { - setBorder(highlighted ? BorderFactory.createLineBorder(HIGHLIGHTED) : null); + setBackground(highlighted ? HIGHLIGHTED : NOT_HIGHLIGHTED); } public void highlightIfNeeded() { highlight(getDataset(dataType) == null); } -} +} \ No newline at end of file diff --git a/src/main/java/pulse/ui/components/controllers/AccessibleTableRenderer.java b/src/main/java/pulse/ui/components/controllers/AccessibleTableRenderer.java index b297d1a..04727c0 100644 --- a/src/main/java/pulse/ui/components/controllers/AccessibleTableRenderer.java +++ b/src/main/java/pulse/ui/components/controllers/AccessibleTableRenderer.java @@ -1,6 +1,5 @@ package pulse.ui.components.controllers; -import static java.awt.Color.RED; import java.awt.Component; import java.awt.Font; @@ -8,7 +7,6 @@ import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JTable; -import javax.swing.UIManager; import pulse.properties.Flag; import pulse.properties.NumericProperty; diff --git a/src/main/java/pulse/ui/components/controllers/InstanceCellEditor.java b/src/main/java/pulse/ui/components/controllers/InstanceCellEditor.java index 526823f..e9611d7 100644 --- a/src/main/java/pulse/ui/components/controllers/InstanceCellEditor.java +++ b/src/main/java/pulse/ui/components/controllers/InstanceCellEditor.java @@ -1,13 +1,11 @@ package pulse.ui.components.controllers; -import com.alee.utils.swing.PopupMenuAdapter; import java.awt.Component; import java.awt.event.ItemEvent; import javax.swing.DefaultCellEditor; import javax.swing.JComboBox; import javax.swing.JTable; -import javax.swing.event.PopupMenuEvent; import pulse.util.InstanceDescriptor; @@ -39,17 +37,7 @@ public Component getTableCellEditorComponent(JTable table, Object value, boolean } } }); - - combobox.addPopupMenuListener(new PopupMenuAdapter() { - - @Override - public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { - fireEditingCanceled(); - } - - } - ); - + return combobox; } diff --git a/src/main/java/pulse/ui/components/controllers/NumericPropertyRenderer.java b/src/main/java/pulse/ui/components/controllers/NumericPropertyRenderer.java index e655f9b..5f05afc 100644 --- a/src/main/java/pulse/ui/components/controllers/NumericPropertyRenderer.java +++ b/src/main/java/pulse/ui/components/controllers/NumericPropertyRenderer.java @@ -7,10 +7,8 @@ import javax.swing.JTable; import javax.swing.UIManager; import javax.swing.table.DefaultTableCellRenderer; -import pulse.math.Segment; import pulse.properties.NumericProperty; -import pulse.properties.Property; import pulse.properties.NumericPropertyFormatter; @SuppressWarnings("serial") @@ -29,25 +27,22 @@ public Component getTableCellRendererComponent(JTable table, Object value, boole if (value instanceof NumericProperty) { var jtf = initTextField((NumericProperty) value, table.isRowSelected(row)); - if (table.getEditorComponent() != null) { result = jtf; } else { - result = new JLabel(jtf.getText(), JLabel.RIGHT); - jtf = null; + result = (JLabel) super.getTableCellRendererComponent(table, + jtf.getText(), isSelected, hasFocus, row, column); + ((JLabel) result).setHorizontalAlignment(RIGHT); } - } else { var superRenderer = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); superRenderer.setHorizontalAlignment(JLabel.LEFT); - superRenderer.setBackground( - isSelected - ? UIManager.getColor("JFormattedTextField.selectionBackground") - : UIManager.getColor("JFormattedTextField.background")); result = superRenderer; } + + result.setForeground(UIManager.getColor("List.foreground")); return result; } @@ -59,4 +54,4 @@ private static JFormattedTextField initTextField(NumericProperty np, boolean row return jtf; } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/ui/components/controllers/ProblemCellRenderer.java b/src/main/java/pulse/ui/components/controllers/ProblemCellRenderer.java index 7c36077..4827624 100644 --- a/src/main/java/pulse/ui/components/controllers/ProblemCellRenderer.java +++ b/src/main/java/pulse/ui/components/controllers/ProblemCellRenderer.java @@ -4,20 +4,20 @@ import javax.swing.ImageIcon; import javax.swing.JTree; -import javax.swing.UIManager; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeCellRenderer; -import com.alee.managers.icon.LazyIcon; +//import com.alee.managers.icon.LazyIcon; import pulse.problem.statements.Problem; import pulse.util.ImageUtils; +import static pulse.util.ImageUtils.loadIcon; @SuppressWarnings("serial") public class ProblemCellRenderer extends DefaultTreeCellRenderer { - private static ImageIcon defaultIcon = (ImageIcon) ((LazyIcon) UIManager.getIcon("Tree.leafIcon")).getIcon(); - + private static ImageIcon defaultIcon = loadIcon("leaf.png", 16); + public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { diff --git a/src/main/java/pulse/ui/components/controllers/SearchListRenderer.java b/src/main/java/pulse/ui/components/controllers/SearchListRenderer.java index e74951a..cfc884c 100644 --- a/src/main/java/pulse/ui/components/controllers/SearchListRenderer.java +++ b/src/main/java/pulse/ui/components/controllers/SearchListRenderer.java @@ -2,7 +2,6 @@ import static javax.swing.BorderFactory.createEmptyBorder; -import java.awt.Color; import java.awt.Component; import javax.swing.DefaultListCellRenderer; @@ -18,8 +17,7 @@ public Component getListCellRendererComponent(JList list, Object value, int i var renderer = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); ((JComponent) renderer).setBorder(createEmptyBorder(10, 10, 10, 10)); - renderer.setForeground(isSelected ? Color.DARK_GRAY : Color.white); - + return renderer; } diff --git a/src/main/java/pulse/ui/components/controllers/TaskTableRenderer.java b/src/main/java/pulse/ui/components/controllers/TaskTableRenderer.java index 85d3ba6..33ccdcb 100644 --- a/src/main/java/pulse/ui/components/controllers/TaskTableRenderer.java +++ b/src/main/java/pulse/ui/components/controllers/TaskTableRenderer.java @@ -3,16 +3,13 @@ import static java.awt.Font.BOLD; import java.awt.Component; -import java.awt.Font; import javax.swing.JLabel; import javax.swing.JTable; import pulse.properties.NumericProperty; -import pulse.properties.Property; import pulse.tasks.Identifier; import pulse.tasks.logs.Status; -import pulse.util.PropertyHolder; @SuppressWarnings("serial") public class TaskTableRenderer extends NumericPropertyRenderer { diff --git a/src/main/java/pulse/ui/components/listeners/LogExportListener.java b/src/main/java/pulse/ui/components/listeners/LogExportListener.java deleted file mode 100644 index 99a32ae..0000000 --- a/src/main/java/pulse/ui/components/listeners/LogExportListener.java +++ /dev/null @@ -1,7 +0,0 @@ -package pulse.ui.components.listeners; - -public interface LogExportListener { - - public void onLogExportRequest(); - -} diff --git a/src/main/java/pulse/ui/components/listeners/LogListener.java b/src/main/java/pulse/ui/components/listeners/LogListener.java new file mode 100644 index 0000000..5a07f5d --- /dev/null +++ b/src/main/java/pulse/ui/components/listeners/LogListener.java @@ -0,0 +1,8 @@ +package pulse.ui.components.listeners; + +public interface LogListener { + + public void onLogExportRequest(); + public void onLogModeChanged(boolean graphical); + +} \ No newline at end of file diff --git a/src/main/java/pulse/ui/components/panels/ChartToolbar.java b/src/main/java/pulse/ui/components/panels/ChartToolbar.java index b4d5e54..dca6e62 100644 --- a/src/main/java/pulse/ui/components/panels/ChartToolbar.java +++ b/src/main/java/pulse/ui/components/panels/ChartToolbar.java @@ -38,7 +38,7 @@ public final class ChartToolbar extends JToolBar { private final static int ICON_SIZE = 16; - private List listeners; + private final List listeners; private RangeTextFields rtf; diff --git a/src/main/java/pulse/ui/components/panels/DoubleTablePanel.java b/src/main/java/pulse/ui/components/panels/DoubleTablePanel.java index 11a5c19..ebb6bf6 100644 --- a/src/main/java/pulse/ui/components/panels/DoubleTablePanel.java +++ b/src/main/java/pulse/ui/components/panels/DoubleTablePanel.java @@ -1,18 +1,3 @@ -/* - * Copyright 2021 kotik. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package pulse.ui.components.panels; import java.awt.GridBagConstraints; @@ -87,7 +72,6 @@ public void initComponents(JTable leftTable, String titleLeft, JTable rightTable var borderLeft = createTitledBorder(titleLeft); leftScroller.setBorder(borderLeft); - borderLeft.setTitleColor(java.awt.Color.WHITE); leftTable.setRowHeight(80); @@ -102,7 +86,6 @@ public void initComponents(JTable leftTable, String titleLeft, JTable rightTable var borderRight = createTitledBorder(titleRight); rightScroller.setBorder(borderRight); - borderRight.setTitleColor(java.awt.Color.WHITE); rightTable.setRowHeight(80); rightScroller.setViewportView(rightTable); diff --git a/src/main/java/pulse/ui/components/panels/LogToolbar.java b/src/main/java/pulse/ui/components/panels/LogToolbar.java index a9fd6a1..5db207b 100644 --- a/src/main/java/pulse/ui/components/panels/LogToolbar.java +++ b/src/main/java/pulse/ui/components/panels/LogToolbar.java @@ -1,7 +1,5 @@ package pulse.ui.components.panels; -import static pulse.tasks.logs.Log.isVerbose; -import static pulse.tasks.logs.Log.setVerbose; import static pulse.ui.Messages.getString; import static pulse.util.ImageUtils.loadIcon; @@ -14,13 +12,15 @@ import javax.swing.JCheckBox; import javax.swing.JToolBar; -import pulse.ui.components.listeners.LogExportListener; +import static pulse.tasks.logs.Log.setGraphicalLog; +import static pulse.tasks.logs.Log.isGraphicalLog; +import pulse.ui.components.listeners.LogListener; @SuppressWarnings("serial") public class LogToolbar extends JToolBar { private final static int ICON_SIZE = 16; - private List listeners; + private List listeners; public LogToolbar() { super(); @@ -35,24 +35,25 @@ public void initComponents() { var saveLogBtn = new JButton(loadIcon("save.png", ICON_SIZE, Color.white)); saveLogBtn.setToolTipText("Save"); - var verboseCheckBox = new JCheckBox(getString("LogToolBar.Verbose")); //$NON-NLS-1$ - verboseCheckBox.setSelected(isVerbose()); - verboseCheckBox.setHorizontalAlignment(CENTER); + var logmodeCheckbox = new JCheckBox(getString("LogToolBar.Verbose")); //$NON-NLS-1$ + logmodeCheckbox.setSelected(isGraphicalLog()); + logmodeCheckbox.setHorizontalAlignment(CENTER); - verboseCheckBox.addActionListener(event -> setVerbose(verboseCheckBox.isSelected())); + logmodeCheckbox.addActionListener(event -> { + boolean selected = logmodeCheckbox.isSelected(); + setGraphicalLog(selected); + listeners.stream().forEach(l -> l.onLogModeChanged(selected)); + }); - saveLogBtn.addActionListener(e -> notifyLog()); + saveLogBtn.addActionListener(e -> listeners.stream().forEach(l -> l.onLogExportRequest())); add(saveLogBtn); - add(verboseCheckBox); - } - public void notifyLog() { - listeners.stream().forEach(l -> l.onLogExportRequest()); + add(logmodeCheckbox); } - public void addLogExportListener(LogExportListener l) { + public void addLogListener(LogListener l) { listeners.add(l); } -} +} \ No newline at end of file diff --git a/src/main/java/pulse/ui/components/panels/ModelToolbar.java b/src/main/java/pulse/ui/components/panels/ModelToolbar.java index 7c0b550..770734e 100644 --- a/src/main/java/pulse/ui/components/panels/ModelToolbar.java +++ b/src/main/java/pulse/ui/components/panels/ModelToolbar.java @@ -18,7 +18,7 @@ @SuppressWarnings("serial") public class ModelToolbar extends JToolBar { - private final static int ICON_SIZE = 20; + private final static int ICON_SIZE = 16; public ModelToolbar() { super(); diff --git a/src/main/java/pulse/ui/components/panels/ProblemToolbar.java b/src/main/java/pulse/ui/components/panels/ProblemToolbar.java index 92e42ca..e06abd0 100644 --- a/src/main/java/pulse/ui/components/panels/ProblemToolbar.java +++ b/src/main/java/pulse/ui/components/panels/ProblemToolbar.java @@ -13,7 +13,6 @@ import java.awt.Component; import java.awt.GridLayout; import java.awt.event.ActionEvent; -import java.util.concurrent.Executors; import javax.swing.JButton; import javax.swing.JToolBar; diff --git a/src/main/java/pulse/ui/frames/LogFrame.java b/src/main/java/pulse/ui/frames/LogFrame.java index 996f1f3..7eeae66 100644 --- a/src/main/java/pulse/ui/frames/LogFrame.java +++ b/src/main/java/pulse/ui/frames/LogFrame.java @@ -6,7 +6,6 @@ import static java.awt.GridBagConstraints.WEST; import static java.lang.System.err; import static java.util.concurrent.TimeUnit.MILLISECONDS; -import static javax.swing.SwingUtilities.getWindowAncestor; import static pulse.io.export.ExportManager.askToExport; import static pulse.tasks.listeners.TaskRepositoryEvent.State.TASK_ADDED; import static pulse.ui.Messages.getString; @@ -14,16 +13,17 @@ import java.awt.BorderLayout; import java.awt.GridBagConstraints; -import javax.swing.JFrame; import javax.swing.JInternalFrame; -import javax.swing.JScrollPane; +import javax.swing.SwingUtilities; import pulse.tasks.TaskManager; import pulse.tasks.listeners.LogEntryListener; import pulse.tasks.logs.Log; import pulse.tasks.logs.LogEntry; import pulse.tasks.logs.AbstractLogger; +import pulse.ui.components.GraphicalLogPane; import pulse.ui.components.TextLogPane; +import pulse.ui.components.listeners.LogListener; import pulse.ui.components.panels.LogToolbar; import pulse.ui.components.panels.SystemPanel; @@ -31,6 +31,8 @@ public class LogFrame extends JInternalFrame { private AbstractLogger logger; + private final static AbstractLogger graphical = new GraphicalLogPane(); + private final static AbstractLogger text = new TextLogPane(); public LogFrame() { super("Log", true, false, true, true); @@ -40,12 +42,10 @@ public LogFrame() { } private void initComponents() { - logger = new TextLogPane(); - var logScroller = new JScrollPane(); - logScroller.setViewportView(logger.getGUIComponent()); + logger = Log.isGraphicalLog() ? graphical : text; getContentPane().setLayout(new BorderLayout()); - getContentPane().add(logScroller, CENTER); + getContentPane().add(logger.getGUIComponent(), CENTER); var gridBagConstraints = new GridBagConstraints(); gridBagConstraints.anchor = WEST; @@ -54,12 +54,24 @@ private void initComponents() { getContentPane().add(new SystemPanel(), PAGE_END); var logToolbar = new LogToolbar(); - logToolbar.addLogExportListener(() -> { - if (!logger.isEmpty()) { - askToExport(logger, (JFrame) getWindowAncestor(this), - getString("LogToolBar.FileFormatDescriptor")); + + var lel = new LogListener() { + @Override + public void onLogExportRequest() { + if (logger == text) { + askToExport(logger, null, getString("LogToolBar.FileFormatDescriptor")); + } else { + System.out.println("To export the log entries, please switch to text mode first!"); + } } - }); + + @Override + public void onLogModeChanged(boolean graphical) { + SwingUtilities.invokeLater(() -> setGraphicalLogger(graphical)); + } + }; + + logToolbar.addLogListener(lel); getContentPane().add(logToolbar, NORTH); } @@ -109,4 +121,16 @@ public AbstractLogger getLogger() { return logger; } + private void setGraphicalLogger(boolean graphicalLog) { + var old = logger; + logger = graphicalLog ? graphical : text; + + if (old != logger) { + getContentPane().remove(old.getGUIComponent()); + getContentPane().add(logger.getGUIComponent(), BorderLayout.CENTER); + logger.postAll(); + } + + } + } \ No newline at end of file diff --git a/src/main/java/pulse/ui/frames/PreviewFrame.java b/src/main/java/pulse/ui/frames/PreviewFrame.java index 76a249d..508d5a7 100644 --- a/src/main/java/pulse/ui/frames/PreviewFrame.java +++ b/src/main/java/pulse/ui/frames/PreviewFrame.java @@ -15,6 +15,7 @@ import java.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Color; +import static java.awt.Color.WHITE; import java.awt.GridLayout; import java.awt.Rectangle; import java.util.ArrayList; @@ -46,6 +47,7 @@ import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; import pulse.tasks.processing.ResultFormat; +import pulse.ui.components.Chart; @SuppressWarnings("serial") public class PreviewFrame extends JInternalFrame { @@ -93,7 +95,6 @@ private void init() { toolbar.add(selectX); selectXBox = new JComboBox<>(); - selectXBox.setFont(selectXBox.getFont().deriveFont(11)); toolbar.add(selectXBox); toolbar.add(new JSeparator()); @@ -102,7 +103,6 @@ private void init() { toolbar.add(selectY); selectYBox = new JComboBox<>(); - selectYBox.setFont(selectYBox.getFont().deriveFont(11)); toolbar.add(selectYBox); var drawSmoothBtn = new JToggleButton(); @@ -229,6 +229,9 @@ private static ChartPanel createEmptyPanel() { //plot.setRangeGridlinesVisible(false); //plot.setDomainGridlinesVisible(false); + var fore = UIManager.getColor("Label.foreground"); + plot.setDomainGridlinePaint(fore); + plot.getRenderer(1).setSeriesPaint(1, SMOOTH_COLOR); plot.getRenderer(0).setSeriesPaint(0, RESULT_COLOR); plot.getRenderer(0).setSeriesShape(0, @@ -244,6 +247,9 @@ private static ChartPanel createEmptyPanel() { cp.setMinimumDrawHeight(10); chart.setBackgroundPaint(UIManager.getColor("Panel.background")); + plot.setBackgroundPaint(chart.getBackgroundPaint()); + Chart.setAxisFontColor(plot.getDomainAxis(), fore); + Chart.setAxisFontColor(plot.getRangeAxis(), fore); return cp; } diff --git a/src/main/java/pulse/ui/frames/SearchOptionsFrame.java b/src/main/java/pulse/ui/frames/SearchOptionsFrame.java index 567c449..f638f4d 100644 --- a/src/main/java/pulse/ui/frames/SearchOptionsFrame.java +++ b/src/main/java/pulse/ui/frames/SearchOptionsFrame.java @@ -48,7 +48,6 @@ public class SearchOptionsFrame extends JInternalFrame { private final JTable rightTable; private final PathSolversList pathList; - private final static Font FONT = new Font(getString("PropertyHolderTable.FontName"), ITALIC, 16); private final static List pathSolvers = instancesOf(PathOptimiser.class); private final NumericPropertyKeyword[] mandatorySelection = new NumericPropertyKeyword[]{MAXTEMP}; @@ -196,7 +195,6 @@ public PathOptimiser getElementAt(int index) { } }); - setFont(FONT); setSelectionMode(SINGLE_SELECTION); setCellRenderer(new SearchListRenderer()); diff --git a/src/main/java/pulse/ui/frames/TaskControlFrame.java b/src/main/java/pulse/ui/frames/TaskControlFrame.java index 880e10d..f56d6ed 100644 --- a/src/main/java/pulse/ui/frames/TaskControlFrame.java +++ b/src/main/java/pulse/ui/frames/TaskControlFrame.java @@ -55,6 +55,8 @@ public class TaskControlFrame extends JFrame { private InternalGraphFrame pulseFrame; private PulseMainMenu mainMenu; + + private final static int ICON_SIZE = 16; public static TaskControlFrame getInstance() { return instance; @@ -188,26 +190,26 @@ private void initComponents() { setJMenuBar(mainMenu); logFrame = new LogFrame(); - logFrame.setFrameIcon(loadIcon("log.png", 20, Color.white)); + logFrame.setFrameIcon(loadIcon("log.png", ICON_SIZE, Color.white)); resultsFrame = new ResultFrame(); - resultsFrame.setFrameIcon(loadIcon("result.png", 20, Color.white)); + resultsFrame.setFrameIcon(loadIcon("result.png", ICON_SIZE, Color.white)); previewFrame = new PreviewFrame(); - previewFrame.setFrameIcon(loadIcon("preview.png", 20, Color.white)); + previewFrame.setFrameIcon(loadIcon("preview.png", ICON_SIZE, Color.white)); taskManagerFrame = new TaskManagerFrame(); - taskManagerFrame.setFrameIcon(loadIcon("task_manager.png", 20, Color.white)); + taskManagerFrame.setFrameIcon(loadIcon("task_manager.png", ICON_SIZE, Color.white)); graphFrame = MainGraphFrame.getInstance(); - graphFrame.setFrameIcon(loadIcon("curves.png", 20, Color.white)); + graphFrame.setFrameIcon(loadIcon("curves.png", ICON_SIZE, Color.white)); problemStatementFrame = new ProblemStatementFrame(); - problemStatementFrame.setFrameIcon(loadIcon("heat_problem.png", 20, Color.white)); + problemStatementFrame.setFrameIcon(loadIcon("heat_problem.png", ICON_SIZE, Color.white)); modelFrame = new ModelSelectionFrame(); - modelFrame.setFrameIcon(loadIcon("stored.png", 20, Color.white)); + modelFrame.setFrameIcon(loadIcon("stored.png", ICON_SIZE, Color.white)); searchOptionsFrame = new SearchOptionsFrame(); - searchOptionsFrame.setFrameIcon(loadIcon("optimiser.png", 20, Color.white)); + searchOptionsFrame.setFrameIcon(loadIcon("optimiser.png", ICON_SIZE, Color.white)); pulseFrame = new InternalGraphFrame("Pulse Shape", new PulseChart("Time (ms)", "Laser Power (a. u.)")); - pulseFrame.setFrameIcon(loadIcon("pulse.png", 20, Color.white)); + pulseFrame.setFrameIcon(loadIcon("pulse.png", ICON_SIZE, Color.white)); pulseFrame.setVisible(false); /* From caf3237e7539d5bdd2a4c8130c17a5a0d2684413 Mon Sep 17 00:00:00 2001 From: kotik-coder Date: Mon, 9 Jan 2023 00:19:47 +0300 Subject: [PATCH 6/6] Pulse & logs - Better handling of pulse discretisation - Fixed graphical log updates - Better pulse visualisation --- pom.xml | 13 +- .../pulse/problem/laser/DiscretePulse.java | 172 +++++++++++------- .../pulse/problem/laser/DiscretePulse2D.java | 4 +- .../pulse/problem/laser/NumericPulse.java | 2 +- .../pulse/problem/laser/RectangularPulse.java | 4 +- .../pulse/problem/laser/TrapezoidalPulse.java | 2 +- .../problem/schemes/DifferenceScheme.java | 7 +- src/main/java/pulse/problem/schemes/Grid.java | 34 +--- .../java/pulse/problem/schemes/Grid2D.java | 1 - .../problem/statements/AdiabaticSolution.java | 2 +- .../pulse/problem/statements/Problem.java | 5 +- .../java/pulse/problem/statements/Pulse.java | 4 +- .../statements/model/ThermalProperties.java | 2 +- .../statistics/ModelSelectionCriterion.java | 29 +++ .../search/statistics/ResidualStatistic.java | 36 +++- src/main/java/pulse/tasks/Calculation.java | 55 +++--- .../java/pulse/tasks/logs/DataLogEntry.java | 2 +- src/main/java/pulse/tasks/logs/LogEntry.java | 20 +- .../pulse/ui/components/GraphicalLogPane.java | 8 +- .../java/pulse/ui/components/LogChart.java | 43 +++-- .../java/pulse/ui/components/PulseChart.java | 10 +- src/main/resources/NumericProperty.xml | 9 +- src/main/resources/Version.txt | 2 +- src/main/resources/images/leaf.png | Bin 0 -> 24476 bytes src/main/resources/messages.properties | 2 +- 25 files changed, 294 insertions(+), 174 deletions(-) create mode 100644 src/main/resources/images/leaf.png diff --git a/pom.xml b/pom.xml index e511886..9605d72 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ kotik-coder PULsE - 1.97 + 1.97b PULsE Processing Unit for Laser flash Experiments @@ -18,8 +18,7 @@ The Apache License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0.txt - - + org.jfree @@ -27,11 +26,11 @@ 1.5.0 - com.weblookandfeel - weblaf-ui - 1.2.13 + com.formdev + flatlaf + 3.0 - + org.apache.commons commons-math3 3.6.1 diff --git a/src/main/java/pulse/problem/laser/DiscretePulse.java b/src/main/java/pulse/problem/laser/DiscretePulse.java index 51f5805..8891324 100644 --- a/src/main/java/pulse/problem/laser/DiscretePulse.java +++ b/src/main/java/pulse/problem/laser/DiscretePulse.java @@ -7,7 +7,10 @@ import pulse.problem.schemes.Grid; import pulse.problem.statements.Problem; import pulse.problem.statements.Pulse; +import static pulse.properties.NumericProperties.derive; +import static pulse.properties.NumericPropertyKeyword.TAU_FACTOR; import pulse.tasks.SearchTask; +import pulse.util.PropertyHolderListener; /** * A {@code DiscretePulse} is an object that acts as a medium between the @@ -20,9 +23,10 @@ public class DiscretePulse { private final Grid grid; private final Pulse pulse; - + private final ExperimentalData data; + private double widthOnGrid; - private double timeConversionFactor; + private double characteristicTime; private double invTotalEnergy; //normalisation factor /** @@ -49,29 +53,28 @@ public class DiscretePulse { */ public DiscretePulse(Problem problem, Grid grid) { this.grid = grid; - timeConversionFactor = problem.getProperties().timeFactor(); + characteristicTime = problem.getProperties().characteristicTime(); this.pulse = problem.getPulse(); Object ancestor = Objects.requireNonNull(problem.specificAncestor(SearchTask.class), "Problem has not been assigned to a SearchTask"); - ExperimentalData data = - (ExperimentalData) ( ((SearchTask) ancestor).getInput() ); - init(data); - + data = (ExperimentalData) (((SearchTask) ancestor).getInput()); + init(); + + PropertyHolderListener phl = e -> { + characteristicTime = problem.getProperties().characteristicTime(); + widthOnGrid = 0; + init(); + }; + pulse.addListener(e -> { - timeConversionFactor = problem.getProperties().timeFactor(); - init(data); + widthOnGrid = 0; + init(); }); - - } - - private void init(ExperimentalData data) { - widthOnGrid = 0; - recalculate(); - pulse.getPulseShape().init(data, this); - invTotalEnergy = 1.0/totalEnergy(); + problem.addListener(phl); + } /** @@ -91,39 +94,77 @@ public double laserPowerAt(double time) { * * @see pulse.problem.schemes.Grid.gridTime(double,double) */ - public final void recalculate() { - final double nominalWidth = ((Number) pulse.getPulseWidth().getValue()).doubleValue(); - final double resolvedWidth = timeConversionFactor / getWidthToleranceFactor(); + public final void init() { + final double nominalWidth = ((Number) pulse.getPulseWidth().getValue()).doubleValue(); + final double resolvedWidth = resolvedPulseWidthSeconds(); final double EPS = 1E-10; - + + double oldValue = widthOnGrid; + this.widthOnGrid = pulseWidthGrid(); + /** * The pulse is too short, which makes calculations too expensive. Can * we replace it with a rectangular pulse shape instead? */ - - if (nominalWidth < resolvedWidth - EPS && widthOnGrid < EPS) { + if (nominalWidth < resolvedWidth - EPS && oldValue < EPS) { //change shape to rectangular var shape = new RectangularPulse(); - pulse.setPulseShape(shape); - //change pulse width - setDiscreteWidth(resolvedWidth); + pulse.setPulseShape(shape); shape.init(null, this); - //adjust the pulse object to update the visualised pulse - } else if(nominalWidth > resolvedWidth + EPS) { - setDiscreteWidth(nominalWidth); - } - - invTotalEnergy = 1.0/totalEnergy(); - + } else { + pulse.getPulseShape().init(data, this); + } + + invTotalEnergy = 1.0 / totalEnergy(); } - + /** - * Calculates the total pulse energy using a numerical integrator.The - * normalisation factor is then equal to the inverse total energy. - * @return the total pulse energy, assuming sample area fully covered by the beam + * Optimises the {@code Grid} parameters so that the timestep is + * sufficiently small to enable accurate pulse correction. + *

+ * This can change the {@code tauFactor} and {@code tau} variables in the + * {@code Grid} object if {@code discretePulseWidth/(M - 1) < grid.tau}, + * where M is the required number of pulse calculations. + *

+ * + * @see PulseTemporalShape.getRequiredDiscretisation() */ + public double pulseWidthGrid() { + //minimum number of points for pulse calculation + int reqPoints = pulse.getPulseShape().getRequiredDiscretisation(); + //physical pulse width in time units + double experimentalWidth = (double) pulse.getPulseWidth().getValue(); + + //minimum resolved pulse width in time units for that specific problem + double resolvedWidth = resolvedPulseWidthSeconds(); + + double pWidth = Math.max(experimentalWidth, resolvedWidth); + + final double EPS = 1E-10; + double newTau = pWidth / characteristicTime / reqPoints; + + double result = 0; + + if (newTau < grid.getTimeStep() - EPS) { + double newTauFactor = (double) grid.getTimeFactor().getValue() / 2.0; + grid.setTimeFactor(derive(TAU_FACTOR, newTauFactor)); + result = pulseWidthGrid(); + } else { + result = grid.gridTime(pWidth, characteristicTime); + } + + return result; + } + + /** + * Calculates the total pulse energy using a numerical integrator.The + * normalisation factor is then equal to the inverse total energy. + * + * @return the total pulse energy, assuming sample area fully covered by the + * beam + */ public final double totalEnergy() { var pulseShape = pulse.getPulseShape(); @@ -140,27 +181,22 @@ public double integrand(double... vars) { } /** - * Gets the discrete dimensionless pulse width, which is a multiplier of the current - * grid timestep. The pulse width is converted to the dimensionless pulse width by - * dividing the real value by l2/a. + * Gets the discrete dimensionless pulse width, which is a multiplier of the + * current grid timestep. The pulse width is converted to the dimensionless + * pulse width by dividing the real value by l2/a. * * @return the dimensionless pulse width mapped to the grid. */ public double getDiscreteWidth() { return widthOnGrid; } - - private void setDiscreteWidth(double width) { - widthOnGrid = grid.gridTime(width, timeConversionFactor); - grid.adjustTimeStep(this); - } /** * Gets the physical {@code Pulse} * * @return the {@code Pulse} object */ - public Pulse getPulse() { + public Pulse getPhysicalPulse() { return pulse; } @@ -172,36 +208,38 @@ public Pulse getPulse() { public Grid getGrid() { return grid; } - + /** - * Gets the dimensional factor required to convert real time variable into - * a dimensional variable, defined in the {@code Problem} class + * Gets the dimensional factor required to convert real time variable into a + * dimensional variable, defined in the {@code Problem} class + * * @return the conversion factor */ - - public double getConversionFactor() { - return timeConversionFactor; + public double getCharacteristicTime() { + return characteristicTime; } - + /** - * Gets the minimal resolved pulse width defined by the {@code WIDTH_TOLERANCE_FACTOR} - * and the characteristic time given by the {@code getConversionFactor}. - * @return + * Gets the minimal resolved pulse width defined by the + * {@code WIDTH_TOLERANCE_FACTOR} and the characteristic time given by the + * {@code getConversionFactor}. + * + * @return */ - - public double resolvedPulseWidth() { - return timeConversionFactor / getWidthToleranceFactor(); + public double resolvedPulseWidthSeconds() { + return characteristicTime / getWidthToleranceFactor(); } - - /** - * Assuming a characteristic time is divided by the return value of this method - * and is set to the minimal resolved pulse width, shows how small a pulse width - * can be to enable finite pulse correction. - * @return the smallest fraction of a characteristic time resolved as a finite pulse. + + /** + * Assuming a characteristic time is divided by the return value of this + * method and is set to the minimal resolved pulse width, shows how small a + * pulse width can be to enable finite pulse correction. + * + * @return the smallest fraction of a characteristic time resolved as a + * finite pulse. */ - public int getWidthToleranceFactor() { return WIDTH_TOLERANCE_FACTOR; } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/problem/laser/DiscretePulse2D.java b/src/main/java/pulse/problem/laser/DiscretePulse2D.java index 00f884f..02a60f0 100644 --- a/src/main/java/pulse/problem/laser/DiscretePulse2D.java +++ b/src/main/java/pulse/problem/laser/DiscretePulse2D.java @@ -26,7 +26,7 @@ public class DiscretePulse2D extends DiscretePulse { * This had to be decreased for the 2d pulses. */ - private final static int WIDTH_TOLERANCE_FACTOR = 2000; + private final static int WIDTH_TOLERANCE_FACTOR = 1000; /** * The constructor for {@code DiscretePulse2D}. @@ -90,7 +90,7 @@ private void calcPulseSpot(ExtendedThermalProperties properties) { * @see pulse.problem.schemes.Grid2D.gridRadialDistance(double,double) */ public final void evalPulseSpot() { - var pulse = (Pulse2D) getPulse(); + var pulse = (Pulse2D) getPhysicalPulse(); var grid2d = (Grid2D) getGrid(); final double spotRadius = (double) pulse.getSpotDiameter().getValue() / 2.0; discretePulseSpot = grid2d.gridRadialDistance(spotRadius, sampleRadius); diff --git a/src/main/java/pulse/problem/laser/NumericPulse.java b/src/main/java/pulse/problem/laser/NumericPulse.java index e834f02..9dbaadb 100644 --- a/src/main/java/pulse/problem/laser/NumericPulse.java +++ b/src/main/java/pulse/problem/laser/NumericPulse.java @@ -64,7 +64,7 @@ public void init(ExperimentalData data, DiscretePulse pulse) { setPulseWidthOf(problem); //convert to dimensionless time and interpolate - double timeFactor = problem.getProperties().timeFactor(); + double timeFactor = problem.getProperties().characteristicTime(); doInterpolation(timeFactor); } diff --git a/src/main/java/pulse/problem/laser/RectangularPulse.java b/src/main/java/pulse/problem/laser/RectangularPulse.java index 2ba6b9d..583a92e 100644 --- a/src/main/java/pulse/problem/laser/RectangularPulse.java +++ b/src/main/java/pulse/problem/laser/RectangularPulse.java @@ -14,7 +14,7 @@ */ public class RectangularPulse extends PulseTemporalShape { - private final static int MIN_POINTS = 4; + private final static int MIN_POINTS = 2; /** * @param time the time measured from the start of the laser pulse. @@ -41,4 +41,4 @@ public int getRequiredDiscretisation() { return MIN_POINTS; } -} +} \ No newline at end of file diff --git a/src/main/java/pulse/problem/laser/TrapezoidalPulse.java b/src/main/java/pulse/problem/laser/TrapezoidalPulse.java index c305b81..a061a87 100644 --- a/src/main/java/pulse/problem/laser/TrapezoidalPulse.java +++ b/src/main/java/pulse/problem/laser/TrapezoidalPulse.java @@ -22,7 +22,7 @@ public class TrapezoidalPulse extends PulseTemporalShape { private double fall; private double h; - private final static int MIN_POINTS = 6; + private final static int MIN_POINTS = 8; /** * Constructs a trapezoidal pulse using a default segmentation principle. diff --git a/src/main/java/pulse/problem/schemes/DifferenceScheme.java b/src/main/java/pulse/problem/schemes/DifferenceScheme.java index 59c9220..8ad8dfd 100644 --- a/src/main/java/pulse/problem/schemes/DifferenceScheme.java +++ b/src/main/java/pulse/problem/schemes/DifferenceScheme.java @@ -1,5 +1,6 @@ package pulse.problem.schemes; +import java.util.Objects; import static pulse.properties.NumericProperties.def; import static pulse.properties.NumericProperties.derive; import static pulse.properties.NumericProperty.requireType; @@ -92,7 +93,7 @@ protected void prepare(Problem problem) throws SolverException { if (discretePulse == null) { discretePulse = problem.discretePulseOn(grid); } - discretePulse.recalculate(); + discretePulse.init(); clearArrays(); } @@ -114,13 +115,13 @@ public void runTimeSequence(Problem problem, final double offset, final double e int numPoints = (int) curve.getNumPoints().getValue(); final double startTime = (double) curve.getTimeShift().getValue(); - final double timeSegment = (endTime - startTime - offset) / problem.getProperties().timeFactor(); + final double timeSegment = (endTime - startTime - offset) / problem.getProperties().characteristicTime(); double tau = grid.getTimeStep(); final double dt = timeSegment / (numPoints - 1); timeInterval = Math.max( (int) (dt / tau), 1); - double wFactor = timeInterval * tau * problem.getProperties().timeFactor(); + double wFactor = timeInterval * tau * problem.getProperties().characteristicTime(); // First point (index = 0) is always (0.0, 0.0) curve.addPoint(0.0, 0.0); diff --git a/src/main/java/pulse/problem/schemes/Grid.java b/src/main/java/pulse/problem/schemes/Grid.java index f68467b..2d7128d 100644 --- a/src/main/java/pulse/problem/schemes/Grid.java +++ b/src/main/java/pulse/problem/schemes/Grid.java @@ -11,6 +11,7 @@ import java.util.Set; import pulse.problem.laser.DiscretePulse; +import pulse.problem.statements.Pulse; import pulse.properties.NumericProperty; import pulse.properties.NumericPropertyKeyword; import pulse.util.PropertyHolder; @@ -63,37 +64,6 @@ public Grid copy() { return new Grid(getGridDensity(), getTimeFactor()); } - /** - * Optimises the {@code Grid} parameters so that the timestep is - * sufficiently small to enable accurate pulse correction. - *

- * This can change the {@code tauFactor} and {@code tau} variables in the - * {@code Grid} object if {@code discretePulseWidth/(M - 1) < grid.tau}, - * where M is the required number of pulse calculations. - *

- * - * @param pulse the discrete pulse representation - * @see PulseTemporalShape.getRequiredDiscretisation() - */ - public final void adjustTimeStep(DiscretePulse pulse) { - double timeFactor = pulse.getConversionFactor(); - - final int reqPoints = pulse.getPulse().getPulseShape().getRequiredDiscretisation(); - - double pNominalWidth = (double) pulse.getPulse().getPulseWidth().getValue(); - double pResolvedWidth = pulse.resolvedPulseWidth(); - double pWidth = pNominalWidth < pResolvedWidth ? pResolvedWidth : pNominalWidth; - - double newTau = pWidth / timeFactor / (reqPoints > 1 ? reqPoints - 1 : 1); - double newTauFactor = newTau / (hx * hx); - - final double EPS = 1E-10; - if (newTauFactor < tauFactor - EPS) { - setTimeFactor(derive(TAU_FACTOR, newTauFactor)); - } - - } - /** * The listed properties include {@code GRID_DENSITY} and * {@code TAU_FACTOR}. @@ -223,7 +193,7 @@ public void setTimeFactor(NumericProperty timeFactor) { * @return a double representing the time on the finite grid */ public final double gridTime(double time, double dimensionFactor) { - return rint((time / dimensionFactor) / tau) * tau; + return ( (int) (time / dimensionFactor / tau) ) * tau; } /** diff --git a/src/main/java/pulse/problem/schemes/Grid2D.java b/src/main/java/pulse/problem/schemes/Grid2D.java index 6b10419..af2284b 100644 --- a/src/main/java/pulse/problem/schemes/Grid2D.java +++ b/src/main/java/pulse/problem/schemes/Grid2D.java @@ -68,7 +68,6 @@ public void adjustStepSize(DiscretePulse pulse) { adjustStepSize(pulse); } - adjustTimeStep(pulse); } @Override diff --git a/src/main/java/pulse/problem/statements/AdiabaticSolution.java b/src/main/java/pulse/problem/statements/AdiabaticSolution.java index a335fac..5fbb5a8 100644 --- a/src/main/java/pulse/problem/statements/AdiabaticSolution.java +++ b/src/main/java/pulse/problem/statements/AdiabaticSolution.java @@ -75,7 +75,7 @@ public static HeatingCurve classicSolution(Problem p, double timeLimit, int prec private final static double solutionAt(ThermalProperties p, double time, int precision) { final double EPS = 1E-8; - final double Fo = time / p.timeFactor(); + final double Fo = time / p.characteristicTime(); if (time < EPS) { return 0; diff --git a/src/main/java/pulse/problem/statements/Problem.java b/src/main/java/pulse/problem/statements/Problem.java index f228515..a9752e2 100644 --- a/src/main/java/pulse/problem/statements/Problem.java +++ b/src/main/java/pulse/problem/statements/Problem.java @@ -6,6 +6,7 @@ import static pulse.properties.NumericPropertyKeyword.TIME_SHIFT; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.concurrent.Executors; import java.util.stream.Collectors; @@ -245,7 +246,7 @@ public void optimisationVector(ParameterVector output) { p.setTransform(new StickTransform(bounds)); break; case TIME_SHIFT: - double magnitude = 0.25 * properties.timeFactor(); + double magnitude = 0.25 * properties.characteristicTime(); bounds = new Segment(-magnitude, magnitude); value = (double) curve.getTimeShift().getValue(); break; @@ -446,4 +447,4 @@ public final void setProperties(ThermalProperties properties) { public abstract boolean isReady(); -} +} \ No newline at end of file diff --git a/src/main/java/pulse/problem/statements/Pulse.java b/src/main/java/pulse/problem/statements/Pulse.java index 1b0fbf3..eac31f9 100644 --- a/src/main/java/pulse/problem/statements/Pulse.java +++ b/src/main/java/pulse/problem/statements/Pulse.java @@ -7,7 +7,6 @@ import static pulse.properties.NumericPropertyKeyword.PULSE_WIDTH; import java.util.List; -import java.util.Objects; import java.util.Set; import pulse.input.ExperimentalData; @@ -217,7 +216,8 @@ public PulseTemporalShape getPulseShape() { public void setPulseShape(PulseTemporalShape pulseShape) { this.pulseShape = pulseShape; - pulseShape.setParent(this); + pulseShape.setParent(this); + } } diff --git a/src/main/java/pulse/problem/statements/model/ThermalProperties.java b/src/main/java/pulse/problem/statements/model/ThermalProperties.java index 153faa1..295f6b8 100644 --- a/src/main/java/pulse/problem/statements/model/ThermalProperties.java +++ b/src/main/java/pulse/problem/statements/model/ThermalProperties.java @@ -325,7 +325,7 @@ public double maxRadiationBiot() { * * @return the time factor */ - public double timeFactor() { + public double characteristicTime() { return l * l / a; } diff --git a/src/main/java/pulse/search/statistics/ModelSelectionCriterion.java b/src/main/java/pulse/search/statistics/ModelSelectionCriterion.java index fbba1cc..6951845 100644 --- a/src/main/java/pulse/search/statistics/ModelSelectionCriterion.java +++ b/src/main/java/pulse/search/statistics/ModelSelectionCriterion.java @@ -37,6 +37,35 @@ public ModelSelectionCriterion(ModelSelectionCriterion another) { this.criterion = another.criterion; } + @Override + public int hashCode() { + int hash = 7; + hash = 43 * hash + this.kq; + hash = 43 * hash + (int) (Double.doubleToLongBits(this.criterion) ^ (Double.doubleToLongBits(this.criterion) >>> 32)); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final ModelSelectionCriterion other = (ModelSelectionCriterion) obj; + if (this.kq != other.kq) { + return false; + } + if (Double.doubleToLongBits(this.criterion) != Double.doubleToLongBits(other.criterion)) { + return false; + } + return true; + } + @Override public void evaluate(GeneralTask t) { kq = t.searchVector().dimension(); //number of parameters diff --git a/src/main/java/pulse/search/statistics/ResidualStatistic.java b/src/main/java/pulse/search/statistics/ResidualStatistic.java index e4c92a3..5b9d3e5 100644 --- a/src/main/java/pulse/search/statistics/ResidualStatistic.java +++ b/src/main/java/pulse/search/statistics/ResidualStatistic.java @@ -6,6 +6,7 @@ import static pulse.properties.NumericPropertyKeyword.OPTIMISER_STATISTIC; import java.util.List; +import java.util.Objects; import pulse.DiscreteInput; import pulse.Response; import pulse.input.IndexRange; @@ -31,6 +32,39 @@ public abstract class ResidualStatistic extends Statistic { private List rx; private List ry; + @Override + public int hashCode() { + int hash = 5; + hash = 53 * hash + (int) (Double.doubleToLongBits(this.statistic) ^ (Double.doubleToLongBits(this.statistic) >>> 32)); + hash = 53 * hash + Objects.hashCode(this.rx); + hash = 53 * hash + Objects.hashCode(this.ry); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final ResidualStatistic other = (ResidualStatistic) obj; + if (Double.doubleToLongBits(this.statistic) != Double.doubleToLongBits(other.statistic)) { + return false; + } + if (!Objects.equals(this.rx, other.rx)) { + return false; + } + if (!Objects.equals(this.ry, other.ry)) { + return false; + } + return true; + } + public ResidualStatistic() { super(); ry = new ArrayList<>(); @@ -144,4 +178,4 @@ public void set(NumericPropertyKeyword type, NumericProperty property) { } } -} +} \ No newline at end of file diff --git a/src/main/java/pulse/tasks/Calculation.java b/src/main/java/pulse/tasks/Calculation.java index 5c673d3..2e38a79 100644 --- a/src/main/java/pulse/tasks/Calculation.java +++ b/src/main/java/pulse/tasks/Calculation.java @@ -9,6 +9,7 @@ import static pulse.util.Reflexive.instantiate; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; import pulse.Response; @@ -330,27 +331,6 @@ public int compareTo(Calculation arg0) { return sThis.compareTo(sAnother); } - @Override - public boolean equals(Object o) { - if (o == this) { - return true; - } - - if (o == null) { - return false; - } - - if (!(o instanceof Calculation)) { - return false; - } - - var c = (Calculation) o; - - return (os.getStatistic().equals(c.getOptimiserStatistic().getStatistic()) - && rs.getStatistic().equals(c.getModelSelectionCriterion().getStatistic())); - - } - public static InstanceDescriptor getModelSelectionDescriptor() { return instanceDescriptor; } @@ -395,4 +375,37 @@ public double objectiveFunction(GeneralTask task) throws SolverException { return (double) os.getStatistic().getValue(); } + @Override + public int hashCode() { + int hash = 7; + hash = 79 * hash + Objects.hashCode(this.problem); + hash = 79 * hash + Objects.hashCode(this.scheme); + hash = 79 * hash + Objects.hashCode(this.result); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Calculation other = (Calculation) obj; + if (!Objects.equals(this.problem, other.problem)) { + return false; + } + if (!Objects.equals(this.scheme, other.scheme)) { + return false; + } + if (!Objects.equals(this.result, other.result)) { + return false; + } + return true; + } + } \ No newline at end of file diff --git a/src/main/java/pulse/tasks/logs/DataLogEntry.java b/src/main/java/pulse/tasks/logs/DataLogEntry.java index c3c5ab3..94b8774 100644 --- a/src/main/java/pulse/tasks/logs/DataLogEntry.java +++ b/src/main/java/pulse/tasks/logs/DataLogEntry.java @@ -127,4 +127,4 @@ public String toString() { } -} +} \ No newline at end of file diff --git a/src/main/java/pulse/tasks/logs/LogEntry.java b/src/main/java/pulse/tasks/logs/LogEntry.java index bfd84fb..f311fd1 100644 --- a/src/main/java/pulse/tasks/logs/LogEntry.java +++ b/src/main/java/pulse/tasks/logs/LogEntry.java @@ -20,9 +20,9 @@ */ public class LogEntry { - private Identifier identifier; - private LocalTime time; - private final Response response; + private final Identifier identifier; + private final LocalTime time; + private final LogEntry previous; /** *

@@ -36,11 +36,17 @@ public LogEntry(SearchTask t) { Objects.requireNonNull(t, Messages.getString("LogEntry.NullTaskError")); time = LocalDateTime.now().toLocalTime(); identifier = t.getIdentifier(); - this.response = t.getResponse(); + var list = t.getLog().getLogEntries(); + if(list != null && !list.isEmpty()) { + previous = list.get(list.size() - 1); + } + else { + previous = null; + } } - public Response getResponse() { - return response; + public LogEntry getPreviousEntry() { + return previous; } public Identifier getIdentifier() { @@ -51,4 +57,4 @@ public LocalTime getTime() { return time; } -} +} \ No newline at end of file diff --git a/src/main/java/pulse/ui/components/GraphicalLogPane.java b/src/main/java/pulse/ui/components/GraphicalLogPane.java index a50e494..82f8424 100644 --- a/src/main/java/pulse/ui/components/GraphicalLogPane.java +++ b/src/main/java/pulse/ui/components/GraphicalLogPane.java @@ -1,6 +1,8 @@ package pulse.ui.components; import javax.swing.JComponent; +import javax.swing.JScrollPane; +import javax.swing.ScrollPaneConstants; import static pulse.properties.NumericPropertyKeyword.ITERATION; import pulse.tasks.TaskManager; import pulse.tasks.listeners.TaskRepositoryEvent; @@ -14,9 +16,13 @@ public class GraphicalLogPane extends AbstractLogger { private final LogChart chart; + private final JScrollPane pane; public GraphicalLogPane() { + pane = new JScrollPane(); + pane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); chart = new LogChart(); + pane.setViewportView(chart.getChartPanel()); TaskManager.getManagerInstance().addTaskRepositoryListener( e -> { if(e.getState() == TaskRepositoryEvent.State.TASK_SUBMITTED) { chart.clear(); @@ -26,7 +32,7 @@ public GraphicalLogPane() { @Override public JComponent getGUIComponent() { - return chart.getChartPanel(); + return pane; } @Override diff --git a/src/main/java/pulse/ui/components/LogChart.java b/src/main/java/pulse/ui/components/LogChart.java index 1f180ca..ba40ae4 100644 --- a/src/main/java/pulse/ui/components/LogChart.java +++ b/src/main/java/pulse/ui/components/LogChart.java @@ -6,9 +6,14 @@ import java.awt.Color; import static java.awt.Color.WHITE; import static java.awt.Color.black; +import java.awt.Dimension; import java.time.Duration; +import java.time.LocalTime; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import javax.swing.SwingUtilities; import org.jfree.chart.JFreeChart; @@ -35,6 +40,7 @@ import pulse.tasks.TaskManager; import pulse.tasks.logs.DataLogEntry; import pulse.tasks.logs.Log; +import pulse.tasks.logs.StateEntry; import pulse.tasks.logs.Status; import pulse.tasks.processing.Buffer; import pulse.ui.ColorGenerator; @@ -44,7 +50,8 @@ public class LogChart extends AuxPlotter { private final Map plots; private Color[] colors; private static final ColorGenerator cg = new ColorGenerator(); - private Response r; + public final static int HEIGHT_FACTOR = 75; + public final static int MARGIN = 10; public LogChart() { var plot = new CombinedDomainXYPlot(new NumberAxis("Iteration")); @@ -58,13 +65,14 @@ public LogChart() { public final void clear() { var p = (CombinedDomainXYPlot) getPlot(); - p.getDomainAxis().setAutoRange(true); if (p != null) { + if (p.getDomainAxis() != null) { + p.getDomainAxis().setAutoRange(true); + } plots.values().stream().forEach(pp -> p.remove(pp)); } plots.clear(); colors = new Color[0]; - r = null; } private void setLegendTitle(Plot plot) { @@ -88,6 +96,11 @@ public final void add(ParameterIdentifier key, int no) { plots.put(key, plot); ((CombinedDomainXYPlot) getPlot()).add(plot); + int height = HEIGHT_FACTOR * plots.size(); + int width = getChartPanel().getParent().getWidth() - MARGIN; + getChartPanel().setPreferredSize(new Dimension(width, height)); + getChartPanel().revalidate(); + var dataset = new XYSeriesCollection(); var series = new XYSeries(key.toString()); @@ -115,7 +128,7 @@ public void changeAxis(boolean iterationMode) { var domainAxis = (NumberAxis) getPlot().getDomainAxis(); domainAxis.setLabel(iterationMode ? "Iteration" : "Time (ms)"); domainAxis.setAutoRange(!iterationMode); - if(iterationMode) { + if (iterationMode) { domainAxis.setTickUnit(new NumberTickUnit(1)); } else { domainAxis.setAutoTickUnitSelection(true); @@ -126,10 +139,18 @@ public void changeAxis(boolean iterationMode) { public void plot(Log l) { requireNonNull(l); - l.getLogEntries().stream() - .filter(le -> le instanceof DataLogEntry) - .forEach(d -> plot((DataLogEntry) d, - Duration.between(l.getStart(), d.getTime()).toMillis())); + List startTimes = l.getLogEntries().stream() + .filter(le -> le instanceof DataLogEntry && le.getPreviousEntry() instanceof StateEntry) + .map(entry -> entry.getTime()).collect(Collectors.toList()); + + if (!startTimes.isEmpty()) { + var recentStart = startTimes.get(startTimes.size() - 1); + l.getLogEntries().stream().filter(le -> le.getTime().isAfter(recentStart)) + .filter(e -> e instanceof DataLogEntry).forEach(dle + -> plot((DataLogEntry) dle, + Duration.between(recentStart, dle.getTime()).toMillis())); + } + } private static void adjustRange(XYPlot pl, int iteration, int bufSize) { @@ -138,7 +159,7 @@ private static void adjustRange(XYPlot pl, int iteration, int bufSize) { var domainAxis = pl.getDomainAxis(); var r = domainAxis.getRange(); var newR = new Range(lower, lower + bufSize); - + if (!r.equals(newR) && iteration > lower) { ((XYPlot) pl).getDomainAxis().setRange(lower, lower + bufSize); } @@ -185,7 +206,7 @@ public final void plot(DataLogEntry dle, double iterationOrTime) { runningAverage.add(iterationOrTime, buf.average(np.getKeyword())); } - SwingUtilities.invokeLater(() -> adjustRange((XYPlot)pl, (int)iterationOrTime, bufSize)); + SwingUtilities.invokeLater(() -> adjustRange((XYPlot) pl, (int) iterationOrTime, bufSize)); } else { var domainAxis = ((XYPlot) pl).getDomainAxis(); @@ -196,4 +217,4 @@ public final void plot(DataLogEntry dle, double iterationOrTime) { } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/ui/components/PulseChart.java b/src/main/java/pulse/ui/components/PulseChart.java index db6e747..5b4151a 100644 --- a/src/main/java/pulse/ui/components/PulseChart.java +++ b/src/main/java/pulse/ui/components/PulseChart.java @@ -4,13 +4,11 @@ import static java.awt.Color.black; import static java.awt.Font.PLAIN; import static java.util.Objects.requireNonNull; -import static org.jfree.chart.plot.PlotOrientation.VERTICAL; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; -import org.jfree.chart.ChartFactory; import org.jfree.chart.annotations.XYTitleAnnotation; import org.jfree.chart.block.BlockBorder; import org.jfree.chart.renderer.xy.XYDifferenceRenderer; @@ -63,8 +61,8 @@ public void plot(Calculation c) { var pulseDataset = new XYSeriesCollection(); - pulseDataset.addSeries(series(problem.getPulse(), c.getScheme().getGrid().getTimeStep(), - problem.getProperties().timeFactor(), startTime)); + pulseDataset.addSeries(series(problem.getPulse(), c.getScheme().getGrid().getTimeStep()/20.0, + problem.getProperties().characteristicTime(), startTime)); getPlot().setDataset(0, pulseDataset); } @@ -76,8 +74,8 @@ private static XYSeries series(Pulse pulse, double dx, double timeFactor, double double timeLimit = pulseShape.getPulseWidth(); double x = startTime/timeFactor; - series.add(TO_MILLIS * (startTime - dx * timeFactor / 10.), 0.0); - series.add(TO_MILLIS * (startTime + timeFactor*(timeLimit + dx / 10.)), 0.0); + series.add(TO_MILLIS * (startTime - dx * timeFactor / 100.), 0.0); + series.add(TO_MILLIS * (startTime + timeFactor*(timeLimit + dx / 100.)), 0.0); for (int i = 0, numPoints = (int) (timeLimit/dx); i < numPoints; i++) { series.add(x * timeFactor * TO_MILLIS, pulseShape.evaluateAt(x - startTime/timeFactor)); diff --git a/src/main/resources/NumericProperty.xml b/src/main/resources/NumericProperty.xml index 9640ce2..06c47f7 100644 --- a/src/main/resources/NumericProperty.xml +++ b/src/main/resources/NumericProperty.xml @@ -1,5 +1,10 @@ + + 1l_GFkuWY3;; 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
Time taken: LogToolBar.FileFormatDescriptor=HTML Log Files LogToolBar.SaveButton=Save Log -LogToolBar.Verbose=Verbose log +LogToolBar.Verbose=Graphical NumberEditor.EditText=Edit NumberEditor.IllegalTableEntry=Illegal table entry NumberEditor.InvalidText=Invalid Text Entered