Skip to content

Commit fdbb026

Browse files
committed
perf: optimize plotting pipeline and reduce allocations
- Reused Point2D.Double instances in Plotter to avoid per-row allocations → replaces new Point2D.Double() calls with setLocation() in hot loops - Hoisted plot type lookup out of the inner loop to reduce repeated branching - Cached FG colors locally and avoided redundant get/set calls during plotting - Avoided repeated setStroke() calls by tracking stroke width in Canvas - Avoided redundant setColor() calls by caching current FG color in Canvas - Removed unused Canvas.getGraphics() accessor and simplified image lifecycle - Switched PlotData nodes storage from Vector to ArrayList to remove synchronization overhead - Minor cleanup in line drawing path (removed redundant operations) Result: significantly reduced per-frame overhead, fewer allocations, and faster plotting, especially for large datasets and during interactive redraws. Profiled using VisualVM
1 parent 98eefc9 commit fdbb026

3 files changed

Lines changed: 35 additions & 37 deletions

File tree

src/main/java/com/babai/ssplot/math/plot/Canvas.java

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public class Canvas {
3939
private boolean axesVisible = true;
4040
private boolean axes3d = false;
4141
private int curNoTics = 10;
42+
private int strokeWidth = 1;
4243
private BufferedImage img; /* The image */
4344
private Graphics2D g;
4445
private Color fgColor, bgColor, axesColor, ticColor, titleColor;
@@ -110,16 +111,9 @@ private Graphics2D initImage(int W, int H) {
110111
return g2;
111112
}
112113

113-
public Graphics2D getGraphics() {
114-
return g;
115-
}
116-
117114
public BufferedImage getImage() {
118-
return img;
119-
}
120-
121-
public void dispose() {
122115
g.dispose();
116+
return img;
123117
}
124118

125119

@@ -138,7 +132,6 @@ public void drawPoint(Point2D.Double p, PlotData.PointType ptype, double sizeX,
138132
/** Draws a line from point q1 to point q2 */
139133
public void drawLine(Point2D.Double q10, Point2D.Double q20) {
140134
g.draw(new Line2D.Double(q10, q20));
141-
142135
}
143136

144137
/** Draws an vector, by drawing a line with a marker.*/
@@ -345,12 +338,17 @@ public void drawTitle(String title) {
345338
/*********************************** Property Getters/Setters ************************************************/
346339

347340
public void setStroke(int width) {
348-
g.setStroke(new BasicStroke(width));
341+
if (strokeWidth != width) {
342+
strokeWidth = width;
343+
g.setStroke(new BasicStroke(width));
344+
}
349345
}
350346

351347
public void setFGColor(Color c) {
352-
fgColor = c;
353-
g.setColor(c);
348+
if (!fgColor.equals(c)) {
349+
fgColor = c;
350+
g.setColor(c);
351+
}
354352
}
355353

356354
public Color getFGColor() {

src/main/java/com/babai/ssplot/math/plot/PlotData.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public String toString() {
6666
public enum PointType { SQUARE, CIRCLE };
6767

6868
private double[][] data;
69-
private Vector<Node> nodes;
69+
private List<Node> nodes;
7070
private EquationSystem system;
7171
private Axis[] axes = Axis.Cartesian.values();
7272

@@ -92,7 +92,7 @@ public enum PointType { SQUARE, CIRCLE };
9292

9393
public PlotData(double[][] extData) {
9494
data = extData;
95-
nodes = new Vector<Node>();
95+
nodes = new ArrayList<Node>();
9696

9797
// Cosmetic point properties
9898
pointType = PointType.SQUARE;
@@ -238,8 +238,8 @@ public double getMin(int dataCol) {
238238

239239
// ------------------ NODE METHODS -------------------
240240
public void addNode(Point2D.Double p, String str, Color c) { nodes.add(new Node(p, str, c)); }
241-
public Vector<Node> getNodes() { return nodes; }
242-
public void setNodes(Vector<Node> nodes) { this.nodes = nodes; }
241+
public List<Node> getNodes() { return nodes; }
242+
public void setNodes(List<Node> nodes) { this.nodes = nodes; }
243243

244244

245245
// ------------------- STRING REPRESENTATION --------------

src/main/java/com/babai/ssplot/math/plot/Plotter.java

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -71,19 +71,22 @@ private void plotData(PlotData pdata, int lastIndex) {
7171
dataCols[i] = pdata.getDataCol(i);
7272
}
7373

74-
Point2D.Double p1 = null, p2 = null;
74+
Point2D.Double p1 = new Point2D.Double(), p2 = new Point2D.Double(), pback = new Point2D.Double();
7575

7676
var dataset = pdata.getData();
7777
if (lastIndex > dataset.length) {
7878
lastIndex = dataset.length;
7979
}
8080

81-
canv.setFGColor(pdata.getFgColor());
82-
canv.setAxes3d(pdata.getColumnCount() == 3);
83-
canv.setProjection(p);
84-
canv.setStroke(pdata.ptX);
85-
Color fgColor = canv.getFGColor();
81+
Color fgColor = pdata.getFgColor();
8682
Color fgColor2 = pdata.getFgColor2();
83+
canv.setFGColor(fgColor);
84+
if (pdata.getColumnCount() == 3) {
85+
canv.setAxes3d(true);
86+
canv.setProjection(p);
87+
}
88+
canv.setStroke(pdata.ptX);
89+
var ptype = pdata.getPlotType();
8790

8891
double[] row;
8992

@@ -92,13 +95,15 @@ private void plotData(PlotData pdata, int lastIndex) {
9295
for (int i = 0; i < lastIndex; i++) {
9396
row = dataset[i];
9497

95-
switch(pdata.getPlotType()) {
98+
switch(ptype) {
9699
case VFIELD -> {
97100
// For now, it works for vector data in first four columns only
98101
// TODO custom column mapping
99102
if (row.length >= 4) {
100-
p1 = canv.cartesianToJava(new Point2D.Double(row[0], row[1]));
101-
p2 = canv.cartesianToJava(new Point2D.Double(row[2], row[3]));
103+
p1.setLocation(row[0], row[1]);
104+
p1 = canv.cartesianToJava(p1);
105+
p2.setLocation(row[2], row[3]);
106+
p2 = canv.cartesianToJava(p2);
102107
canv.drawVector(p1, p2, fgColor2);
103108
} else {
104109
System.err.println("Bad vector field data!");
@@ -127,26 +132,23 @@ private void plotData(PlotData pdata, int lastIndex) {
127132
}
128133

129134
case POINTS, LINES, LINES_POINTS -> {
130-
p2 = canv.cartesianToJava(new Point2D.Double(row[dataCols[0]], row[dataCols[1]]));
135+
p2.setLocation(row[dataCols[0]], row[dataCols[1]]);
136+
p2 = canv.cartesianToJava(p2);
131137

132138
if (p1 != null) {
133-
switch(pdata.getPlotType()) {
139+
switch(ptype) {
134140
case LINES -> canv.drawLine(p1, p2);
135141

136142
case POINTS -> canv.drawPoint(p1, PlotData.PointType.SQUARE, pdata.ptX, pdata.ptY);
137143

144+
// The line is drawn with plot FGcolor 1
145+
// the points on the top is drawn with plot FGcolor 2
146+
// FIXME generalization needed
138147
case LINES_POINTS -> {
139-
Point2D.Double pback = new Point2D.Double(
140-
p1.getX() - (pdata.ptX+4)/2,
141-
p1.getY() - (pdata.ptY+4)/2);
142-
// The line is drawn with plot FGcolor 1
143-
// the points on the top is drawn with plot FGcolor 2
144-
// FIXME generalization needed
148+
pback.setLocation(p1.x - (pdata.ptX+4)/2, p1.y - (pdata.ptY+4)/2);
145149
canv.drawLine(p1, p2);
146-
147150
canv.setFGColor(fgColor2);
148151
canv.drawPoint(pback, PlotData.PointType.CIRCLE, pdata.ptX+4, pdata.ptY+4);
149-
canv.setFGColor(fgColor);
150152
}
151153

152154
default -> {}
@@ -157,8 +159,6 @@ private void plotData(PlotData pdata, int lastIndex) {
157159
}
158160
}
159161
}
160-
161-
canv.setStroke(1);
162162
}
163163

164164
private void plotOthers(PlotData pdata) {

0 commit comments

Comments
 (0)