Skip to content

Commit da2da78

Browse files
committed
Implement saving settings to file
1 parent 02d4e22 commit da2da78

File tree

7 files changed

+409
-27
lines changed

7 files changed

+409
-27
lines changed

src/main/java/ovh/neziw/visualizer/RegressionVisualizer.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ public class RegressionVisualizer extends JFrame {
5151
private final ChartUpdater chartUpdater;
5252
private final TableFileManager fileManager;
5353
private final ChartSettings chartSettings;
54+
private final ChartSettingsPanel settingsPanel;
5455

5556
public RegressionVisualizer() {
5657
this.initializeFrame();
@@ -59,8 +60,9 @@ public RegressionVisualizer() {
5960
this.dataTable = this.createTable();
6061
this.chart = new RegressionChart(this.chartSettings);
6162
this.chartUpdater = new ChartUpdater(this.tableModel, this.chart);
62-
this.fileManager = this.createFileManager();
6363
this.setupTableListeners();
64+
this.settingsPanel = this.createSettingsPanel();
65+
this.fileManager = this.createFileManager();
6466
this.setupUI();
6567
}
6668

@@ -89,7 +91,15 @@ private JTable createTable() {
8991

9092
private TableFileManager createFileManager() {
9193
final FileDialogManager dialogManager = new FileDialogManager(this);
92-
return new TableFileManager(this.tableModel, dialogManager, this::updateChart);
94+
return new TableFileManager(this.tableModel, dialogManager, this::onDataLoaded, this.chartSettings);
95+
}
96+
97+
private void onDataLoaded() {
98+
if (this.settingsPanel != null) {
99+
this.settingsPanel.refreshUI();
100+
}
101+
this.chart.applySettings();
102+
this.updateChart();
93103
}
94104

95105
private void setupTableListeners() {
@@ -105,11 +115,10 @@ private void setupUI() {
105115
final JScrollPane tableScrollPane = this.createTableScrollPane();
106116
final JSplitPane splitPane = this.createSplitPane(tableScrollPane);
107117
final ButtonPanel buttonPanel = this.createButtonPanel();
108-
final ChartSettingsPanel settingsPanel = this.createSettingsPanel();
109118

110119
final JPanel topPanel = new JPanel(new BorderLayout());
111120
topPanel.add(buttonPanel, BorderLayout.WEST);
112-
topPanel.add(settingsPanel, BorderLayout.CENTER);
121+
topPanel.add(this.settingsPanel, BorderLayout.CENTER);
113122

114123
final JPanel mainPanel = new JPanel(new BorderLayout());
115124
mainPanel.add(topPanel, BorderLayout.NORTH);
@@ -123,7 +132,7 @@ private void setupUI() {
123132
private ChartSettingsPanel createSettingsPanel() {
124133
return new ChartSettingsPanel(this.chartSettings, () -> {
125134
this.chart.applySettings();
126-
this.chartUpdater.updateChart();
135+
this.updateChart();
127136
});
128137
}
129138

src/main/java/ovh/neziw/visualizer/gui/ChartSettingsPanel.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public class ChartSettingsPanel extends JPanel {
4141
private JSpinner paddingSpinner;
4242
private JButton dataPointColorButton;
4343
private JButton regressionLineColorButton;
44+
private JCheckBox dashedLineCheckBox;
4445

4546
public ChartSettingsPanel(final ChartSettings settings, final Runnable onSettingsChanged) {
4647
this.settings = settings;
@@ -121,13 +122,19 @@ private void setupPanel() {
121122
final JButton shapeButton = this.createShapeButton();
122123
this.add(shapeButton);
123124

124-
final JCheckBox dashedLineCheckBox = new JCheckBox("Linia przerywana",
125+
this.dashedLineCheckBox = new JCheckBox("Linia przerywana",
125126
this.settings.isDashedLine());
126-
dashedLineCheckBox.addActionListener(e -> {
127-
this.settings.setDashedLine(dashedLineCheckBox.isSelected());
127+
this.dashedLineCheckBox.addActionListener(e -> {
128+
this.settings.setDashedLine(this.dashedLineCheckBox.isSelected());
128129
this.onSettingsChanged.run();
129130
});
130-
this.add(dashedLineCheckBox);
131+
this.add(this.dashedLineCheckBox);
132+
}
133+
134+
public void refreshUI() {
135+
this.paddingSpinner.setValue(this.settings.getPaddingPercent() * 100.0);
136+
this.updateButtonColors();
137+
this.dashedLineCheckBox.setSelected(this.settings.isDashedLine());
131138
}
132139

133140
private JButton createColorButton(final String text,

src/main/java/ovh/neziw/visualizer/io/TableFileManager.java

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,27 +25,33 @@
2525

2626
import java.io.File;
2727
import java.io.IOException;
28-
import java.util.List;
2928
import javax.swing.JFrame;
3029
import javax.swing.JOptionPane;
3130
import ovh.neziw.visualizer.DataTableModel;
31+
import ovh.neziw.visualizer.gui.ChartSettings;
3232
import ovh.neziw.visualizer.gui.FileDialogManager;
33+
import ovh.neziw.visualizer.serialization.ChartSettingsConverter;
34+
import ovh.neziw.visualizer.serialization.ChartSettingsData;
3335
import ovh.neziw.visualizer.serialization.JsonDataSerializer;
36+
import ovh.neziw.visualizer.serialization.SavedData;
3437

3538
public class TableFileManager {
3639

3740
private final DataTableModel tableModel;
3841
private final JsonDataSerializer serializer;
3942
private final FileDialogManager dialogManager;
4043
private final Runnable onDataLoaded;
44+
private final ChartSettings chartSettings;
4145

4246
public TableFileManager(final DataTableModel tableModel,
4347
final FileDialogManager dialogManager,
44-
final Runnable onDataLoaded) {
48+
final Runnable onDataLoaded,
49+
final ChartSettings chartSettings) {
4550
this.tableModel = tableModel;
4651
this.serializer = new JsonDataSerializer();
4752
this.dialogManager = dialogManager;
4853
this.onDataLoaded = onDataLoaded;
54+
this.chartSettings = chartSettings;
4955
}
5056

5157
public void saveToFile(final JFrame parentFrame) {
@@ -54,8 +60,11 @@ public void saveToFile(final JFrame parentFrame) {
5460
return;
5561
}
5662
try {
57-
final List<DataTableModel.DataPoint> dataPoints = this.tableModel.getAllDataPoints();
58-
this.serializer.writeToFile(file, dataPoints);
63+
final SavedData savedData = new SavedData(
64+
this.tableModel.getAllDataPoints(),
65+
ChartSettingsConverter.toData(this.chartSettings)
66+
);
67+
this.serializer.writeToFile(file, savedData);
5968
this.showSuccessMessage(parentFrame,
6069
"Dane zostały zapisane do pliku: " + file.getName());
6170
} catch (final IOException exception) {
@@ -70,8 +79,13 @@ public void loadFromFile(final JFrame parentFrame) {
7079
return;
7180
}
7281
try {
73-
final List<DataTableModel.DataPoint> dataPoints = this.serializer.readFromFile(file);
74-
this.tableModel.setAllDataPoints(dataPoints);
82+
final SavedData savedData = this.serializer.readFromFile(file);
83+
this.tableModel.setAllDataPoints(savedData.getDataPoints());
84+
85+
if (savedData.getChartSettings() != null) {
86+
ChartSettingsConverter.applyToSettings(savedData.getChartSettings(), this.chartSettings);
87+
}
88+
7589
this.onDataLoaded.run();
7690
this.showSuccessMessage(parentFrame,
7791
"Dane zostały wczytane z pliku: " + file.getName());
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
/*
2+
* This file is part of "linear-regression-visualizer", licensed under MIT License.
3+
*
4+
* Copyright (c) 2025 neziw
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
package ovh.neziw.visualizer.serialization;
25+
26+
import java.awt.Color;
27+
import java.awt.Shape;
28+
import ovh.neziw.visualizer.gui.ChartSettings;
29+
30+
public final class ChartSettingsConverter {
31+
32+
public static ChartSettingsData toData(final ChartSettings settings) {
33+
return new ChartSettingsData(
34+
settings.getPaddingPercent(),
35+
settings.getDataPointColor().getRGB(),
36+
settings.getRegressionLineColor().getRGB(),
37+
shapeToString(settings.getDataPointShape()),
38+
settings.isDashedLine()
39+
);
40+
}
41+
42+
public static void applyToSettings(final ChartSettingsData data, final ChartSettings settings) {
43+
settings.setPaddingPercent(data.getPaddingPercent());
44+
settings.setDataPointColor(new Color(data.getDataPointColorRgb(), true));
45+
settings.setRegressionLineColor(new Color(data.getRegressionLineColorRgb(), true));
46+
settings.setDataPointShape(stringToShape(data.getPointShapeType()));
47+
settings.setDashedLine(data.isDashedLine());
48+
}
49+
50+
private static String shapeToString(final Shape shape) {
51+
if (shape == null) {
52+
return "CIRCLE";
53+
}
54+
final String className = shape.getClass().getSimpleName();
55+
switch (className) {
56+
case "Ellipse2D$Double":
57+
return "CIRCLE";
58+
case "Rectangle2D$Double":
59+
final java.awt.geom.Rectangle2D rect = (java.awt.geom.Rectangle2D) shape;
60+
if (rect.getWidth() > 6.5) {
61+
return "SQUARE_LARGE";
62+
}
63+
return "SQUARE";
64+
case "Path2D$Double":
65+
if (isDiamond(shape)) {
66+
return "DIAMOND";
67+
} else if (isTriangle(shape)) {
68+
return "TRIANGLE";
69+
} else if (isStar(shape)) {
70+
return "STAR";
71+
}
72+
break;
73+
}
74+
return "CIRCLE";
75+
}
76+
77+
private static Shape stringToShape(final String shapeType) {
78+
if (shapeType == null) {
79+
return createCircle();
80+
}
81+
switch (shapeType) {
82+
case "SQUARE":
83+
return new java.awt.geom.Rectangle2D.Double(-3, -3, 6, 6);
84+
case "SQUARE_LARGE":
85+
return new java.awt.geom.Rectangle2D.Double(-4, -4, 8, 8);
86+
case "DIAMOND":
87+
return createDiamond();
88+
case "TRIANGLE":
89+
return createTriangle();
90+
case "STAR":
91+
return createStar();
92+
default:
93+
return createCircle();
94+
}
95+
}
96+
97+
private static Shape createCircle() {
98+
return new java.awt.geom.Ellipse2D.Double(-3, -3, 6, 6);
99+
}
100+
101+
private static Shape createDiamond() {
102+
final java.awt.geom.Path2D.Double diamond = new java.awt.geom.Path2D.Double();
103+
diamond.moveTo(0, -4);
104+
diamond.lineTo(4, 0);
105+
diamond.lineTo(0, 4);
106+
diamond.lineTo(-4, 0);
107+
diamond.closePath();
108+
return diamond;
109+
}
110+
111+
private static Shape createTriangle() {
112+
final java.awt.geom.Path2D.Double triangle = new java.awt.geom.Path2D.Double();
113+
triangle.moveTo(0, -4);
114+
triangle.lineTo(-4, 4);
115+
triangle.lineTo(4, 4);
116+
triangle.closePath();
117+
return triangle;
118+
}
119+
120+
private static Shape createStar() {
121+
final java.awt.geom.Path2D.Double star = new java.awt.geom.Path2D.Double();
122+
final int outerRadius = 4;
123+
final int innerRadius = 2;
124+
final int points = 5;
125+
for (int i = 0; i < points * 2; i++) {
126+
final double angle = Math.PI * i / points - Math.PI / 2;
127+
final int radius = i % 2 == 0 ? outerRadius : innerRadius;
128+
final double x = Math.cos(angle) * radius;
129+
final double y = Math.sin(angle) * radius;
130+
if (i == 0) {
131+
star.moveTo(x, y);
132+
} else {
133+
star.lineTo(x, y);
134+
}
135+
}
136+
star.closePath();
137+
return star;
138+
}
139+
140+
private static boolean isDiamond(final Shape shape) {
141+
if (!(shape instanceof java.awt.geom.Path2D.Double)) {
142+
return false;
143+
}
144+
final java.awt.geom.Path2D.Double path = (java.awt.geom.Path2D.Double) shape;
145+
final java.awt.geom.PathIterator it = path.getPathIterator(null);
146+
final double[] coords = new double[6];
147+
int pointCount = 0;
148+
while (!it.isDone()) {
149+
final int type = it.currentSegment(coords);
150+
if (type == java.awt.geom.PathIterator.SEG_MOVETO || type == java.awt.geom.PathIterator.SEG_LINETO) {
151+
pointCount++;
152+
}
153+
it.next();
154+
}
155+
return pointCount == 4;
156+
}
157+
158+
private static boolean isTriangle(final Shape shape) {
159+
if (!(shape instanceof java.awt.geom.Path2D.Double)) {
160+
return false;
161+
}
162+
final java.awt.geom.Path2D.Double path = (java.awt.geom.Path2D.Double) shape;
163+
final java.awt.geom.PathIterator it = path.getPathIterator(null);
164+
final double[] coords = new double[6];
165+
int pointCount = 0;
166+
while (!it.isDone()) {
167+
final int type = it.currentSegment(coords);
168+
if (type == java.awt.geom.PathIterator.SEG_MOVETO || type == java.awt.geom.PathIterator.SEG_LINETO) {
169+
pointCount++;
170+
}
171+
it.next();
172+
}
173+
return pointCount == 3;
174+
}
175+
176+
private static boolean isStar(final Shape shape) {
177+
if (!(shape instanceof java.awt.geom.Path2D.Double)) {
178+
return false;
179+
}
180+
final java.awt.geom.Path2D.Double path = (java.awt.geom.Path2D.Double) shape;
181+
final java.awt.geom.PathIterator it = path.getPathIterator(null);
182+
final double[] coords = new double[6];
183+
int pointCount = 0;
184+
while (!it.isDone()) {
185+
final int type = it.currentSegment(coords);
186+
if (type == java.awt.geom.PathIterator.SEG_MOVETO || type == java.awt.geom.PathIterator.SEG_LINETO) {
187+
pointCount++;
188+
}
189+
it.next();
190+
}
191+
return pointCount == 10;
192+
}
193+
}

0 commit comments

Comments
 (0)