Skip to content

Commit f1f9542

Browse files
committed
Add a demo of the Jzy3D library
1 parent 7759d6f commit f1f9542

File tree

2 files changed

+169
-0
lines changed

2 files changed

+169
-0
lines changed

pom.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,14 @@
177177
<artifactId>tablesaw-jsplot</artifactId>
178178
<version>${tablesaw-jsplot.version}</version>
179179
</dependency>
180+
<dependency>
181+
<groupId>org.jzy3d</groupId>
182+
<artifactId>jzy3d-core</artifactId>
183+
</dependency>
184+
<dependency>
185+
<groupId>org.jzy3d</groupId>
186+
<artifactId>jzy3d-native-jogl-awt</artifactId>
187+
</dependency>
180188

181189
<!-- Test scope dependencies -->
182190
<dependency>
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
package org.scijava.ui.swing.plot;
2+
3+
import org.jzy3d.analysis.AWTAbstractAnalysis;
4+
import org.jzy3d.chart.Chart;
5+
import org.jzy3d.chart.factories.AWTChartFactory;
6+
import org.jzy3d.colors.Color;
7+
import org.jzy3d.colors.ColorMapper;
8+
import org.jzy3d.colors.colormaps.ColorMapRainbow;
9+
import org.jzy3d.maths.Coord3d;
10+
import org.jzy3d.maths.Range;
11+
import org.jzy3d.plot3d.builder.Mapper;
12+
import org.jzy3d.plot3d.builder.SurfaceBuilder;
13+
import org.jzy3d.plot3d.builder.concrete.OrthonormalGrid;
14+
import org.jzy3d.plot3d.primitives.Scatter;
15+
import org.jzy3d.plot3d.primitives.Shape;
16+
import org.jzy3d.plot3d.rendering.canvas.Quality;
17+
18+
import java.util.ArrayList;
19+
import java.util.List;
20+
import java.util.Random;
21+
22+
public class Jzy3DDemo extends AWTAbstractAnalysis {
23+
private double[][] data;
24+
private int gridSize = 50;
25+
private double bandwidthX;
26+
private double bandwidthY;
27+
28+
public Jzy3DDemo() {
29+
super();
30+
setFactory(new AWTChartFactory());
31+
}
32+
33+
public static void main(String[] args) throws Exception {
34+
Jzy3DDemo demo = new Jzy3DDemo();
35+
demo.init();
36+
demo.getChart().open("Bivariate KDE", 800, 600);
37+
}
38+
39+
@Override
40+
public Chart initializeChart() {
41+
Quality quality = Quality.Advanced();
42+
// Initialize the chart
43+
Chart chart = getFactory().newChart(quality);
44+
this.chart = chart; // Store the chart in the parent class
45+
return chart;
46+
}
47+
48+
@Override
49+
public void init() throws Exception {
50+
// First initialize the chart
51+
Chart chart = initializeChart();
52+
53+
// Generate sample data - two clusters
54+
int n = 1000;
55+
Random rand = new Random(42);
56+
data = new double[n][2];
57+
58+
// Generate two clusters
59+
for (int i = 0; i < n; i++) {
60+
if (rand.nextDouble() < 0.6) {
61+
// First cluster
62+
data[i][0] = rand.nextGaussian() * 0.5 + 2;
63+
data[i][1] = rand.nextGaussian() * 0.5 + 2;
64+
} else {
65+
// Second cluster
66+
data[i][0] = rand.nextGaussian() * 0.3 + 4;
67+
data[i][1] = rand.nextGaussian() * 0.3 + 4;
68+
}
69+
}
70+
71+
// Calculate grid boundaries
72+
double minX = Double.POSITIVE_INFINITY;
73+
double maxX = Double.NEGATIVE_INFINITY;
74+
double minY = Double.POSITIVE_INFINITY;
75+
double maxY = Double.NEGATIVE_INFINITY;
76+
77+
for (double[] point : data) {
78+
minX = Math.min(minX, point[0]);
79+
maxX = Math.max(maxX, point[0]);
80+
minY = Math.min(minY, point[1]);
81+
maxY = Math.max(maxY, point[1]);
82+
}
83+
84+
// Add padding
85+
double padX = (maxX - minX) * 0.1;
86+
double padY = (maxY - minY) * 0.1;
87+
minX -= padX;
88+
maxX += padX;
89+
minY -= padY;
90+
maxY += padY;
91+
92+
// Calculate bandwidth using Silverman's rule
93+
double sdX = calculateSD(data, 0);
94+
double sdY = calculateSD(data, 1);
95+
bandwidthX = 1.06 * sdX * Math.pow(n, -0.2);
96+
bandwidthY = 1.06 * sdY * Math.pow(n, -0.2);
97+
98+
// Create KDE mapper
99+
Mapper mapper = new Mapper() {
100+
@Override
101+
public double f(double x, double y) {
102+
double sum = 0;
103+
for (double[] point : data) {
104+
double zx = (x - point[0]) / bandwidthX;
105+
double zy = (y - point[1]) / bandwidthY;
106+
sum += Math.exp(-0.5 * (zx * zx + zy * zy)) /
107+
(2 * Math.PI * bandwidthX * bandwidthY);
108+
}
109+
return sum / data.length;
110+
}
111+
};
112+
113+
// Create surface
114+
Range xRange = new Range((float)minX, (float)maxX);
115+
Range yRange = new Range((float)minY, (float)maxY);
116+
117+
OrthonormalGrid grid = new OrthonormalGrid(xRange, gridSize, yRange, gridSize);
118+
Shape surface = new SurfaceBuilder().orthonormal(grid, mapper);
119+
120+
// Style the surface
121+
surface.setColorMapper(new ColorMapper(new ColorMapRainbow(), surface.getBounds().getZmin(), surface.getBounds().getZmax()));
122+
surface.setWireframeDisplayed(true);
123+
surface.setWireframeColor(Color.BLACK);
124+
chart.add(surface);
125+
126+
// Create scatter plot of original data
127+
List<Coord3d> points = new ArrayList<>();
128+
for (double[] point : data) {
129+
points.add(new Coord3d(point[0], point[1], 0));
130+
}
131+
Scatter scatter = new Scatter(points.toArray(new Coord3d[0]), Color.BLACK);
132+
chart.add(scatter);
133+
}
134+
135+
private static double calculateSD(double[][] data, int dimension) {
136+
double mean = 0;
137+
for (double[] point : data) {
138+
mean += point[dimension];
139+
}
140+
mean /= data.length;
141+
142+
double variance = 0;
143+
for (double[] point : data) {
144+
double diff = point[dimension] - mean;
145+
variance += diff * diff;
146+
}
147+
variance /= (data.length - 1);
148+
149+
return Math.sqrt(variance);
150+
}
151+
152+
@Override
153+
public String getName() {
154+
return "Bivariate KDE Example";
155+
}
156+
157+
@Override
158+
public String getPitch() {
159+
return "2D Kernel Density Estimation visualization";
160+
}
161+
}

0 commit comments

Comments
 (0)