1

I have an interesting problem. Using Java graphics, I'd like to draw a circle with parallel lines in the circle that are spaced with some pre defined gap constant. The circle has a known x and y position and a radius. The lines should start from the center of the circle and should move outward thereof. For example,

enter image description here

I'd imaging it would be easiest to first draw the lines to fill up the whole square as follows:

enter image description here

and then perhaps draw 4 polygons and fill them in white white. The 4 polygons are labeled as follows:

enter image description here

As you can see, a polygon in this case is defined by a left top point (x,y), edges of width and height defined by the radius of the circle, and then an arc from (x+radius, y+radius).

Feedback needed:

  1. Is it even possible to draw such a polygon in Java?
  2. Is this logic making sense? Does this seem easier to you?
  3. The alternative is to somehow determine the pixels that make up the circle and draw lines using those. But this seems messy to me. Do you agree?
  4. Can you think of an alternative way of doing this?

IMPORTANT: note that while this solution has vertical lines, the lines should be defined in terms of some angle theta. That is, they can be angled (but all parallel to each other).

If someone could provide code that actually draws this, I would be forever thankful!! :)

public void draw(Graphics g, int x, int y, int radius, int lineGap, int lineThickness, int theta) {
   //g = the graphics object
   //x,y = the top left coordinate of the square
   //radius = the radius of the circle, the width of the rectangle, the height of the rectangle
   //lineGap = the gap in between each of the lines
   //lineThickness = the thickness of the lines in pixels
   //theta = the angle that the lines should be at, relative to the y axis
}
CodeGuy
  • 28,427
  • 76
  • 200
  • 317

3 Answers3

5

Using the ol' Pythagorean Thereom, this simple bit of algebra follows... y in terms of x and r

r is the radius of the circle.
x is a variable you'll make iterate through the circle, in increments you can easily work out from knowing the radius and the spacing.

Knowing the height of each line (and their x position), it won't be a challenge centering the verticle lines parallel to the cirlce center (if your placement coordinates specify the top left corner as they usually do, you'll want to place it at y = circle_center - (line_length / 2)

Note also, that if you do specify the coordinates of the vertical lines via the top left corner, you'll need to consider how thick they are when calculating the x coordinate.
This is extremely simple, using the x value suggested by the circle.
true_x = suggested_x - (line_width / 2)

Refer to other more sensible answers for dealing with angled lines

This procedure remains as a demonstration of the sensibility of using a clip over finding the exact domain/range of each line (and because I wasted so much of my life on it).

enter image description here enter image description here enter image description here enter image description here Here's where the fun ended for me. enter image description here

Oomph! Replace that last m with 'tan(theta)'

Anti Earth
  • 4,671
  • 13
  • 52
  • 83
  • But what if the line can be angled. The line won't always be vertical, but rather defined by some angle theta. – CodeGuy Jan 14 '13 at 07:02
  • Imagine two identical graphs, excepting that one has angled lines. Both graphs will have the same number of lines and the same area between them (mathematically. In pixels, things are yuck). Before, you iterated across the line y = 0 (the x axis) and made perpendicular lines (x = constant). In this case, you can iterate across a line y = mx (where your gradient m - tan(90 - angle)). Your lines will be perpendicular to this quasi-axis (thus at angle). I'll write out a thorough explanation soon (this is a brainstorm for those who can write it out quicker) – Anti Earth Jan 14 '13 at 08:23
  • Adding images as I make them, hopefully they inspire some ideas. – Anti Earth Jan 14 '13 at 08:53
  • Even if I hadn't made a bad assumption, your 'pre determined gap size' wouldn't be maintained when you consider the width of the lines (which the maths, in its current form, doesn't acknowledge). This is a very interesting problem, I'm sorry I can't be of more assistance. The most advisable approach if you need the thing working is to take Andrews and MapProgrammers approach, drawing the angled lines and then clipping them. – Anti Earth Jan 14 '13 at 09:11
  • *"take Andrews and MapProgrammers approach"* It is "the lazy man's approach" which is why I like it. ;) +1 for your in depth treatment of the problem, & especially for the drawings. Happy 500 rep. :) – Andrew Thompson Jan 14 '13 at 09:40
  • @AndrewThompson It has proven to be the sane man's approach. Thankyou! :) – Anti Earth Jan 14 '13 at 10:03
3

Simple change the graphics clipping...

enter image description here

public class SimplePaint02 {

    public static void main(String[] args) {
        new SimplePaint02();
    }

    public SimplePaint02() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(100, 100);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            int radius = Math.min(getWidth(), getHeight());
            int x = (getWidth() - radius) / 2;
            int y = (getHeight()- radius) / 2;

            BufferedImage buffer = new BufferedImage(radius, radius, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2d = buffer.createGraphics();

            Ellipse2D circle = new Ellipse2D.Float(0, 0, radius, radius);
            Shape clip = g2d.getClip();
            g2d.setClip(circle);
            int gap = getWidth() / 10;
            g2d.setColor(Color.RED);
            for (int index = 0; index < 10; index++) {

                g2d.drawLine(index * gap, 0, index * gap, radius);

            }
            g2d.setClip(clip);
            g2d.setColor(Color.BLUE);
            g2d.draw(circle);
            g2d.dispose();
            g.drawImage(buffer, x, y, this);
        }

    }

}

Update

I should point out that you should NEVER modify any graphics context's clipping that is rendering to the screen, this can seriously screw up the screen rendering ;)

Updated #2

Example showing the use of an AffinTransformation to support rotation...

enter image description here

public class CirlceDraw {

    public static void main(String[] args) {
        new CirlceDraw();
    }

    public CirlceDraw() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }

        });
    }

    public class TestPane extends JPanel {

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(100, 100);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            int radius = Math.min(getWidth(), getHeight());
            int x = (getWidth() - radius) / 2;
            int y = (getHeight() - radius) / 2;

            BufferedImage buffer = new BufferedImage(radius, radius, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2d = buffer.createGraphics();

            Ellipse2D circle = new Ellipse2D.Float(0, 0, radius, radius);
            Shape clip = g2d.getClip();
            g2d.setClip(circle);
            AffineTransform at = g2d.getTransform();
            g2d.setTransform(AffineTransform.getRotateInstance(Math.toRadians(33), radius / 2, radius / 2));
            int gap = getWidth() / 10;
            g2d.setColor(Color.RED);
            for (int index = 0; index < 10; index++) {

                g2d.drawLine(index * gap, 0, index * gap, radius);

            }
            g2d.setTransform(at);
            g2d.setClip(clip);
            g2d.setColor(Color.BLUE);
            g2d.draw(circle);
            g2d.dispose();
            g.drawImage(buffer, x, y, this);
        }

    }

}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • @Andrew Add an `AffineTransformation` and you don't need to worry about rotating the results either :D – MadProgrammer Jan 14 '13 at 01:18
  • Now what if the lines are not straight but are angled at some defined theta? – CodeGuy Jan 14 '13 at 06:59
  • @codeeguy Easy, simple apply an AffineTransformation to the lines portion of the code – MadProgrammer Jan 14 '13 at 08:23
  • *"simple apply an AffineTransformation to the lines"* (grins) not sure if much that is useful to do with transforms is 'simple'. I always need to tweak them a bit. ;) OTOH perhaps the [`OvalGradientPaint`](http://stackoverflow.com/a/14170312/418556) code can give the OP a start. Of course, swap out all the concatenated transforms for [`AffineTransform.getRotateInstance(theta,anchorX,anchorY)`](http://docs.oracle.com/javase/7/docs/api/java/awt/geom/AffineTransform.html#getRotateInstance%28double,%20double,%20double%29) - maybe it would be simple.. :) – Andrew Thompson Jan 14 '13 at 09:18
  • 1
    @AndrewThompson While I love all the maths theory, if it's between getting the job done or having fun ;) - Also, the Graphics2D is well documented, making it much easier for other developers to follow - believe me, I've worked with enough developers who love working out the complex algorithm themselves, which is great, until someone else has to maintain it - IMHO – MadProgrammer Jan 14 '13 at 19:46
  • One last bonus: can you add a paramater to allow to specify the thickness of the angled lines in pixel? I tried to set g2d.setStroke however then the lines extend beyond the bounds of the circle – CodeGuy Jan 15 '13 at 08:40
  • How much you paying? You could take a look at [this](http://docs.oracle.com/javase/tutorial/2d/geometry/strokeandfill.html) – MadProgrammer Jan 15 '13 at 08:41
1

I would use trigonometry.

The x of your line is given by the cosinus of the angle from origin (center of the circle). So, arccos(x) would give you which is the angle.

Once you have the angle, you can have its maxim height by using the sinus sin function.

Of course, all java trig functions work with radians (360 degrees = 2*PI radians).

SJuan76
  • 24,532
  • 6
  • 47
  • 87
  • Sorry, I'm not following. Could you clarify and write a little more details. 1) You say "the x of your line"...what "line"? 2) you say arccos(x) would give you. give me what? what angle? – CodeGuy Jan 14 '13 at 00:51
  • All the points of each of your vertical lines have the same `x` (they would not be vertical otherwise). The angle from the reference radius (3 o'clock, counter-clockwise) – SJuan76 Jan 14 '13 at 00:54
  • However not all lines will be vertical. Please see question. – CodeGuy Jan 14 '13 at 07:02