How would you go about creating a line graph using outputs from a thread, the threads are simulations of incoming and outgoing bill that run over a course of 52 seconds and this will be dipicted on a line graph as shown below
I want to use the paint component not any third party classes like JChart.
Assuming you have some JPanel object that you are using to paint on, I would add the following to your object:
public class GraphPanel extends JPanel{
//static constants for defining the size and positioning of the graph on canvas
//Ignore the values I chose, they were completely random :p
private static final int X_AXIS_LENGTH = 1000;
private static final int Y_AXIS_LENGTH = 500;
private static final int X_AXIS_OFFEST = 50;
private static final int Y_AXIS_OFFSET = 50;
...
These should all be constant values that define the size you want your graph to be on the canvas (the axis lengths) and its positioning (the offsets).
You can then refer to these values in the paintComponent method to find the actual position of the line you want to draw for this update on the canvas.
...
#Override
public void paintComponent(Graphics g){
int x, y;
int prevX, prevY;
int maxX, maxY;
...
//retrieve values from your model for the declared variables
...
//calculate the coords of your line on the canvas
int xPos = ((x / maxX) * X_AXIS_LENGTH) + X_AXIS_OFFSET;
...
//do the same for y, prevX, prevY and then you can use g.drawLine
}
...
Note that you want to change maxX and maxY because the x and y values are moving above those limits, you will need to add some extra code to check for that change and redraw the whole graph with the new limits in place.
Related
I want to create a shape Object(in my case the object is a rectangle). Each time I click a button. Currently, I'm able to make it appear just once. The idea would be that each time I click the button, a new rectangle Object is created, additional to the old one. Therefore, if I click the button 5 times, I should have 5 rectangles.
I tried to do it with an ArrayList, but still, there is just one rectangle appearing. Does someone know how to do it!
Thank you very much in advance!
This is the main class, FYI there is also a rectangle Class(not attached)
import controlP5.*;
ControlP5 cp5;
Rectangle rect; // rect begins as null
Button rc;
ArrayList<Rectangle> rectList;
void setup(){
size(1000, 1000);
rectList = new ArrayList<Rectangle>();
cp5 = new ControlP5(this);
rc = cp5.addButton("Rectangle").
setPosition(5, 4).
setColorBackground(color(52, 55, 76));
rc.onRelease(new CallbackListener() {
public void controlEvent(CallbackEvent theEvent) {
// only create the rectangle when the button is clicked
rect = new Rectangle(100, 100, 100, 100);
}
});
}
void draw(){
background(255);
// if the rect exists, draw it on the screen
if(rect != null) {
rect.displayRect();
showRect();
}
for(int i = 0; i < rectList.size(); i++){
//((Rectangle)rectList.get(i)).update();
((Rectangle)rectList.get(i)).displayRect();
}
}
public void showRect(){
for(Rectangle r: rectList){
r.displayRect();
rect(r.getXvalue(), r.getYvalue(), r.getWvalue(), r.getHvalue());
}
}
You have a list, but you never add anything to that list. The list remains empty.
Drop the member field rect, delete this line:
Rectangle rect; // rect begins as null
When you instantiate a new Rectangle, immediately add it to the list.
rc.onRelease( new CallbackListener() {
public void controlEvent(CallbackEvent theEvent) {
// When the button is clicked, instantiate a new rectangle and remember it by adding to our list of rectangles.
rectList.add(
new Rectangle( 100, 100, 100, 100 )
);
}
});
Some chiding: This was not a good Question for Stack Overflow. You could easily have found this bug by using a debugger to step through the code. You would have seen that the list remains empty. Before posting, do your own debugging exhaustively.
You should post your Rectangle class to make it easier for others to test and help out.
As Basil pointed out (+1) you're only rendering a new rectangle for one frame when there's a click event.
The idea would be that each time I click the button, a new rectangle Object is created, additional to the old one. Therefore, if I click the button 5 times, I should have 5 rectangles.
This statement is a bit ambiguous. I get you'd like to render a rectangle per click, however in the click handler the rect has the exact same dimensions and coordinates. Even if you would make minor fixes, rendering 5 identical rectangles on top of each other will likely appear as if it's a single rectangle.
Regarding the code you posted, this stands out to me:
Rectangle rect; // rect begins as null: what is the purpose of the rect if you have this underneath: ArrayList<Rectangle> rectList; ?
showRect(); is called in draw(): it loops over rectList and not only calls displayRect() which I'd assume would render the current rect, but also re-renders the same data on the following line (rect(r.getXvalue(), r.getYvalue(), r.getWvalue(), r.getHvalue());)
underneath, there's a for loop over the same list calling displayRect() yet again. My guess is 2 out 3 calls to render rectangles are redundant. (Also the array list is typed therefore, no need to cast like this: (Rectangle)rectList.get(i)), rectList.get(i) should suffice)
The only other minor caveat I have is around naming: ideally you would want to stick to Java naming conventions in Processing. (For example getXValue() instead of getXvalue(), etc.)
Regarding the ControlP5 button you could use controlEvent() which is a bit simpler than setting a callback. Even simpler is to use this automatic variable plugging functionality. In short, if a function has the same name as a button's name it will be called automatically:
Automatic controller-event detection
ControlP5 offers a range of controllers that allow you to easily change and adjust values while your sketch is running. Each controller is identified by a unique name assigned when creating a controller. ControlP5 locates variables and functions inside your sketch and will link controllers to matching variables or functions automatically
(from controlP5 reference)
Here's a basic example that prints a message to console each time the button is clicked:
import controlP5.*;
ControlP5 cp5;
void setup() {
size(1000, 1000);
cp5 = new ControlP5(this);
cp5.addButton("rectangle").
setPosition(5, 4).
setColorBackground(color(52, 55, 76));
}
void draw(){
background(255);
}
void rectangle(){
println("rectangle button clicked");
}
(I've kept the name rectangle instead of Rectangle to keep in line with Java naming conventions. The text label is displayed in uppercase anyway)
Back to your main question, if you want to add new rectangle per button press and render them the code be as simple as:
import controlP5.*;
ControlP5 cp5;
ArrayList<Rectangle> rectList = new ArrayList<Rectangle>();
int x = 100;
int y = 100;
void setup() {
size(1000, 1000);
rectList = new ArrayList<Rectangle>();
cp5 = new ControlP5(this);
cp5.addButton("rectangle").
setPosition(5, 4).
setColorBackground(color(52, 55, 76));
}
void draw() {
background(255);
for (int i = 0; i < rectList.size(); i++) {
rectList.get(i).display();
}
}
void rectangle(){
rectList.add(new Rectangle(x, y, 100, 100));
// increment x, y to avoid superimposed rectangles
x += 50;
y += 50;
}
class Rectangle{
private int x, y, w, h;
Rectangle(int x, int y, int w, int h){
this.x = x;
this.y = y;
this.w = w;
this.h = h;
}
void display(){
rect(x, y, w, h);
}
// currently not used
public int getX(){
return x;
}
public int getY(){
return x;
}
public int getWidth(){
return x;
}
public int getHeight(){
return x;
}
}
Rectangle rect; stands out. If this was an OOP homework assignment or exercise perhaps the intention was to have a basic drawing app functionality where the user could have a rectangle drawing tool ?
If that's the case rect could be selection rectangle which could cloned into rectList so it persists.
You could implement rectangle selection like so:
when the mouse is pressed remember the coordinates: these are the starting point of the selection
as the mouse is dragged the end point coordinates are the current mouse coordinates, hence the selection rectangle dimensions are the difference between the current mouse coordinates and the previously stored mouse coordinates
as the mouse is released, reset the selection rectangle (so it no longer displays)
Here's a basic example sketch:
Rectangle selection = new Rectangle(0, 0, 0, 0);
void setup(){
size(1000, 1000);
}
void draw(){
background(255);
selection.display();
}
void mousePressed(){
// store selection start
selection.x = mouseX;
selection.y = mouseY;
}
void mouseDragged(){
// update selection dimension as the difference between the current mouse coordinates and the previous ones (selection x, y)
selection.w = mouseX - selection.x;
selection.h = mouseY - selection.y;
}
void mouseReleased(){
selection.w = selection.h = selection.x = selection.y = 0;
}
class Rectangle{
private int x, y, w, h;
Rectangle(int x, int y, int w, int h){
this.x = x;
this.y = y;
this.w = w;
this.h = h;
}
void display(){
rect(x, y, w, h);
}
}
Resetting the Rectangle properties to 0 could be nicely encapsulated into a method:
void reset(){
x = y = w = h = 0;
}
The release handler would also be useful to add a new rectangle to rectList which has the same properties (x, y, w, h) as the selection, but before the selection is reset. something like:
void mouseReleased(){
// add a copy of the selection to rectList
rectList.add(new Rectangle(selection.x, selection.y, selection.w, selection.h));
// reset selection
selection.reset();
}
Again, the copy functionality is something that could be nicely encapsulated as a method as well:
Rectangle copy(){
return new Rectangle(x, y, w, h);
}
Putting it all together would look like this:
import controlP5.*;
ControlP5 cp5;
ArrayList<Rectangle> rectList = new ArrayList<Rectangle>();
Rectangle selection = new Rectangle(0, 0, 0, 0);
void setup() {
size(1000, 1000);
rectList = new ArrayList<Rectangle>();
cp5 = new ControlP5(this);
cp5.addButton("rectangle").
setPosition(5, 4).
setColorBackground(color(52, 55, 76));
}
void draw() {
background(255);
// draw previous rectangles (black)
stroke(0);
for (int i = 0; i < rectList.size(); i++) {
rectList.get(i).display();
}
// draw current selection (green)
stroke(0, 192, 0);
selection.display();
}
void rectangle(){
println("selected drawing tool is rectangle");
}
void mousePressed(){
// store selection start
selection.x = mouseX;
selection.y = mouseY;
}
void mouseDragged(){
// update selection dimension as the difference between the current mouse coordinates and the previous ones (selection x, y)
selection.w = mouseX - selection.x;
selection.h = mouseY - selection.y;
}
void mouseReleased(){
// add a new rectangle to the list: a copy of the selection
rectList.add(selection.copy());
// reset selection
selection.reset();
}
class Rectangle{
private int x, y, w, h;
Rectangle(int x, int y, int w, int h){
this.x = x;
this.y = y;
this.w = w;
this.h = h;
}
void display(){
rect(x, y, w, h);
}
Rectangle copy(){
return new Rectangle(x, y, w, h);
}
void reset(){
x = y = w = h = 0;
}
}
At this stage the button is a bit redundant, however it could useful in the future if other shapes are required (e.g. an Ellipse would be an obviously simple one to implement since ellipse() has the same parameters as rect(), just need to ensure ellipseMode(CORNER) is set to use the same selection x,y coordinates the rectangle does)
Hopefully this is useful. The initial code, as mentioned before looks a bit messy, as if it's put together in a haste before a deadline.
(I assume this because it reminds me of my code as a student :))
I'd recommend taking a short walk away from the computer, remembering what the task is and pen on paper breaking the task into small, clear, easy to implement subtasks. Once that is as clear as it can be, implement one subtask at a time in isolation. Initially the code may break or get messy, but eventually it will work (and it will be easier to write compared to the whole drawing program). Once that works, cleanup the code so that all unnecessary code is removed and it's easy to move the code to another sketch and run it without errors. Repeat the process for each subtask which should result in multiple minimal sketches solving one single problem. Once all parts are solved in isolation you can start putting the code together into one main sketch, however I recommend adding one subtask code as a time and testing first. When mixing code from multiple sketches conflicts/errors may arise and it will far easier to tackle merging two sketches a time than all of the subtask sketches in one go. Best of luck !
I have a polygon I've made with GeneralPath, and I've made a basic stroke with line thickness of 8 and set the join type to JOIN_MITER. When I try to paint this shape like so:
g2d.setStroke(stroke);
g2d.draw(generalPath);
It paints the shape with the correct line thickness, but it treats each line like it's own, giving it whatever type of cap end I define in the stroke. CAP_ROUND looks the best like this, but I would much prefer the if lines properly joined with JOIN_MITER. Can this be done? Am I using the right class when I use GeneralPath? Any help or advice would be much appreciated.
For context, here's the class stripped down to just the pertinent info. the paintComponent method is called from the paintComponent method of the JPanel which I'm drawing on, it has an arrayList of these which it iterates through and calls each their paintComponent methods at a time:
public class RShape extends RComponent{
GeneralPath linkedLines;
Stroke stroke;
public RShape(int x, int y, int sides, Stroke stroke, int defaultLineLength) {
this.stroke = stroke;
linkedLines = new GeneralPath();
double angle = ((double)360)/sides;//find what the angles would need to be to make a shape with that many sides
double dStartX = x;
double dStartY = y;
double nextAngle;
for(int i=0;i<sides;i++) {
nextAngle = angle*i;
nextAngle = nextAngle * Math.PI / 180;//convert to radians
double dEndX = dStartX + defaultLineLength * Math.sin(nextAngle);//find where the end of the line should be for the given angle and line length
double dEndY = dStartY + defaultLineLength * Math.cos(nextAngle);
int endX = (int) Math.round(dEndX);//round to the nearest int
int endY = (int) Math.round(dEndY);
int startX = (int) Math.round(dStartX);
int startY = (int) Math.round(dStartY);
linkedLines.moveTo(startX, startY);//add the next segment of the GeneralPath
linkedLines.lineTo(endX, endY);
dStartX = dEndX;//move the starting point to the end point so it's ready for the next loop
dStartY = dEndY;
}
linkedLines.closePath();//close the last gap.
}
public void paintComponent(Graphics2D g2d) {
g2d.setStroke(stroke);
g2d.draw(linkedLines);
}
}
This is called like this:
ComponentList components = new ArrayList<RComponent>();
components.add(new RShape(50,50,5,new BasicStroke(8,BasicStroke.CAP_BUTT,BasicStroke.JOIN_MITER),60));
I have a Main class of game named BrickBreaker
public class BrickBreaker extends Activity {
// there is lot of other code but i am only pointing to the issue
class BreakoutView extends SurfaceView implements Runnable {
// The size of the screen in pixels
int screenX;
int screenY;
// Get a Display object to access screen details
Display display = getWindowManager().getDefaultDisplay();
// Load the resolution into a Point object
Point size = new Point();
display.getSize(size);
screenX = size.x;
screenY = size.y;
}}
And from another class (below) I want to access screenX from the Main class:
public class Paddle {
// This the the constructor method
// When we create an object from this class we will pass
// in the screen width and height
public Paddle(int screenX, int screenY){
// 130 pixels wide and 20 pixels high
length = 130;
height = 20;
// Start paddle in roughly the sceen centre
x = screenX / 2;
y = screenY - 20;
rect = new RectF(x, y, x + length, y + height);
// How fast is the paddle in pixels per second
paddleSpeed = 550;
}
public void update(long fps){
rect.left = x;
rect.right = x + length;
if (x<0){
x=0;
}
else if (x+length > screenX){
x = screenX-length;
}
}
}
How can I access screenX from the Paddle class?
In Paddle class modify field screenX
Java static variable
If you declare any variable as static, it is known static variable.
The static variable can be used to refer the common property of all objects (that is not unique for each object) e.g. company name of employees,college name of students etc.
The static variable gets memory only once in class area at the time of class loading.
Advantage of static variable
Please check this : STATIC
It makes your program memory efficient (i.e it saves memory).
public class Paddle {
public static int screenX;
// This the the constructor method
// When we create an object from this class we will pass
// in the screen width and height
public Paddle(int screenX, int screenY){
this.screenX = screenX;
// 130 pixels wide and 20 pixels high
length = 130;
height = 20;
// Start paddle in roughly the sceen centre
x = screenX / 2;
y = screenY - 20;
rect = new RectF(x, y, x + length, y + height);
// How fast is the paddle in pixels per second
paddleSpeed = 550;
}
public void update(long fps){
rect.left = x;
rect.right = x + length;
if (x<0){
x=0;
}
else if (x+length > screenX){
x = screenX-length;
}
}
}
And to acces in BrickBeaker you need to:
Paddle.screenX
Enjoy
You need to get access to BreakoutView's member screenX from your Paddle class.
For this, your Paddle class first needs to get access to the BreakoutView instance.
You could do this by passing it for example in the constructor:
public class Paddle {
BreakoutView view;
public Paddle (BreakoutView view) {
this.view = view;
}
public void update(long fps){
...
}
}
And where you create it:
BreakoutView view = new BreakoutView(.....);
Paddle paddle = new Paddle(view);
....
Next, you need to get access to the screenX member. There are two options:
First: make it public:
// The size of the screen in pixels
public int screenX;
public int screenY;
With this solution, you could access it in your Paddle class with this.view.screenX.
But this would allow other code, for example from inside your Paddle class, to modify these values, which is often not desired. Thus, the safer way is:
Second: Provide a getter.
In BreakoutView, add the following method:
public int getScreenX() {
return this.screenX;
}
and then, in your Paddle class, you could access it like this.view.getScreenX() instead of just screenX.
I'm following a textbook and have become stuck at a particular point.
This is a console application.
I have the following class with a rotate image method:
public class Rotate {
public ColorImage rotateImage(ColorImage theImage) {
int height = theImage.getHeight();
int width = theImage.getWidth();
//having to create new obj instance to aid with rotation
ColorImage rotImage = new ColorImage(height, width);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
Color pix = theImage.getPixel(x, y);
rotImage.setPixel(height - y - 1, x, pix);
}
}
//I want this to return theImage ideally so I can keep its state
return rotImage;
}
}
The rotation works, but I have to create a new ColorImage (class below) and this means I am creating a new object instance (rotImage) and losing the state of the object I pass in (theImage). Presently, it's not a big deal as ColorImage does not house much, but if I wanted it to house the state of, say, number of rotations it has had applied or a List of something I'm losing all that.
The class below is from the textbook.
public class ColorImage extends BufferedImage {
public ColorImage(BufferedImage image) {
super(image.getWidth(), image.getHeight(), TYPE_INT_RGB);
int width = image.getWidth();
int height = image.getHeight();
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
setRGB(x, y, image.getRGB(x, y));
}
public ColorImage(int width, int height) {
super(width, height, TYPE_INT_RGB);
}
public void setPixel(int x, int y, Color col) {
int pixel = col.getRGB();
setRGB(x, y, pixel);
}
public Color getPixel(int x, int y) {
int pixel = getRGB(x, y);
return new Color(pixel);
}
}
My question is, how can I rotate the image I pass in so I can preserve its state?
Unless you limit yourself to square images or to 180° rotations, you need a new object, as the dimensions would have changed. The dimensions of a BufferedImage object, once created, are constant.
If I wanted it to house the state of, say, number of rotations it has had applied or a List of something I'm losing all that
You can create another class to hold that other information along with the ColorImage/BufferedImage, then limit the ColorImage/BufferedImage class itself to holding only the pixels. An example:
class ImageWithInfo {
Map<String, Object> properties; // meta information
File file; // on-disk file that we loaded this image from
ColorImage image; // pixels
}
Then you can replace the pixels object freely, while preserving the other state. It's often helpful to favor composition over inheritance. In brief that means, instead of extending a class, create a separate class that contains the original class as a field.
Also note that the rotation implementation from your book seems to be mainly for learning purposes. It's fine for that, but will show its performance limitations if you manipulate very big images or for continuous graphics rotation at animation speeds.
Below I have a simple method for painting a set of objects onto an java.awt.applet.Applet. I thought this was very straightforward, but it ends up only painting the objects as a single pixel at the top-left corner of the applet. The idea is that the Display class takes in a set of fairly lightweight objects that extend GameObject and contain information about their location, size, and appearance on the screen, and then draws them pixel-by-pixel onto the applet, stretching and positioning them proportionally depending on the specified display height and width. In testing this, I set the width and height of the Display to 128, and pass two objects to the Display, which are both 32-pixel squares (both return 32 for getWidth() and getHeight()), one is red and returns 24 for getX() and getY(), and the other is blue and returns 16 for getX() and getY(). I put the Display in a javax.swing.JFrame and use a java.awt.BorderLayout to ensure it fills the frame (I use add(display, java.awt.BorderLayout.CENTER); within the aforementioned javax.swing.JFrame).
As far as I can tell, this should be paining a blue 32-pixel square that's 16 pixels from the top and left edges and a red 32-pixel square that is either obscured by or obscuring part of the other one. However, all I get is a single red or blue pixel in the top-left corner of the Display. This is consistent no matter how big or small the window is.
Code
public class Display extends java.awt.applet.Applet
{
private int w,h;
private ArrayPP<GameObject> gameObjects;//ArrayPP is my way of making a dynamically expanding array. It is similar to Vector, but has many more useful methods I use elsewhere.
public Display(int width, int height)
{
w = width;
h = height;
}
public void addGameObject(GameObject go)
{
gameObjects.add(go);
}
public void refresh(java.awt.Graphics g)
{
int x, y, w, h;
final int W = getWidth(), H = getHeight();
for (GameObject go : gameObjects)//Draw all objects
for (x = go.getX(), y = go.getY(), w = go.getWidth() + x, h = go.getHeight() + y; y < h; y++)//Draw all lines of the object
for (x = go.getX(); x < w; x++)//Draw all the pixels on this line of the object
{
g.setColor(go.getColorForPixel(x, y));
g.fillRect((x / this.w) * W, (y / this.h) * H, w/W, h/H);
}
}
}
public interface GameObject
{
public int getX();
public int getY();
public int getWidth();
public int hetHeight();
public java.awt.Color getColorForPixel(int x, int y);
}
Question
Why is java.awt.Graphics.fillRect(int x, int y, int width, int height) only painting the topleft corner of the applet?
Solution
The solution lies in the line that reads
g.fillRect((x / this.w) * W, (y / this.h) * H, w/W, h/H);
wherein integer calculations cause all values to be 0. The solution is as follows:
g.fillRect((int)(((float)x/(float)this.w) *W),
(int)(((float)y/(float)this.h) *H),
(int)((float)W/(float)w),
(int)((float)H/(float)h));
The problem lies with
(x / this.w) * W
If x and this.w are both integers and x
Convert one or more of x and w to float to force a floting point division.
(int)(((float)x/(float)this.w) *W)