Dessiner un graphique vectoriel en Java 7
J'essaie de créer du code dans Swing / JPanel qui prend des variables (modifiables) et dessine un graphique comme ceci:
Je n'ai aucune idée par où commencer. L'idée est, il y aurait des points clés sur l'hexagone, une pour chaque variable, et une ligne serait de tracer entre chaque point, puis l'espace à l'intérieur de la forme personnalisée serait ombragée. Des idées?
Prototype de code
import java.awt.*;
import javax.swing.*;
public class DrawPolygon extends JPanel {
int xOffset = 0;
int yOffset = 0;
int sizeModifer = 50;
int numOfPoints = 8;
int linePosition = 80;
double sizeMultiplier = 1;
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Polygon[] polygons = new Polygon[5];
for (int i = 0; i < polygons.length; i++){
polygons[i] = new Polygon();
for (int q = 0; q < numOfPoints; q++) {
polygons[i].addPoint(
xOffset + (int) (linePosition + (sizeModifer*sizeMultiplier)
* Math.cos(q * 2 * Math.PI / numOfPoints)),
yOffset + (int) ((linePosition - 10) + (sizeModifer*sizeMultiplier)
* Math.sin(q * 2 * Math.PI / numOfPoints)));
}//built points
sizeMultiplier = sizeMultiplier - 0.2;
}//build polygon arrays
Polygon innerPolygon = new Polygon();
for (int i = 0; i < numOfPoints; i++) {
int randomRange = 5 + (int) (Math.random() * ((sizeModifer - 5) + 1));
innerPolygon.addPoint(
xOffset + (int) (linePosition + randomRange
* Math.cos(i * 2 * Math.PI / numOfPoints)),
yOffset + (int) ((linePosition - 10) + randomRange
* Math.sin(i * 2 * Math.PI / numOfPoints)));
}
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setStroke(new BasicStroke(1));
for (int i = 0; i < polygons.length; i++){
g2d.setColor(Color.green);
g2d.fill(polygons[i]);
g2d.setColor(Color.black);
g2d.draw(polygons[i]);
}//draw polygons from arrays
double distanceModifier = 1;
for (int i = 0; i < numOfPoints; i++) {
g2d.drawString("test"+(i+1),
xOffset + (int) (linePosition + (sizeModifer*distanceModifier)
* Math.cos(i * 2 * Math.PI / numOfPoints)),
yOffset + (int) ((linePosition - 10) + (sizeModifer*distanceModifier)
* Math.sin(i * 2 * Math.PI / numOfPoints)));
distanceModifier = distanceModifier +.01;
}
g2d.setColor(new Color(255,213,200,90));
g2d.fill(innerPolygon);
g2d.setColor(Color.red);
g2d.draw(innerPolygon);
}
@Override
public Dimension getPreferredSize() {
// TODO calculate size based on geometry
return new Dimension(160, 160);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle("Show Different Polygons");
frame.add(new DrawPolygon());
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
});
}
}
3 answers
JFreeChart
comprend un SpiderWebPlot
, sur la photo dans la démo, en vertu de Divers et discuté ici. Alors que SpiderWebChartDemo1
contient cinq catégories, votre image semble en contenir six.
Bien sûr. Je suis sûr qu'il doit déjà y avoir des bibliothèques Java pour le faire; par exemple, Sonar montre un diagramme similaire à celui-ci. Il est généralement préférable d'utiliser le composant de quelqu'un d'autre au lieu d'écrire le vôtre si possible. Habituellement, j'utiliserais JFreeChart, mais il semble que cela n'ait pas un tel composant.
Si vous voulez écrire le vôtre, il vous suffit de faire quelques calculs simples de l'emplacement de chaque point sur le Web. Du centre, vous commenceriez par dessiner un ligne à 0 degrés. Ensuite, chaque ligne radiale suivante serait tournée par 2*pi/n
radians. Vous pouvez appliquer une trigonométrie simple pour déterminer les coordonnées cartésiennes (x, y) à partir des angles et du rayon de chaque hexagone. Enfin, vous superposeriez la zone remplie. Donc, un algorithme pourrait être quelque chose comme ça. Dans le pseudocode suivant, le paramètre values
est une liste de petits entiers positifs représentant chaque point du polygone comme un décalage par rapport au centre du diagramme. Pour un hexagon, par conséquent, vous auriez six valeurs dans cette liste.
function drawGraph(values):
steps = maximum value from values
lines = number of values
webWidth = min(width, height)
centerX = width / 2
centerY = height / 2
// Draw lines
for radialLineNumber in 0..lines:
angle = radialLineNumber * 2 * pi / lines;
draw line from (centerX, centerY) to (centerX + cos(angle)*webWidth, centerY + sin(angle) * webWidth))
edgePolygon = blank polygon
for edgeNumber in 1..steps:
x = centerX + cos(angle) * webWidth * edgeNumber / steps
y = centerY + sin(angle) * webWidth * edgeNumber / steps
add (x, y) to edgePolygon
stroke polygon edgePolygon
// Draw polygon
areaPolygon = blank polygon
radialLineNumber = 0
for value in values:
angle = radialLineNumber * 2 * pi / lines;
x = centerX + cos(angle) * value * webWidth / steps
y = centerY + sin(angle) * value * webWidth / steps
add (x, y) to areaPolygon
fill polygon areaPolygon
Je vous laisse le soin de traduire ce pseudocode en un vrai JComponent
. Toutes les fonctions que j'ai utilisées sont soit sur Math
ou Graphics2D
. Vous pouvez définir l'opacité du polygone de zone avant de le dessiner.
Quelques notes sur votre exemple:
Utiliser les fonctions de
Graphics2D
pour rendre votrePolygon
, qui implémenteShape
.Remplacer
getPreferredSize()
, tel que discuté ici.Les objets Swing GUI doivent être construits et manipulés uniquement sur le thread de répartition des événements .
import java.awt.*;
import javax.swing.*;
public class DrawPolygon extends JPanel {
int xOffset = 0;
int yOffset = 0;
int sizeModifer = 50;
int numOfPoints = 8;
int linePosition = 80;
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Polygon outerPolygon = new Polygon();
for (int i = 0; i < numOfPoints; i++) {
outerPolygon.addPoint(
xOffset + (int) (linePosition + sizeModifer
* Math.cos(i * 2 * Math.PI / numOfPoints)),
yOffset + (int) ((linePosition - 10) + sizeModifer
* Math.sin(i * 2 * Math.PI / numOfPoints)));
}
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.cyan);
g2d.fill(outerPolygon);
g2d.setStroke(new BasicStroke(2));
g2d.setColor(Color.red);
g2d.draw(outerPolygon);
}
@Override
public Dimension getPreferredSize() {
// TODO calculate size based on geometry
return new Dimension(160, 160);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle("Show Different Polygons");
frame.add(new DrawPolygon());
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
});
}
}
Addendum: Sur la base des commentaires, cette révision restaure l'original innerPolygon
.
import java.awt.*;
import javax.swing.*;
public class DrawPolygon extends JPanel {
int xOffset = 0;
int yOffset = 0;
int sizeModifer = 50;
int numOfPoints = 8;
int linePosition = 80;
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Polygon outerPolygon = new Polygon();
for (int i = 0; i < numOfPoints; i++) {
outerPolygon.addPoint(
xOffset + (int) (linePosition + sizeModifer
* Math.cos(i * 2 * Math.PI / numOfPoints)),
yOffset + (int) ((linePosition - 10) + sizeModifer
* Math.sin(i * 2 * Math.PI / numOfPoints)));
}
Polygon innerPolygon = new Polygon();
for (int i = 0; i < numOfPoints; i++) {
int randomRange = 5 + (int) (Math.random() * ((sizeModifer - 5) + 1));
innerPolygon.addPoint(
xOffset + (int) (linePosition + randomRange
* Math.cos(i * 2 * Math.PI / numOfPoints)),
yOffset + (int) ((linePosition - 10) + randomRange
* Math.sin(i * 2 * Math.PI / numOfPoints)));
}
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setStroke(new BasicStroke(2));
g2d.setColor(Color.cyan);
g2d.fill(outerPolygon);
g2d.setColor(Color.red);
g2d.draw(outerPolygon);
g2d.setColor(Color.blue);
g2d.fill(innerPolygon);
g2d.setColor(Color.red);
g2d.draw(innerPolygon);
}
@Override
public Dimension getPreferredSize() {
// TODO calculate size based on geometry
return new Dimension(160, 160);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle("Show Different Polygons");
frame.add(new DrawPolygon());
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
});
}
}