NB: I have never used swing before, neither graphics 2D, and I don't program very much...
What I am trying to do is to make a program which takes an array/vector as input. This array, where each index 0,1,2 etc holds either zero or one (int) - which represents "no activity" or "activity" in minute 0,1,2 etc...
I want the program to draw a discontinuous straight horizontal line - representing "activity" vs "no activity" as a function of time - based on the array that was taken as input.
And this should pop up in a panel when I run the code..
The Idea is to show activity/no activity as a function of time, so the line would preferably be shown in a chart ( x-axis & y-axis )... And there will be several of these discontinuous lines above each other - for comparison of different cases.
I have tried for a while to look at examples using swing and graphics 2D, but as I have very limited amount of time - I could really need some help ..
Any code that:
creates a panel, frame etc - where I "easily" can see where I can insert my graph: that is a panel which is ready to display the graph I will make
draw a graph of discontinuous horizontal lines based on an array as described above
...is immensely appreciated :)
added from comment:
Sorry - did not finish my answer :) I could sure try to learn how to use all the different things in Swing frames, panels etc.. But at the moment my main goal is to finish my assignment for school - which is the visualization of data itself - and they do not really care how you get there, the most important thing is that it visualizes something useful... So I thought that I could decrease the time I had to spend on this if I got some code which could get me started - and not have to learn how it all works first.
No need to learn Graphics2D, just go for JFreeChart. Here is a simple tutorial to get you started (A minimum of Java programming knowledge is required though)
This is is an example, I guess it will help
import java.awt.*;
import javax.swing.*;
public class ActivityGraph extends JFrame {
int[] active = {0,1,1,0,0,0,1,0,1,0,1,0,1,1,1,1,0,0,1,0,1,1};
int length = 25, //basic length in pixels for drawing the lines
offset = 50; //so the lines aren't sticked at the border
private ActivityGraph(String name, int x, int y, int width, int height) {
super(name);
setBounds(x, y, width, height);
setDefaultCloseOperation(EXIT_ON_CLOSE);
JPanel panel = new GraphPanel();
//panel.setBounds(0, 0, 800, 400); not nessesary
add(panel);
}
public static void main(String[] args) {
new ActivityGraph("Activity Graph", 60, 60, 800, 400).setVisible(true);
}
private class GraphPanel extends JPanel {
public void paint(Graphics g) {
g.setColor(Color.white);
g.fillRect(0, 0, 800, 400);
//setting background (method setBackground() doesn't want to work for me)
g.setColor(Color.black);
for(int i = 0; i<active.length; i++) {
if(active[i]==0) {
g.drawLine(offset + i*length, offset + length, offset + i*length + length, offset + length);
}
else {
g.drawLine(offset + i*length, offset, offset + i*length + length, offset);
}
/*
* draw line from XY point to another XY point
* notice that X = Y = 0 point is in left top corner
* so higher Y values will mean "downer" points acctualy
*/
}
}
}
}
If you want, I can send you a graph drawer for math functions (like sinus, power, ...)
Related
I'm writing a recursive method in Java that creates essentially creates a circle tree. It draws a circle at the top and center and then the method gets called again and creates the circles one level lower on the y axis and half way to the left and right of the new circle. I was successful but only for a certain number of objects to be drawn This is what it looks like
public void test(Graphics g, int y, int num, double instance) {
if(num<50) {
int r = 20;
for(int i=1;i<=instance;i++) {
if(i%2==1) {
g.fillOval(getWidth() * i / num, y, r, r);
}
}
if(instance==1){
instance= 2* instance;
}
test(g, y + 20, num * 2, Math.pow(instance,2.0));
}
Everything works perfectly until I try to increase the number in "if(num<50)" to exactly "if(num<65)". When I change that the JFrame appears but now it is empty and it seems like the program is frozen. I want to increase that so that I can fill the Jframe with the circle tree. Why is it doing that? Looking forward to your response! Thank you!
I found the issue. I don't know why I choose to use Math power when I only had to use *2 and that fixed the whole program.
So I was messing with Swing for the first time in a while and I came across a strange issue. So I am adding "shapes" to a list every so often, and then in the paintComponent method of a JPanel I am looping through the list and drawing the shapes. I also draw a shape outside of the for loop for testing purposes.
What happens is the shapes in the for loop will jump around the screen randomly. This only happens when the shapes are drawn in this loop.
What I have tried already:
Updating graphics drivers for both the integrated GPU and discrete GPU
Using java.util.Timer instead of Swing Timer
Using Thread/Runnable
Using things other than ArrayList, such as LinkedList, Vector, and a normal Array.
Trimmed literally everything out of my code except the basics, which is what we're left with here. I was drawing more complex things before when I noticed it.
Changed the timing (PERIOD variable, in millis). It will get worse if I increase or decrease it.
Changed from using System time in milliseconds to the System time in nanoseconds, converted to milliseconds. I know this should be the same but I was running out of ideas.
Here is a gif of the problem (15 seconds long):
image
You'll notice that the small squares will jump around at random intervals. This should not occur. I'm just trying to "spawn" a square at random coords.
Here is the code in a pastebin:
code
I have included all 3 classes in this order: the JPanel class, the Main class (extends JFrame), and the shape class
If any of the links don't work, inform me and I will promptly post other links.
Thanks.
This setup ...
#Override
public final void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
if (this.startTime == -1L) {
this.startTime = Main.timeMillis();
}
final long timeDiff = Main.timeMillis() - this.startTime;
if (this.circlesIndex <= 19 && timeDiff > 2000) {
final int randX = this.rand.nextInt(this.WIDTH);
final int randY = this.rand.nextInt(this.HEIGHT);
this.testShapes.add(new TestShape(randX, randY));
this.startTime = Main.timeMillis();
}
for (TestShape ts : this.testShapes) {
ts.draw(g);
}
g2.setColor(Color.gray);
g2.fill3DRect(350, 400, 100, 60, true);
}
#Override
public final void actionPerformed(final ActionEvent event) {
x++;
if (x > WIDTH) {
x = -50;
}
repaint();
}
is wrong.
Paint is for painting - you should not modify the state of the UI from inside any paint method, do this within your ActionListener. The problem is, your component can be painted for any number of reasons, many of which you don't control or know about
Ok dear folks, i've got this question and i don't really know a certain way to solve it.
I'm doing like a "Paint application" in java, i know everything is ready, but I need to paint the shapes with Computer Graphics Algorithms.
So, the thing is, once the shape is painted in the container how could I convert it like sort of an "Object" to be able to select the shape and move it around (I have to move it with another algorithm) I just want to know how could I know that some random point clicked in the screen belongs to an object, knowing that, I would be able to fill it(with algorithm).
I was thinking that having a Point class, and a shape class, if i click on the screen, get the coordinates and look within all the shapes and their points, but this may not be very efficient.
Any ideas guys ?
Thanks for the help.
Here is some of my code:
public class Windows extends JFrame{
private JPanel panel;
private JLabel etiqueta,etiqueta2;
public Windows() {
initcomp();
}
public void initcomp()
{
panel = new JPanel();
panel.setBounds(50, 50, 300, 300);
etiqueta = new JLabel("Circulo Trigonometrico");
etiqueta.setBounds(20, 40, 200, 30);
etiqueta2 = new JLabel("Circulo Bresenham");
etiqueta2.setBounds(150, 110, 200, 30);
panel.setLayout(null);
panel.add(etiqueta);
panel.add(etiqueta2);
panel.setBackground(Color.gray);
this.add(panel);
this.setLayout(null);
this.setVisible(true);
this.setSize(400,400);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public void paint(Graphics g){
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.red);
g2d.setStroke(new BasicStroke(2));
dibujarCirculo_bresenham(g2d, 50, 260, 260);
dibujarCirculo_trigonometrico(g2d, 50, 130, 200);
}
/*This functions paints a Circle*/
public void dibujarCirculo_trigonometrico(Graphics g,int R,int xc,int yc)
{
int x,y;
for (int i = 0; i < 180; i++) {
double angulo = Math.toRadians(i);
x = (int) (Math.cos(angulo)*R);
y = (int) (Math.sin(angulo)*R);
g.drawLine(x+xc, y+yc, x+xc, y+yc);
g.drawLine((-x+xc), (-y+yc), (-x+xc), (-y+yc));
}
}
I assume that any image is a valid (isn't constrained to a particular set of shapes). To get an contiguous area with similar properties, try using a flood fill.
To colour in or move a particular shape around, you can use flood fill to determine the set of pixels and manipulate the set accordingly. You can set a tolerance for similar hue, etc so that it's not as rigid as in Paint, and becomes more like the magic selection tool in Photoshop.
There are a couple of approaches to take here depending on what precisely you want.
1) is to have objects, one for each drawn thing on screen, with classes like Circle and Rectangle and Polygon so on. They would define methods like paint (how to draw them on screen), isCLickInsideOf (is a click at this point on screen contained by this shape, given size/position/etc?) and so on. Then, to redraw the screen draw each object, and to test if an object is being clicked on ask each object what it thinks.
2) is, if objects have the property of being uniform in colour, you can grab all pixels that make up a shape when the user clicks on one of the pixels by using a floodfill algorithm. Then you can load these into some kind of data structure, move them around as the user moves the mouse around, etc. Also, if every object is guaranteed to have a unique colour, you can test which object is being clicked on by just looking at colour. (Libraries like OpenGL use a trick like this sometimes to determine what object you have clicked on - drawing each object as a flat colour on a hidden frame and testing what pixel colour under the mouse pointer is)
My assignment is to implement an algorithm to color a closed shape starting from a given (x,y) coordinate and "spread" via recursive calls untill it reaches the borders of the shape. So far this is what I've come up with:
private void color(int x, int y) {
g2d.draw(new Line2D.Double(x, y, x, y));
if (!robot.getPixelColor(x - 1, y).equals(Color.BLACK) &&
!robot.getPixelColor(x - 1, y).equals(Color.RED)) {
color(x - 1, y);
} else if (!robot.getPixelColor(x + 1, y).equals(Color.BLACK) &&
!robot.getPixelColor(x - 1, y).equals(Color.RED)) {
color(x + 1, y);
} else if (!robot.getPixelColor(x, y - 1).equals(Color.BLACK) &&
!robot.getPixelColor(x - 1, y).equals(Color.RED)) {
color(x, y - 1);
} else if (!robot.getPixelColor(x, y + 1).equals(Color.BLACK) &&
!robot.getPixelColor(x - 1, y).equals(Color.RED)) {
color(x, y + 1);
}
}
The Robot class' getPixelColor is the only way I found to get the color of a given pixel (as far as I know another would be getRGB, but that only works on Image objects). To my understanding this should work, as the outer lines of the shape are definitely black, and the initial x and y values come from a MouseListener, so they are inside the shape, however I get the following error:
Exception in thread "AWT-EventQueue-0" java.lang.StackOverflowError
at sun.java2d.pipe.BufferedContext.validateContext(BufferedContext.java:110)
at sun.java2d.d3d.D3DRenderer.validateContextAA(D3DRenderer.java:42)
at sun.java2d.pipe.BufferedRenderPipe$AAParallelogramPipe.fillParallelogram(BufferedRenderPipe.java:445)
at sun.java2d.pipe.PixelToParallelogramConverter.drawGeneralLine(PixelToParallelogramConverter.java:264)
at sun.java2d.pipe.PixelToParallelogramConverter.draw(PixelToParallelogramConverter.java:121)
at sun.java2d.SunGraphics2D.draw(SunGraphics2D.java:2336)
at dline.DrawingSpace.color(DrawingSpace.java:87)
at dline.DrawingSpace.color(DrawingSpace.java:93)
at dline.DrawingSpace.color(DrawingSpace.java:90)
at dline.DrawingSpace.color(DrawingSpace.java:93)
at dline.DrawingSpace.color(DrawingSpace.java:90)
(drawingSpace is a sub-class of JPanel)
The teacher did tell us that this is memory consuming, however it's supposed to be a working algorithm, so I'm doing something wrong, obviously. Any help would be much appriciated, thank you.
You can try to increase the Stack size: How to increase the Java stack size?
Probably you have a bug in your algorithm, or the shape is too big. What helps if you 'draw' your algorithm on a piece of graph paper. That way you can check your algorithm.
I'm guessing that you're backtracking onto previously visited pixels. The pixel you just drew probably won't be visible to robot until after you return from color, so it will not appear red from the previous painting.
Do you have a reference to the java.awt.Shape? A much simpler way than using the robot would be to use Shape.contains(Point) to see whether it's in the shape you're supposed to draw.
The basic algorithm either way is depth-first traveral. To do a DFS when there are possible cycles, you can record the pixels you've already drawn.
//java.awt.Point
Set<Point> paintedPixels = new HashSet<Point>();
private void color(int x, int y) {
if ( paintedPixels.contains(new Point(x, y)) ) {
//already painted
return;
}
paintedPixels.add(new Point(x, y));
//...
}
Now, this could still result in a very deep search. You might consider instead using a non-recursive breadth-first traveral. See the Wikipedia article on Flood Fill.
The problem with implementing this as a recursive algorithm is that it has (for bigger images) a very high recursion depth.
In Java (and most other imperative programming languages, too) the maximal recursion depth is limited by the amount of stack space for each thread, since it must keep a stack frame for each method invocation there.
You may try smaller images first, and try to increase the stack size with the -xss parameter.
Edit: As pointed out by Mark, the Robot will not get any pixels until your drawing is complete, since often your drawing is double-buffered (i.e. the Swing engine lets you paint first on an image, and draws then the complete image to the screen).
Also, you are not converting between device (screen) and user (component) coordinates for the lookup.
You wrote:
The Robot class' getPixelColor is the only way I found to get the color of a given pixel (as far as I know another would be getRGB, but that only works on Image objects).
So, why don't you use an Image object? Fill your shape while drawing on the Image, and then draw the whole image at once to the screen.
And your method can be made much more readable if you transfer the "is already painted" test inside the recursive call:
private void color(int x, int y) {
// getPixel invokes something in the image - or replace it here.
Color org = getPixel(x,y);
if (org.equals(Color.BLACK)) {
// reached the border
return;
}
if (org.equals(Color.RED)) {
// already painted before
return;
}
g2d.draw(new Line2D.Double(x, y, x, y));
color(x-1, y);
color(x+1, y);
color(x, y-1);
color(x, y-1);
}
I have a task to draw a circle and then fill in with the most amount of circles without touching the sides. I can draw the circle, and I can make loops to pack the circle in a hexagonal/honeycomb format, but can't control whether they are inside or outside the circle.
I have used this: g.drawOval(50, 50, 300, 300); to specify my circle. Given I'm actually specifying a square as my boundaries I can't actually determine where the circle boundaries are. So I'm basically packing the square full of circles rather than the circle full of circles.
Can some please point me in the right direction? I'm new to java so not sure if I have done this the complete wrong way. My code is below. I have another class for the frame and another with the main in it.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class DrawCircle extends JPanel
{
private int width, height, diameter;
public DrawFrame d;
public DrawCircle()
{
width = 400;
height = 400;
diameter = 300;
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.blue);
g.drawOval(50, 50, 300, 300);
for(int i=50; i<200; i=i+20)
{
for(int j=50; j<350; j=j+10)
{
g.drawOval(j, i, 10, 10);
}
}
for(int i=60; i<200; i=i+20)
{
for(int j=55; j<350; j=j+10)
{
g.drawOval(j, i, 10, 10);
}
}
for(int i=330; i>190; i=i-20)
{
for(int j=340; j>40; j=j-10)
{
g.drawOval(j, i, 10, 10);
}
}
for(int i=340; i>190; i=i-20)
{
for(int j=345; j>40; j=j-10)
{
g.drawOval(j, i, 10, 10);
}
}
}
}
All those magic numbers make me cringe a bit. You're new to Java, and it's homework, so I understand why you're doing it, but I would not recommend it if you do much programming in the future.
You need an algorithm or recipe for deciding when a small circle on the inside falls outside the big one you're trying to pack. Think about the ways you might do this:
If the distance between the center of the big circle and the small circle is is greater than the difference in their radii, the small circle will overlap the big circle or fall completely outside it.
You can add this check to your code: Just before you draw the circle, perform this check. Only draw if that circle passes.
Don't worry about Java for a second; draw yourself a picture on a piece of paper, draw that enclosing and packed circle, and see if that statement is correct. Then think about any corner situations that it might not cover, just as a check.
I'll make two more recommendations. First, do this by hand without a computer once so you'll see what the "right" answer might look like. Second, see if you can separate the calculation of the circles from the drawing part. It might make your job easier, because you can concentrate on one thing at a time. It's called "decomposition". You solve complex problems by breaking them up into smaller, more manageable pieces. In this case, it's also called "model-view separation". You might need to know that someday.
Maybe another way to think about this problem would be to imagine a 2D arrangement of circles, packed in their closest arrangement, extending to infinity in both the x- and y-directions. Now take your enclosing circle, put it on top of the 2D arrangement, and eliminate all the circles that overlap the big circle. I don't know if it'll be optimal, but it's easy to visualize.