1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
| import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
import java.util.HashSet;
import java.util.Set;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class DemoVector extends JPanel {
private Set<Vector> vectors = new HashSet<>(); // stocke tous les vecteurs à dessiner (un set permet d'éviter d'avoir deux fois le même vecteur (en terme d'instance) à dessiner
private Rectangle2D bounds = new Rectangle2D.Double(); // l'emplacement global pris par tous les vecteurs (à calculer)
private void JPanel() {
updateBounds(); // on initalise l'emplacement global
}
public void add(Vector vector) {
// on ajoute un vecteur
if ( vector!=null ) { // pour éviter les NullPointerException en amont, on ignore les null en aval
vectors.add(vector); // on stocke le vecteur
updateBounds(); // on recalcule l'emplacement globale puisqu'on a ajouter une vecteur
repaint(); // on redessine puisqu'on a ajouter un vecteur
}
}
private void updateBounds() {
// on met à jour la taille du JPanel par rapport à son contenu (les vecteurs)
bounds = new Rectangle2D.Double(-32,-32,64,64); // un emplacement minimum de 64x64 (centré sur 0,0)
for(Vector vector : vectors) { // l'emplacement total est l'union des emplacements de chaque vecteur
bounds = bounds.createUnion(vector.getBounds());
}
double d = 1.5; // on compense le fait qu'on dessine centré
bounds = AffineTransform.getScaleInstance(d,d).createTransformedShape(bounds).getBounds2D();
setPreferredSize(new Dimension((int)bounds.getWidth(), (int)bounds.getHeight()));
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g); // on laisse le composant se dessiner normalement (pour avoir le fond entre autres)
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// exemple pour avoir le centre du repère au centre du composant:
g2d.translate(getWidth()/2d,getHeight()/2d);
// on inverse le sens du y pour avoir un repère plus "naturel" géométriquement parlant
g2d.scale(1, -1);
// on dessine le repère
g.setColor(Color.LIGHT_GRAY);
g.drawLine(-getWidth()/2, 0, getWidth()/2, 0); // horizontal
g.drawLine(0, -getHeight()/2, 0, getHeight()/2); // vertical
for(Vector vector : vectors) {
g.setColor(getForeground()); // couleur de dessin par défaut
vector.draw(g2d); // on dessine le vecteur
}
}
// le programme principal
public static void main(String[] args) {
JFrame frame = new JFrame("Exemple");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DemoVector panel = new DemoVector(); // on créé le panel
panel.setBackground(Color.BLACK); // fond noir
panel.setForeground(Color.WHITE); // trait par défaut blanc
// on ajoute 4 vecteurs pour l'exemple
panel.add(new Vector(Color.RED, 0,0,100,0)); // départ 0,0, longueur 100, angle 0°
panel.add(new Vector(Color.YELLOW.darker(), 10,10,100,45)); // départ 10,10, longueur 100, angle 45*
panel.add(new Vector(-50,-50,-30,-60)); // départ -50,-50, longueur -30 (je n'ai pas limité volontairement la norme du vecteur à une longueur positive ou nul, mais on pourrait), angle -60*
panel.add(new Vector(Color.CYAN.darker(),-50,-50,30,-60)); // le même avec la longueur à 30
frame.add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
// une classe qui représente un vecteur
public static class Vector {
private static final double POINT_SIZE = 3;
private static final double ARROW_SIZE = 10;
private static final Stroke STROKE = new BasicStroke(2); // on va dessiner les vecteurs avec un trait de 2 pixels de large
private final Shape shape; // la flêche
private final Shape start; // le point de départ
private final Paint color; // la couleur (ou plus exactement la peinture)
/**
*
* @param x
* @param y
* @param longueur
* @param angle en degrés
*/
public Vector(double x, double y, double longueur, double angle) {
this(null, x, y, longueur, angle);
}
/**
*
* @param color
* @param x
* @param y
* @param longueur
* @param angle en degrés
*/
public Vector(Paint color, double x, double y, double longueur, double angle) {
this.color=color;
// on appliquera l'angle par transformée affine (on converti l'angle en radian et on rectifie le sens pour avoir un sens horaire positif (c'est un choix) )
AffineTransform affineTransform = AffineTransform.getRotateInstance(Math.toRadians(-angle), x, y);
if ( longueur==0 ) {
shape=null; // on ne dessine pas de flêche si la longueur est 0
}
else {
// on dessine toujours la flêche horizontalement
Path2D.Double path2D = new Path2D.Double();
path2D.moveTo(x, y);
path2D.lineTo(x+longueur, y);
path2D.lineTo(x+longueur-ARROW_SIZE*Math.signum(longueur), y-ARROW_SIZE);
path2D.moveTo(x+longueur-ARROW_SIZE*Math.signum(longueur), y+ARROW_SIZE);
path2D.lineTo(x+longueur, y);
shape = affineTransform.createTransformedShape(path2D);
}
// on dessine le point comme un disque
Ellipse2D.Double point = new Ellipse2D.Double(x-POINT_SIZE,y-POINT_SIZE,POINT_SIZE*2,POINT_SIZE*2);
start = affineTransform.createTransformedShape(point);
}
public void draw(Graphics2D g2d) {
g2d.setStroke(STROKE); // on selectionne le trait
if ( color!=null ) {
g2d.setPaint(color); // on sélectionne la couleur s'il y en a une (sinon ça se dessinera dans la dernière couleur sélectionée)
}
g2d.fill(start); // on dessine le point (rempli)
if ( shape!=null ) {
g2d.draw(shape); // on dessine la flêche
}
}
public Rectangle2D getBounds() {
// on calcule l'emplacement pris par le vecteur, largeur de trait compris
Area area = new Area(STROKE.createStrokedShape(start));
if ( shape!=null ) {
area.add(new Area(STROKE.createStrokedShape(shape)));
}
return area.getBounds2D();
}
}
} |
Partager