0

How i want it to look like:

enter image description here

The circles move along with the arrow around the center circle.

How it is looking at the moment:

enter image description here

I want to draw a 2 lines between two circles. however these circles move all around the screen and i dont know a methodical way to draw lines between them. For example, I always have the top left corner of the two circles i want to draw a line between but thats it. I need help to draw a line in java that will adjust based on its position so that the lines move around the edge as the circles move

      for (int z = 0; z < lines.size(); z++) {
                    if (lines.get(z).getfState().equals(states.get(a).getText()) && !lines.get(z).getfState().equals(lines.get(z).getnState())) {
                        transition.get(z).setIcon(null);
                        for (int x = 0; x < states.size(); x++) {
                            if (states.get(x).getText().equals(lines.get(z).getnState()) && states.get(a).getText().equals(lines.get(z).getfState())) {
                                int xbegin = (int) states.get(a).getBounds().getX();
                                int ybegin = (int) states.get(a).getBounds().getY();
                                int xend = (int) states.get(x).getBounds().getX();
                                int yend = (int) states.get(x).getBounds().getY();
                                if (xbegin > xend) {
                                    Path2D.Double rect = new Path2D.Double(drawArrowLine(xbegin, ybegin, xend, yend, 10, 7));
                                    OutlineIcon transit = new OutlineIcon(drawArrowLine(xbegin, ybegin, xend + 30, yend, 10, 7), Color.BLACK);
                                    transition.get(z).setIcon(transit);
                                    transition.get(z).setBounds(rect.getBounds().x, rect.getBounds().y, rect.getBounds().width + 20, rect.getBounds().height + 20);
                                    jPanel2.revalidate();
                                    jPanel2.repaint();
                                } else {
                                    if (xend - xbegin < 75) {
                                        xbegin = xbegin - 20;
                                        xend = xend - 20;
                                    }
                                    xbegin = xbegin + 5;
                                    ybegin = ybegin + 25;
                                    xend = xend + 5;
                                    yend = yend + 25;
                                    Path2D.Double rect = new Path2D.Double(drawArrowLine(xbegin, ybegin, xend - 10, yend, 10, 7));
                                    OutlineIcon transit = new OutlineIcon(drawArrowLine(xbegin, ybegin, xend - 10, yend, 10, 7), Color.BLACK);
                                    transition.get(z).setIcon(transit);
                                    transition.get(z).setBounds(rect.getBounds().x, rect.getBounds().y, rect.getBounds().width + 20, rect.getBounds().height + 20);
                                    jPanel2.revalidate();
                                    jPanel2.repaint();
                                }

                            }
                        }
                    } else if (lines.get(z).getnState().equals(states.get(a).getText()) && !lines.get(z).getfState().equals(lines.get(z).getnState())) {
                        transition.get(z).setIcon(null);
                        for (int x = 0; x < states.size(); x++) {
                            if (states.get(x).getText().equals(lines.get(z).getfState()) && states.get(a).getText().equals(lines.get(z).getnState())) {
                                int xend = (int) states.get(a).getBounds().getX();
                                int yend = (int) states.get(a).getBounds().getY();
                                int xbegin = (int) states.get(x).getBounds().getX();
                                int ybegin = (int) states.get(x).getBounds().getY();
                                if (xbegin > xend) {
                                    Path2D.Double rect2 = new Path2D.Double(drawArrowLine(xbegin, ybegin, xend, yend, 10, 7));

                                    OutlineIcon transit = new OutlineIcon(drawArrowLine(xbegin, ybegin, xend + 30, yend, 10, 7), Color.BLACK);
                                    transition.get(z).setIcon(transit);
                                    transition.get(z).setBounds(rect2.getBounds().x, rect2.getBounds().y, rect2.getBounds().width + 20, rect2.getBounds().height + 20);
                                    jPanel2.revalidate();
                                    jPanel2.repaint();
                                } else {
                                    if (xend - xbegin < 75) {
                                        xbegin = xbegin + 20;
                                        xend = xend + 20;
                                    }
                                    xbegin = xbegin + 5;
                                    ybegin = ybegin + 25;
                                    xend = xend + 5;
                                    yend = yend + 25;
                                    Path2D.Double rect2 = new Path2D.Double(drawArrowLine(xbegin, ybegin, xend - 10, yend, 10, 7));
                                    OutlineIcon transit = new OutlineIcon(drawArrowLine(xbegin, ybegin, xend - 10, yend, 10, 7), Color.BLACK);
                                    transition.get(z).setIcon(transit);
                                    transition.get(z).setBounds(rect2.getBounds().x, rect2.getBounds().y, rect2.getBounds().width + 20, rect2.getBounds().height + 20);
                                    jPanel2.revalidate();
                                    jPanel2.repaint();
                                }
                            }
                        }
          public static Path2D.Double createArrowForLine(
        int fromPointx,
        int fromPointy,
        double rotationDeg,
        double length,
        double wingsAngleDeg) {

    double ax = fromPointx;
    double ay = fromPointy;

    double radB = Math.toRadians(-rotationDeg + wingsAngleDeg);
    double radC = Math.toRadians(-rotationDeg - wingsAngleDeg);

    Path2D resultPath = new Path2D.Double();
    resultPath.moveTo(length * Math.cos(radB) + ax, length * Math.sin(radB) + ay);
    resultPath.lineTo(ax, ay);
    resultPath.lineTo(length * Math.cos(radC) + ax, length * Math.sin(radC) + ay);
    return (Path2D.Double) resultPath;
}
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
zarir
  • 29
  • 6
  • Consider posting images and pertinent code to better help us understand your problem, and to see what you've tried and where you're stuck. If you can't post an image due to your reputation score being too low, then post a link to an **image** (not the web page that holds it), and we can insert it into your question for you. – Hovercraft Full Of Eels Jan 28 '16 at 23:49
  • 1
    Do you want the line to go from the center of each circle or from the edges of each circle? – MadProgrammer Jan 28 '16 at 23:56
  • @MadProgrammer: I'm guessing the latter since he states two lines, but that's were an image would greatly help. If so, solving this would involve a little trigonometry. – Hovercraft Full Of Eels Jan 28 '16 at 23:58
  • i added a picture now, and i want to draw two lines from the edges of each circle based on its position as you can see right now it looks very weird based on the position, but i attempted some offsetting based on the direction of the arrow – zarir Jan 29 '16 at 00:01
  • So, without much more info, I would draw an imaginary line from the center of the first circle to the center of the second circle, this means you can calculate the angle between two points, something like [this](http://stackoverflow.com/questions/23398744/setting-arc-position-in-java-using-mouse/23399282#23399282) for example. Knowing this, you can then calculate the point on each circle where the line would intersect (which should now give you a pair of points) and you can draw line between them – MadProgrammer Jan 29 '16 at 00:06
  • 1
    [Example](http://stackoverflow.com/questions/25923480/simple-circle-rotation-simulate-motion/25923780#25923780) and [example](http://stackoverflow.com/questions/26456405/sensing-hover-over-outer-edge-of-a-path2d-circle-in-jpanel/26456606#26456606) and [example](http://stackoverflow.com/questions/30228146/drawing-a-line-maximum-point/30228270#30228270) of finding the point on a circle based on a given angle... – MadProgrammer Jan 29 '16 at 00:07
  • Wow thanks but is there a way to calculate the points based on the direction of the line? For example if i want an arrow to pointing to a circle vs one without? i need to maintain the distance between the two as the user moves it around the circle. Basically i need to find out how i would calculate where the line should intersect without colliding with other arrows. And i also need to change the point of intersection based on the angle too – zarir Jan 29 '16 at 00:08
  • In simpler terms i need to maintain a distance between the two lines so they dont collide like in the picture on the bottom left and this distance needs to be maintained as i move around the circle – zarir Jan 29 '16 at 00:16
  • if you have the circle and the surrounding rectangle then you can draw the lines to that rectangle: they will not collide - I dont see where the circles are and rectangles in your code - its too obfuscated - maybe you can make it clearer what defines a circle in your code – gpasch Jan 29 '16 at 00:51
  • Yes i can draw it to the circle but i dont know how to calculate the point i need to draw to as the line moves around – zarir Jan 29 '16 at 00:54
  • 1
    *"Example of how it looks now"* Do you have an image of the **expected result?** Because I have little idea of the requirement based on the current image. – Andrew Thompson Jan 29 '16 at 05:18
  • I added a picture of how i want it to look like, but since my reputation is low I needed to take out the other picture of how it looks like now – zarir Jan 31 '16 at 17:41
  • @zarir: I've edited the earlier picture into the post. It contains now "How it SHOULD look" and "How it IS looking". :) – hamena314 Feb 01 '16 at 09:06

1 Answers1

4

Although there have been some hiccups in the question, and the code provided so far looks questionable, the core of the question as it stands now is quite interesting...

There are different options for solving this. From the images that you provided so far, it looks like the circles always have the same size, which makes things far simpler. For circles with different sizes, you'd really have to compute the tangents of the circles, in the desired direction, mutually considering the radius of the other circle. Of course, this is possible, but a bit less trivial.

For the case that you have equally-sized circles, you can

  • Compute the difference of the centers of two circles
  • Divide this by the distance, to obtain the (normalized) direction
  • Rotate this direction by 90°
  • Scale the rotated direction vector by the radius
  • Add the scaled and rotated vector to the circle center

This will yield one endpoint of such a line. The rotation about 90° can be done once in clockwise and once in counterclockwise direction, to obtain the "upper" and "lower" endpoint for the line, respectively.

LinesAtCircle

Image was updated with the EDIT, see below

The actual computation is done in the computeLine method of the MCVE below. Note that this example uses the "simple" approach, although it uses circles of slightly different sizes. The effect is that, when the difference between the sizes of two circles is too large (compared to the distance between the circles, basically), then the lines may slightly intersect the circles. But the solution should be a reasonable trade-off between simplicity and general applicability. Particularly, for equally-sized circles, there will be no intersections at all.

Code was updated with the EDIT, see below

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class LinesAtCirclesTest
{
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {

            @Override
            public void run()
            {
                createAndShowGUI();
            }
        });
    }
    private static void createAndShowGUI()
    {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JPanel linesAtCirclesTestPanel = new LinesAtCirclesTestPanel(); 
        f.getContentPane().add(linesAtCirclesTestPanel);
        f.setSize(400,400);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }
}

class LinesAtCirclesTestPanel extends JPanel 
    implements MouseListener, MouseMotionListener
{
    private Point2D draggedCenter;
    private List<Point2D> centers = new ArrayList<Point2D>();
    private List<Double> radii = new ArrayList<Double>();

    public LinesAtCirclesTestPanel()
    {
        addMouseListener(this);
        addMouseMotionListener(this);

        addCircle(100, 100, 30);
        addCircle(200, 300, 50);
        addCircle(300, 200, 40);
    }

    private void addCircle(double x, double y, double radius)
    {
        centers.add(new Point2D.Double(x,y));
        radii.add(radius);
    }

    @Override
    protected void paintComponent(Graphics gr)
    {
        super.paintComponent(gr);
        Graphics2D g = (Graphics2D)gr;
        g.setRenderingHint(
            RenderingHints.KEY_ANTIALIASING, 
            RenderingHints.VALUE_ANTIALIAS_ON);

        for (int i=0; i<centers.size(); i++)
        {
            Point2D center0 = centers.get(i);
            double radius0 = radii.get(i);

            Shape ellipse = new Ellipse2D.Double(
                center0.getX() - radius0, center0.getY() - radius0, 
                radius0 + radius0, radius0 + radius0);

            g.setColor(Color.LIGHT_GRAY);
            g.fill(ellipse);
            g.setColor(Color.BLACK);
            g.draw(ellipse);
        }

        g.setColor(Color.RED);
        for (int i=0; i<centers.size() - 1; i++)
        {
            Point2D center0 = centers.get(i);
            double radius0 = radii.get(i);
            Point2D center1 = centers.get(i+1);
            double radius1 = radii.get(i+1);

            g.draw(createArrow(computeLine(center0, radius0, center1, radius1, true)));
            g.draw(createArrow(computeLine(center0, radius0, center1, radius1, false)));
        }
    }

    private static Shape createArrow(Line2D line)
    {
        double dx = line.getX2() - line.getX1();
        double dy = line.getY2() - line.getY1();
        double angleToX = Math.atan2(dy, dx);
        final double angleRad = Math.toRadians(30);
        final double headLength = 20.0f;
        double dxL = Math.cos(Math.PI + angleToX + angleRad) * headLength;
        double dyL = Math.sin(Math.PI + angleToX + angleRad) * headLength;
        double dxR = Math.cos(Math.PI + angleToX - angleRad) * headLength;
        double dyR = Math.sin(Math.PI + angleToX - angleRad) * headLength;
        Path2D arrow = new Path2D.Double();
        arrow.moveTo(line.getX1(), line.getY1());
        arrow.lineTo(line.getX2(), line.getY2());
        arrow.lineTo(line.getX2() + dxL, line.getY2() + dyL);
        arrow.moveTo(line.getX2(), line.getY2());
        arrow.lineTo(line.getX2() + dxR, line.getY2() + dyR);
        return arrow;
    }

    private static Line2D computeLine(
        Point2D center0, double radius0,
        Point2D center1, double radius1, 
        boolean upper)
    {
        double dx = center1.getX() - center0.getX();
        double dy = center1.getY() - center0.getY();
        double invLength = 1.0 / Math.hypot(dx, dy);
        double dirX = dx * invLength;
        double dirY = dy * invLength;

        double rotDirX = dirY;
        double rotDirY = -dirX;
        if (upper)
        {
            rotDirX = -dirY;
            rotDirY = dirX;
        }

        double x0 = center0.getX() + rotDirX * radius0;
        double y0 = center0.getY() + rotDirY * radius0;

        double x1 = center1.getX() + rotDirX * radius1;
        double y1 = center1.getY() + rotDirY * radius1;

        if (upper)
        {
            return new Line2D.Double(x1, y1, x0, y0);
        }
        return new Line2D.Double(x0, y0, x1, y1);
    }

    @Override
    public void mousePressed(MouseEvent e)
    {
        draggedCenter = null;
        for (int i=0; i<centers.size(); i++)
        {
            Point2D center = centers.get(i);
            double radius = radii.get(i);
            if (e.getPoint().distance(center) < radius)
            {
                draggedCenter = center;
            }
        }
    }

    @Override
    public void mouseReleased(MouseEvent e)
    {
        draggedCenter = null;
    }

    @Override
    public void mouseDragged(MouseEvent e)
    {
        if (draggedCenter == null)
        {
            return;
        }
        draggedCenter.setLocation(e.getPoint());
        repaint();
    }



    @Override
    public void mouseMoved(MouseEvent e)
    {
        // Not used
    }

    @Override
    public void mouseClicked(MouseEvent e)
    {
        // Not used
    }

    @Override
    public void mouseEntered(MouseEvent e)
    {
        // Not used
    }

    @Override
    public void mouseExited(MouseEvent e)
    {
        // Not used
    }
}

EDIT in response to the comment:

The original code computed Line2D objects. Creating an arrow from a line is, in the simplest case, basically done with a bit of trigonometry, and many resources exist for this on the web.

In response to the comment, I extended the example to show simple arrows, as depicted in the above image.


However, when taking a closer look at this, one may notice several degrees of freedom for such an arrow:

  • Should the head length be absolute or relative to the arrow?
  • Should the head width be absolute or relative to the arrow?
  • (Or: What should be the angle of the arrow head?)
  • Should the arrow head be filled, or consist of lines?
  • Should the "trunk" of the arrow be a single line, or an outline shape?
  • What should be the width of the trunk?
  • ...

In order to cover some of these degrees of freedom, I created an ArrowCreator class a while ago, and there's also a sample showing how it may be used.

Community
  • 1
  • 1
Marco13
  • 53,703
  • 9
  • 80
  • 159
  • Alright thank you for your answer but I have somewhat of a challenge question. How would you draw an arrowhead on the end of the lines based on its direction – zarir Feb 01 '16 at 15:58
  • @zarir I added an EDIT and updated the code accordingly. – Marco13 Feb 01 '16 at 16:28
  • alright i will use the arrowCreator, it is a very helpful thing when drawing lines – zarir Feb 03 '16 at 00:51