diff --git a/ProcessingGrapher/LiveGraph.pde b/ProcessingGrapher/LiveGraph.pde index 100f936..6633f1e 100644 --- a/ProcessingGrapher/LiveGraph.pde +++ b/ProcessingGrapher/LiveGraph.pde @@ -50,9 +50,11 @@ class LiveGraph implements TabAPI { int fileCounter; int maxFileRows = 100000; int drawFrom; + int pausedCount; float xRate; int selectedGraph; boolean autoAxis; + boolean isPaused; int maxSamples; int[] sampleWindow = {1000,1000,1000,1000}; int signalListChange; @@ -95,8 +97,10 @@ class LiveGraph implements TabAPI { xRate = 100; autoAxis = true; + isPaused = false; drawFrom = 0; + pausedCount = 0; maxSamples = 10; signalListChange = 0; @@ -136,6 +140,17 @@ class LiveGraph implements TabAPI { graphD.resetGraph(); } + if (isPaused) { + String messageText = "Live Graph is Paused [⏸]"; + rectMode(CENTER); + textAlign(CENTER, TOP); + stroke(c_alert_message_box); + fill(c_alert_message_box); + rect((cR - cL) / 2, cT + (15 * uimult), textWidth(messageText) + (10 * uimult), 20 * uimult); + fill(c_sidebar_heading); + text(messageText, (cR - cL) / 2, cT + int(5 * uimult)); + } + // Show message if no serial device is connected if (!serialConnected) { if (showInstructions) { @@ -155,7 +170,11 @@ class LiveGraph implements TabAPI { * Draw new tab data */ void drawNewData () { + print("."); int currentCount = dataTable.getRowCount(); + if (isPaused) { + if (pausedCount < currentCount) currentCount = pausedCount; + } // If there is content to draw if (currentCount > 0) { @@ -200,7 +219,6 @@ class LiveGraph implements TabAPI { } else if (graphAssignment[i] == 1 && samplesA <= drawFrom) { checkGraphSize(dataPoint, graphA); graphA.plotData(dataPoint, i); - println("Plotting: " + drawFrom + ", " + dataPoint); } } catch (Exception e) { println("LiveGraph::drawNewData() - drawFrom: " + drawFrom + ", currentCount: " + currentCount + ", Error: " + e); @@ -309,6 +327,9 @@ class LiveGraph implements TabAPI { // Ensure table is empty dataTable = new CustomTable(); drawFrom = 0; + pausedCount = 0; + isPaused = false; + redrawContent = true; // Add columns to the table while(dataTable.getColumnCount() < dataColumns.length) dataTable.addColumn(dataColumns[dataTable.getColumnCount()]); @@ -441,7 +462,7 @@ class LiveGraph implements TabAPI { // the last 10 input data samples didn't contain the signal if (dataColumns.length > dataArray.length) { signalListChange++; - if (signalListChange >= 10 && !recordData) { + if (signalListChange >= 10 && !recordData && !isPaused) { dataColumns = shorten(dataColumns); graphAssignment = shorten(graphAssignment); dataTable.removeColumn(dataColumns.length); @@ -493,7 +514,7 @@ class LiveGraph implements TabAPI { dataTable = new CustomTable(); drawFrom = 0; } - } else { + } else if (!isPaused) { // Remove rows from table which don't need to be shown on the graphs anymore while (dataTable.getRowCount() > maxSamples) { dataTable.removeRow(0); @@ -524,7 +545,7 @@ class LiveGraph implements TabAPI { int iH = round((sideItemHeight - 5) * uimult); int iL = round(sL + (10 * uimult)); int iW = round(sW - (20 * uimult)); - menuHeight = round((12.5 + dataColumns.length + ((graphMode + 1) * 0.75)) * uH); + menuHeight = round((13.5 + dataColumns.length + ((graphMode + 1) * 0.75)) * uH); // Figure out if scrolling of the menu is necessary if (menuHeight > sH) { @@ -592,17 +613,18 @@ class LiveGraph implements TabAPI { // Input Data Columns drawHeading("Data Format", iL, sT + (uH * 9), iW, tH); drawDatabox("Rate: " + xRate + "Hz", iL, sT + (uH * 10), iW, iH, tH); + drawButton((isPaused)? "Resume Data [ ▶ ]":"Pause Data [⏸]", (isPaused)? c_sidebar_accent:c_sidebar_button, iL, sT + (uH * 11), iW, iH, tH); //drawButton("Add Column", c_sidebar_button, iL, sT + (uH * 13.5), iW, iH, tH); - drawDatabox("Split", c_idletab_text, iL, sT + (uH * 11), iW - (80 * uimult), iH, tH); - drawButton("1", (graphMode == 1)? c_sidebar_accent:c_sidebar_button, iL + iW - (80 * uimult), sT + (uH * 11), 20 * uimult, iH, tH); - drawButton("2", (graphMode == 2)? c_sidebar_accent:c_sidebar_button, iL + iW - (60 * uimult), sT + (uH * 11), 20 * uimult, iH, tH); - drawButton("3", (graphMode == 3)? c_sidebar_accent:c_sidebar_button, iL + iW - (40 * uimult), sT + (uH * 11), 20 * uimult, iH, tH); - drawButton("4", (graphMode == 4)? c_sidebar_accent:c_sidebar_button, iL + iW - (20 * uimult), sT + (uH * 11), 20 * uimult, iH, tH); - drawRectangle(c_sidebar_divider, iL + iW - (60 * uimult), sT + (uH * 11) + (1 * uimult), 1 * uimult, iH - (2 * uimult)); - drawRectangle(c_sidebar_divider, iL + iW - (40 * uimult), sT + (uH * 11) + (1 * uimult), 1 * uimult, iH - (2 * uimult)); - drawRectangle(c_sidebar_divider, iL + iW - (20 * uimult), sT + (uH * 11) + (1 * uimult), 1 * uimult, iH - (2 * uimult)); + drawDatabox("Split", c_idletab_text, iL, sT + (uH * 12), iW - (80 * uimult), iH, tH); + drawButton("1", (graphMode == 1)? c_sidebar_accent:c_sidebar_button, iL + iW - (80 * uimult), sT + (uH * 12), 20 * uimult, iH, tH); + drawButton("2", (graphMode == 2)? c_sidebar_accent:c_sidebar_button, iL + iW - (60 * uimult), sT + (uH * 12), 20 * uimult, iH, tH); + drawButton("3", (graphMode == 3)? c_sidebar_accent:c_sidebar_button, iL + iW - (40 * uimult), sT + (uH * 12), 20 * uimult, iH, tH); + drawButton("4", (graphMode == 4)? c_sidebar_accent:c_sidebar_button, iL + iW - (20 * uimult), sT + (uH * 12), 20 * uimult, iH, tH); + drawRectangle(c_sidebar_divider, iL + iW - (60 * uimult), sT + (uH * 12) + (1 * uimult), 1 * uimult, iH - (2 * uimult)); + drawRectangle(c_sidebar_divider, iL + iW - (40 * uimult), sT + (uH * 12) + (1 * uimult), 1 * uimult, iH - (2 * uimult)); + drawRectangle(c_sidebar_divider, iL + iW - (20 * uimult), sT + (uH * 12) + (1 * uimult), 1 * uimult, iH - (2 * uimult)); - float tHnow = 12; + float tHnow = 13; for (int j = 0; j < graphMode + 1; j++) { if (j < graphMode) drawText("Graph " + (j + 1), c_idletab_text, iL, sT + (uH * tHnow), iW, iH * 3 / 4); @@ -934,8 +956,16 @@ class LiveGraph implements TabAPI { } } + // Pause/Resume + else if ((mouseY > sT + (uH * 11)) && (mouseY < sT + (uH * 11) + iH)) { + pausedCount = dataTable.getRowCount(); + isPaused = !isPaused; + redrawUI = true; + redrawContent = true; + } + // Add a new input data column - else if ((mouseY > sT + (uH * 11)) && (mouseY < sT + (uH * 11) + iH)){ + else if ((mouseY > sT + (uH * 12)) && (mouseY < sT + (uH * 12) + iH)){ // Graph mode 1 if ((mouseX >= iL + iW - (80 * uimult)) && (mouseX < iL + iW - (60 * uimult))) { @@ -1015,7 +1045,7 @@ class LiveGraph implements TabAPI { } else { - float tHnow = 12; + float tHnow = 13; for (int j = 0; j < graphMode + 1; j++) { tHnow += 0.75; diff --git a/ProcessingGrapher/ProcessingGrapher.pde b/ProcessingGrapher/ProcessingGrapher.pde index bb88427..8f1a5d1 100644 --- a/ProcessingGrapher/ProcessingGrapher.pde +++ b/ProcessingGrapher/ProcessingGrapher.pde @@ -7,8 +7,8 @@ * @website https://wired.chillibasket.com/processing-grapher/ * * @copyright GNU General Public License v3 - * @date 1st April 2021 - * @version 1.2.3 + * @date 9th May 2021 + * @version 1.2.4 * * * * * * * * * * * * * * * * * * * * * * */ /* @@ -31,7 +31,7 @@ * along with this program. If not, see . */ -String versionNumber = "1.2.3"; +String versionNumber = "1.2.4"; // Swing for input popups import static javax.swing.JOptionPane.*; diff --git a/ProcessingGrapher/SerialMonitor.pde b/ProcessingGrapher/SerialMonitor.pde index cf2d46d..3020397 100644 --- a/ProcessingGrapher/SerialMonitor.pde +++ b/ProcessingGrapher/SerialMonitor.pde @@ -627,7 +627,7 @@ class SerialMonitor implements TabAPI { drawHeading("Terminal Options", iL, sT + (uH * 8), iW, tH); if (recordData) drawDatabox("Clear Terminal", c_idletab_text, iL, sT + (uH * 9), iW, iH, tH); else drawButton("Clear Terminal", c_sidebar_button, iL, sT + (uH * 9), iW, iH, tH); - drawButton((autoScroll)? "Autoscroll: On":"Autoscroll: Off", c_sidebar_button, iL, sT + (uH * 10), iW, iH, tH); + drawButton((autoScroll)? "Autoscroll: On":"Autoscroll: Off", (autoScroll)? c_sidebar_button:c_sidebar_accent, iL, sT + (uH * 10), iW, iH, tH); // Input Data Columns drawHeading("Colour Tags", iL, sT + (uH * 11.5), iW, tH); diff --git a/ProcessingGrapher/Settings.pde b/ProcessingGrapher/Settings.pde index 9c29caf..8b62caa 100644 --- a/ProcessingGrapher/Settings.pde +++ b/ProcessingGrapher/Settings.pde @@ -301,7 +301,7 @@ class Settings implements TabAPI { int iH = round((sideItemHeight - 5) * uimult); int iL = round(sL + (10 * uimult)); int iW = round(sW - (20 * uimult)); - if (menuLevel == 0) menuHeight = round(23.5 * uH); + if (menuLevel == 0) menuHeight = round(23 * uH); else if (menuLevel == 1) menuHeight = round((3 + baudRateList.length) * uH); else if (menuLevel == 2) menuHeight = round((3 + lineEndingList.length) * uH); else if (menuLevel == 3) menuHeight = round((3 + parityBitsList.length) * uH); @@ -333,50 +333,50 @@ class Settings implements TabAPI { if (menuLevel == 0) { // UI Scaling Options - drawHeading("Interface Size", iL, sT + (uH * 0.5), iW, tH); - drawButton("-", c_sidebar_button, iL, sT + (uH * 1.5), iW / 4, iH, tH); - drawButton("+", c_sidebar_button, iL + (iW * 3 / 4), sT + (uH * 1.5), iW / 4, iH, tH); - drawDatabox(round(uimult*100) + "%", c_idletab_text, iL + (iW / 4), sT + (uH * 1.5), iW / 2, iH, tH); + drawHeading("Interface Size", iL, sT + (uH * 0), iW, tH); + drawButton("-", c_sidebar_button, iL, sT + (uH * 1), iW / 4, iH, tH); + drawButton("+", c_sidebar_button, iL + (iW * 3 / 4), sT + (uH * 1), iW / 4, iH, tH); + drawDatabox(round(uimult*100) + "%", c_idletab_text, iL + (iW / 4), sT + (uH * 1), iW / 2, iH, tH); // Change the colour scheme - drawHeading("Colour Scheme", iL, sT + (uH * 3), iW, tH); - drawButton("Light - Celeste", (colorScheme == 0)? c_sidebar_accent:c_sidebar_button, iL, sT + (uH * 4), iW, iH, tH); - drawButton("Dark - Gravity", (colorScheme == 1)? c_sidebar_accent:c_sidebar_button, iL, sT + (uH * 5), iW, iH, tH); - drawButton("Dark - Monokai", (colorScheme == 2)? c_sidebar_accent:c_sidebar_button, iL, sT + (uH * 6), iW, iH, tH); + drawHeading("Colour Scheme", iL, sT + (uH * 2.5), iW, tH); + drawButton("Light - Celeste", (colorScheme == 0)? c_sidebar_accent:c_sidebar_button, iL, sT + (uH * 3.5), iW, iH, tH); + drawButton("Dark - Gravity", (colorScheme == 1)? c_sidebar_accent:c_sidebar_button, iL, sT + (uH * 4.5), iW, iH, tH); + drawButton("Dark - Monokai", (colorScheme == 2)? c_sidebar_accent:c_sidebar_button, iL, sT + (uH * 5.5), iW, iH, tH); // Turn FPS counter on/off - drawHeading("FPS Indicator", iL, sT + (uH * 7.5), iW, tH); - drawButton("Show", (drawFPS)? c_sidebar_accent:c_sidebar_button, iL, sT + (uH * 8.5), iW/2, iH, tH); - drawButton("Hide", (!drawFPS)? c_sidebar_accent:c_sidebar_button, iL + (iW/2), sT + (uH * 8.5), iW/2, iH, tH); - drawRectangle(c_sidebar_divider, iL + (iW / 2), sT + (uH * 8.5) + (1 * uimult), 1 * uimult, iH - (2 * uimult)); + drawHeading("FPS Indicator", iL, sT + (uH * 7), iW, tH); + drawButton("Show", (drawFPS)? c_sidebar_accent:c_sidebar_button, iL, sT + (uH * 8), iW/2, iH, tH); + drawButton("Hide", (!drawFPS)? c_sidebar_accent:c_sidebar_button, iL + (iW/2), sT + (uH * 8), iW/2, iH, tH); + drawRectangle(c_sidebar_divider, iL + (iW / 2), sT + (uH * 8) + (1 * uimult), 1 * uimult, iH - (2 * uimult)); // Turn useful instructions on/off - drawHeading("Usage Instructions", iL, sT + (uH * 10), iW, tH); - drawButton("Show", (showInstructions)? c_sidebar_accent:c_sidebar_button, iL, sT + (uH * 11), iW/2, iH, tH); - drawButton("Hide", (!showInstructions)? c_sidebar_accent:c_sidebar_button, iL + (iW/2), sT + (uH * 11), iW/2, iH, tH); - drawRectangle(c_sidebar_divider, iL + (iW / 2), sT + (uH * 11) + (1 * uimult), 1 * uimult, iH - (2 * uimult)); + drawHeading("Usage Instructions", iL, sT + (uH * 9.5), iW, tH); + drawButton("Show", (showInstructions)? c_sidebar_accent:c_sidebar_button, iL, sT + (uH * 10.5), iW/2, iH, tH); + drawButton("Hide", (!showInstructions)? c_sidebar_accent:c_sidebar_button, iL + (iW/2), sT + (uH * 10.5), iW/2, iH, tH); + drawRectangle(c_sidebar_divider, iL + (iW / 2), sT + (uH * 10.5) + (1 * uimult), 1 * uimult, iH - (2 * uimult)); - drawHeading("Serial Port", iL, sT + (uH * 12.5), iW, tH); + drawHeading("Serial Port", iL, sT + (uH * 12), iW, tH); color c_serial_items = c_sidebar_text; if (serialConnected) c_serial_items = c_sidebar_button; - drawDatabox("Baud: " + baudRate, c_serial_items, iL, sT + (uH * 13.5), iW, iH, tH); - drawDatabox("Line Ending: " + ((lineEnding == '\r')? "CR":"NL"), c_serial_items, iL, sT + (uH * 14.5), iW, iH, tH); - drawDatabox("Parity: " + serialParity, c_serial_items, iL, sT + (uH * 15.5), iW, iH, tH); - drawDatabox("Data Bits: " + serialDatabits, c_serial_items, iL, sT + (uH * 16.5), iW, iH, tH); - drawDatabox("Stop Bits: " + serialStopbits, c_serial_items, iL, sT + (uH * 17.5), iW, iH, tH); + drawDatabox("Baud: " + baudRate, c_serial_items, iL, sT + (uH * 13), iW, iH, tH); + drawDatabox("Line Ending: " + ((lineEnding == '\r')? "CR":"NL"), c_serial_items, iL, sT + (uH * 14), iW, iH, tH); + drawDatabox("Parity: " + serialParity, c_serial_items, iL, sT + (uH * 15), iW, iH, tH); + drawDatabox("Data Bits: " + serialDatabits, c_serial_items, iL, sT + (uH * 16), iW, iH, tH); + drawDatabox("Stop Bits: " + serialStopbits, c_serial_items, iL, sT + (uH * 17), iW, iH, tH); // Save preferences - drawHeading("User Preferences", iL, sT + (uH * 19), iW, tH); - if (unsavedChanges) drawButton("Save Settings", c_sidebar_button, iL, sT + (uH * 20), iW, iH, tH); - else drawDatabox("Save Settings", c_sidebar_button, iL, sT + (uH * 20), iW, iH, tH); + drawHeading("User Preferences", iL, sT + (uH * 18.5), iW, tH); + if (unsavedChanges) drawButton("Save Settings", c_sidebar_button, iL, sT + (uH * 19.5), iW, iH, tH); + else drawDatabox("Save Settings", c_sidebar_button, iL, sT + (uH * 19.5), iW, iH, tH); if (checkDefault()) { - drawButton("Reset to Default", c_sidebar_button, iL, sT + (uH * 21), iW, iH, tH); + drawButton("Reset to Default", c_sidebar_button, iL, sT + (uH * 20.5), iW, iH, tH); } else { - drawDatabox("Reset to Default", c_sidebar_button, iL, sT + (uH * 21), iW, iH, tH); + drawDatabox("Reset to Default", c_sidebar_button, iL, sT + (uH * 20.5), iW, iH, tH); } - drawButton("Exit Settings", c_sidebar_button, iL, sT + (uH * 22), iW, iH, tH); + drawButton("Exit Settings", c_sidebar_button, iL, sT + (uH * 21.5), iW, iH, tH); // Baud rate selection } else if (menuLevel == 1) { @@ -468,6 +468,11 @@ class Settings implements TabAPI { menuLevel = 0; menuScroll = 0; redrawUI = true; + } else { + settingsMenuActive = false; + menuLevel = 0; + menuScroll = 0; + redrawUI = true; } } @@ -592,7 +597,7 @@ class Settings implements TabAPI { // Main Menu if (menuLevel == 0) { // UI scaling - if ((mouseY > sT + (uH * 1.5)) && (mouseY < sT + (uH * 1.5) + iH)){ + if (menuYclick(mouseY, sT, uH, iH, 1)){ // Decrease if ((mouseX > iL) && (mouseX <= iL + iW / 4)) { if (uimult > 0.5) { @@ -611,7 +616,7 @@ class Settings implements TabAPI { } // Color Scheme 0 - Light Celeste - else if ((mouseY > sT + (uH * 4)) && (mouseY < sT + (uH * 4) + iH)) { + else if (menuYclick(mouseY, sT, uH, iH, 3.5)) { if (colorScheme != 0) { unsavedChanges = true; colorScheme = 0; @@ -620,7 +625,7 @@ class Settings implements TabAPI { } // Color Scheme 1 - Dark Gravity - else if ((mouseY > sT + (uH * 5)) && (mouseY < sT + (uH * 5) + iH)) { + else if (menuYclick(mouseY, sT, uH, iH, 4.5)) { if (colorScheme != 1) { unsavedChanges = true; colorScheme = 1; @@ -629,7 +634,7 @@ class Settings implements TabAPI { } // Color Scheme 2 - Dark Monokai - else if ((mouseY > sT + (uH * 6)) && (mouseY < sT + (uH * 6) + iH)) { + else if (menuYclick(mouseY, sT, uH, iH, 5.5)) { if (colorScheme != 2) { unsavedChanges = true; colorScheme = 2; @@ -638,7 +643,7 @@ class Settings implements TabAPI { } // Toggle FPS indicator - else if ((mouseY > sT + (uH * 8.5)) && (mouseY < sT + (uH * 8.5) + iH)) { + else if (menuYclick(mouseY, sT, uH, iH, 8)) { // Show if ((mouseX > iL) && (mouseX <= iL + iW / 2)) { if (!drawFPS) { @@ -659,7 +664,7 @@ class Settings implements TabAPI { } // Toggle usage instructions - else if ((mouseY > sT + (uH * 11)) && (mouseY < sT + (uH * 11) + iH)){ + else if (menuYclick(mouseY, sT, uH, iH, 10.5)){ // Show if ((mouseX > iL) && (mouseX <= iL + iW / 2)) { if (!showInstructions) { @@ -682,7 +687,7 @@ class Settings implements TabAPI { } // Baud rate selection - else if (menuYclick(mouseY, sT, uH, iH, 13.5)) { + else if (menuYclick(mouseY, sT, uH, iH, 13)) { if (!serialConnected) { menuLevel = 1; menuScroll = 0; @@ -691,7 +696,7 @@ class Settings implements TabAPI { } // Line ending selection - else if (menuYclick(mouseY, sT, uH, iH, 14.5)) { + else if (menuYclick(mouseY, sT, uH, iH, 14)) { if (!serialConnected) { menuLevel = 2; menuScroll = 0; @@ -700,7 +705,7 @@ class Settings implements TabAPI { } // Parity selection - else if (menuYclick(mouseY, sT, uH, iH, 15.5)) { + else if (menuYclick(mouseY, sT, uH, iH, 15)) { if (!serialConnected) { menuLevel = 3; menuScroll = 0; @@ -709,7 +714,7 @@ class Settings implements TabAPI { } // Data bits selection - else if (menuYclick(mouseY, sT, uH, iH, 16.5)) { + else if (menuYclick(mouseY, sT, uH, iH, 16)) { if (!serialConnected) { menuLevel = 4; menuScroll = 0; @@ -718,7 +723,7 @@ class Settings implements TabAPI { } // Stop bits selection - else if (menuYclick(mouseY, sT, uH, iH, 17.5)) { + else if (menuYclick(mouseY, sT, uH, iH, 17)) { if (!serialConnected) { menuLevel = 5; menuScroll = 0; @@ -727,12 +732,12 @@ class Settings implements TabAPI { } // Remember preferences - else if (menuYclick(mouseY, sT, uH, iH, 20)){ + else if (menuYclick(mouseY, sT, uH, iH, 19.5)){ if (unsavedChanges) saveSettings(); } // Reset preferences to default - else if (menuYclick(mouseY, sT, uH, iH, 21)){ + else if (menuYclick(mouseY, sT, uH, iH, 20.5)){ if (checkDefault()) { drawFPS = false; showInstructions = true; @@ -754,7 +759,7 @@ class Settings implements TabAPI { } // Exit settings menu - else if (menuYclick(mouseY, sT, uH, iH, 22)){ + else if (menuYclick(mouseY, sT, uH, iH, 21.5)){ settingsMenuActive = false; redrawUI = true; } diff --git a/README.md b/README.md index aa704e3..4218829 100644 --- a/README.md +++ b/README.md @@ -97,24 +97,27 @@ A full set of instructions and documentation can be found on my website at: [htt
## Changelog +1. (9th May 2021) Version 1.2.4 + 1. ([#18](https://github.com/chillibasket/processing-grapher/issues/18)): Added button to pause/resume the "Live Graph" to make it easier to analyse real-time data. Thanks to [aka-Ani](https://github.com/aka-Ani) for suggesting this feature. + 1. Other minor UI improvements and bug fixes. 1. (1st April 2021) Version 1.2.3 1. Fixed export bug when trying to save CSV data to a file. 1. (6th March 2021) Version 1.2.2 1. Fixed bug where the first and last data-point on the Live Graph were not being displayed. 1. Improved mouse-wheel scrolling speed to better match the content. 1. (1st March 2021) Version 1.2.1 [Release] - 1. When adding labels to the graph in the "File Graph" tab, they are now stored as a new signal. + 1. ([#16](https://github.com/chillibasket/processing-grapher/issues/16)) When adding labels to the graph in the "File Graph" tab, they are now stored as a new signal. 1. Changes to the chart in the "File Graph" tab can now be saved to a new file. 1. Added option to apply filters to the signals in the "File Graph" tab. - 1. Advanced serial port settings can now be modified in the "Settings" menu. + 1. ([#13](https://github.com/chillibasket/processing-grapher/issues/13)) Advanced serial port settings can now be modified in the "Settings" menu. 1. (9th February 2021) Version 1.2.0 - 1. Updated to use the JavaFX (FX2D) renderer, which significantly reduces the processor usage of the program. + 1. ([#15](https://github.com/chillibasket/processing-grapher/issues/15)) Updated to use the JavaFX (FX2D) renderer, which significantly reduces the processor usage of the program. 1. Implemented native JavaFX pop-up dialogues while maintaining backwards compatibility with the default renderer. 1. (29th November 2020) Version 1.1.1 1. Added button on "Live Graph" tab to toggle automatic y-axis scaling on/off. - 2. Added a "Hidden" section on the "Live Graph" tab so that unwanted signals can be hidden from the real-time graphs. - 3. Added a "Settings" sidebar menu to make changing program preferences easier. - 4. Added two additional program colour schemes. + 1. Added a "Hidden" section on the "Live Graph" tab so that unwanted signals can be hidden from the real-time graphs. + 1. Added a "Settings" sidebar menu to make changing program preferences easier. + 1. Added two additional program colour schemes. 1. (7th September 2020) Version 1.1.0 [Release] 1. Updated all version number to differentiate between minor updates and larger releases. 1. Improved the way in which the axis labels are displayed on the graphs.