linea - ¿Cómo dibujar una línea de flecha dirigida en Java?
dibujar linea en java con mouse (4)
Aunque la publicación de Pete es increíblemente completa, estoy usando este método para dibujar una línea muy simple con un pequeño triángulo en su extremo.
// create an AffineTransform
// and a triangle centered on (0,0) and pointing downward
// somewhere outside Swing''s paint loop
AffineTransform tx = new AffineTransform();
Line2D.Double line = new Line2D.Double(0,0,100,100);
Polygon arrowHead = new Polygon();
arrowHead.addPoint( 0,5);
arrowHead.addPoint( -5, -5);
arrowHead.addPoint( 5,-5);
// [...]
private void drawArrowHead(Graphics2D g2d) {
tx.setToIdentity();
double angle = Math.atan2(line.y2-line.y1, line.x2-line.x1);
tx.translate(line.x2, line.y2);
tx.rotate((angle-Math.PI/2d));
Graphics2D g = (Graphics2D) g2d.create();
g.setTransform(tx);
g.fill(arrowHead);
g.dispose();
}
Quiero dibujar una línea de flecha dirigida a través de Java.
Actualmente estoy usando la clase java.awt.Line2D.Double
para dibujar una línea
g2.setStroke(new BasicStroke(2.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); // g2 is an instance of Graphics2D
g2.draw(new Line2D.Double(x1,y1,x2,y2));
Pero solo aparece la línea y no aparece ninguna flecha dirigida. BasicStroke.Join_BEVEL
se utiliza para dibujar una flecha dirigida. Se aplica cuando dos segmentos de línea se encuentran.
La línea que estoy dibujando se encuentra con el borde de un rectángulo pero no se dibuja una flecha dirigida. Sólo se dibuja una línea simple.
¿Hay algo que me esté perdiendo?
El bisel se dibuja entre segmentos en una polilínea si están en ciertos ángulos. No tiene importancia si está dibujando una línea que se dibuja cerca de otros píxeles que son de un color determinado. Una vez que haya dibujado el rectángulo, el objeto Gráficos no conoce el rectángulo, (en efecto) Sólo mantiene los píxeles. (o más bien la imagen o la ventana del sistema operativo contiene los píxeles).
Para dibujar una flecha simple, trace una línea para el tallo mientras lo hace, luego una polilínea para la vee. Las flechas más bonitas y bonitas tienen lados curvos y están rellenas.
Probablemente no quiera usar el bisel para la cabeza de flecha, ya que los biseles son planos; en su lugar usa la opción de inglete:
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
public class BevelArrows
{
public static void main ( String...args )
{
SwingUtilities.invokeLater ( new Runnable () {
BevelArrows arrows = new BevelArrows();
@Override
public void run () {
JFrame frame = new JFrame ( "Bevel Arrows" );
frame.add ( new JPanel() {
public void paintComponent ( Graphics g ) {
arrows.draw ( ( Graphics2D ) g, getWidth(), getHeight() );
}
}
, BorderLayout.CENTER );
frame.setSize ( 800, 400 );
frame.setDefaultCloseOperation ( JFrame.EXIT_ON_CLOSE );
frame.setVisible ( true );
}
} );
}
interface Arrow {
void draw ( Graphics2D g );
}
Arrow[] arrows = { new LineArrow(), new CurvedArrow() };
void draw ( Graphics2D g, int width, int height )
{
g.setRenderingHint ( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
g.setColor ( Color.WHITE );
g.fillRect ( 0, 0, width, height );
for ( Arrow arrow : arrows ) {
g.setColor ( Color.ORANGE );
g.fillRect ( 350, 20, 20, 280 );
g.setStroke ( new BasicStroke ( 20.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL ) );
g.translate ( 0, 60 );
arrow.draw ( g );
g.setStroke ( new BasicStroke ( 20.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER ) );
g.translate ( 0, 100 );
arrow.draw ( g );
g.setStroke ( new BasicStroke ( 20.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND ) );
g.translate ( 0, 100 );
arrow.draw ( g );
g.translate ( 400, -260 );
}
}
static class LineArrow implements Arrow
{
public void draw ( Graphics2D g )
{
// where the control point for the intersection of the V needs calculating
// by projecting where the ends meet
float arrowRatio = 0.5f;
float arrowLength = 80.0f;
BasicStroke stroke = ( BasicStroke ) g.getStroke();
float endX = 350.0f;
float veeX;
switch ( stroke.getLineJoin() ) {
case BasicStroke.JOIN_BEVEL:
// IIRC, bevel varies system to system, this is approximate
veeX = endX - stroke.getLineWidth() * 0.25f;
break;
default:
case BasicStroke.JOIN_MITER:
veeX = endX - stroke.getLineWidth() * 0.5f / arrowRatio;
break;
case BasicStroke.JOIN_ROUND:
veeX = endX - stroke.getLineWidth() * 0.5f;
break;
}
// vee
Path2D.Float path = new Path2D.Float();
path.moveTo ( veeX - arrowLength, -arrowRatio*arrowLength );
path.lineTo ( veeX, 0.0f );
path.lineTo ( veeX - arrowLength, arrowRatio*arrowLength );
g.setColor ( Color.BLUE );
g.draw ( path );
// stem for exposition only
g.setColor ( Color.YELLOW );
g.draw ( new Line2D.Float ( 50.0f, 0.0f, veeX, 0.0f ) );
// in practice, move stem back a bit as rounding errors
// can make it poke through the sides of the Vee
g.setColor ( Color.RED );
g.draw ( new Line2D.Float ( 50.0f, 0.0f, veeX - stroke.getLineWidth() * 0.25f, 0.0f ) );
}
}
static class CurvedArrow implements Arrow
{
// to draw a nice curved arrow, fill a V shape rather than stroking it with lines
public void draw ( Graphics2D g )
{
// as we''re filling rather than stroking, control point is at the apex,
float arrowRatio = 0.5f;
float arrowLength = 80.0f;
BasicStroke stroke = ( BasicStroke ) g.getStroke();
float endX = 350.0f;
float veeX = endX - stroke.getLineWidth() * 0.5f / arrowRatio;
// vee
Path2D.Float path = new Path2D.Float();
float waisting = 0.5f;
float waistX = endX - arrowLength * 0.5f;
float waistY = arrowRatio * arrowLength * 0.5f * waisting;
float arrowWidth = arrowRatio * arrowLength;
path.moveTo ( veeX - arrowLength, -arrowWidth );
path.quadTo ( waistX, -waistY, endX, 0.0f );
path.quadTo ( waistX, waistY, veeX - arrowLength, arrowWidth );
// end of arrow is pinched in
path.lineTo ( veeX - arrowLength * 0.75f, 0.0f );
path.lineTo ( veeX - arrowLength, -arrowWidth );
g.setColor ( Color.BLUE );
g.fill ( path );
// move stem back a bit
g.setColor ( Color.RED );
g.draw ( new Line2D.Float ( 50.0f, 0.0f, veeX - arrowLength * 0.5f, 0.0f ) );
}
}
}
En el pasado, he escrito el siguiente método para crear una forma de flecha, que luego puedo rellenar con ((Graphics2D) g) .fill (forma);
public static Shape createArrowShape(Point fromPt, Point toPt) {
Polygon arrowPolygon = new Polygon();
arrowPolygon.addPoint(-6,1);
arrowPolygon.addPoint(3,1);
arrowPolygon.addPoint(3,3);
arrowPolygon.addPoint(6,0);
arrowPolygon.addPoint(3,-3);
arrowPolygon.addPoint(3,-1);
arrowPolygon.addPoint(-6,-1);
Point midPoint = midpoint(fromPt, toPt);
double rotate = Math.atan2(toPt.y - fromPt.y, toPt.x - fromPt.x);
AffineTransform transform = new AffineTransform();
transform.translate(midPoint.x, midPoint.y);
double ptDistance = fromPt.distance(toPt);
double scale = ptDistance / 12.0; // 12 because it''s the length of the arrow polygon.
transform.scale(scale, scale);
transform.rotate(rotate);
return transform.createTransformedShape(arrowPolygon);
}
private static Point midpoint(Point p1, Point p2) {
return new Point((int)((p1.x + p2.x)/2.0),
(int)((p1.y + p2.y)/2.0));
}
Este es mi enfoque, solo Matemáticas absolutas:
/**
* Draw an arrow line between two points.
* @param g the graphics component.
* @param x1 x-position of first point.
* @param y1 y-position of first point.
* @param x2 x-position of second point.
* @param y2 y-position of second point.
* @param d the width of the arrow.
* @param h the height of the arrow.
*/
private void drawArrowLine(Graphics g, int x1, int y1, int x2, int y2, int d, int h) {
int dx = x2 - x1, dy = y2 - y1;
double D = Math.sqrt(dx*dx + dy*dy);
double xm = D - d, xn = xm, ym = h, yn = -h, x;
double sin = dy / D, cos = dx / D;
x = xm*cos - ym*sin + x1;
ym = xm*sin + ym*cos + y1;
xm = x;
x = xn*cos - yn*sin + x1;
yn = xn*sin + yn*cos + y1;
xn = x;
int[] xpoints = {x2, (int) xm, (int) xn};
int[] ypoints = {y2, (int) ym, (int) yn};
g.drawLine(x1, y1, x2, y2);
g.fillPolygon(xpoints, ypoints, 3);
}