I'm doing a little personnal project to improve my java skill.
What I do there, is that i create 3 JPanel (a global one, and 2 into the global).
In one of them (the right one), I want to draw blocks. Those blocks have a random height, width and an unique id.
In one of my java class, i create a random number of blocks. Then, I'm doing a function to draw those blocks and here are the problems.
First of all, the paintComponent function is executing twice, and I only want it to be executed one time.
Secondly, my blocks are.. not draw very well.. here is a picture of 20 blocks drawing into my panel (to debug, I put a fix number of blocks). My windows is 900x700.
Here is my paintComponent function, I tried to see where I did a mistake, but
I pull my hair off..
Your target picture indicates you basically want to render the images in lines, i.e. if an image wouldn't fit into the current line you'd start a new one. Thus you need to track the offsets for x and y as well as the hight of the highest block in the line. This means for each block you'd do something like this (untested but a little debugging should help if this isn't 100% correct):
//maximum width of a line
int maxWidth = 300;
//Space in pixels between the border as well as the blocks
int paddingX = 5;
int paddingY = 5;
//the offset for the next block
int offsetX = paddingX;
int offsetY = paddingY;
int currentLargestHeight = 0;
for( Block block : blocks ) {
//if the block doesn't fit into the "line" start a new one
//we assume an empty line always can take at least one block
if( block.getWidth() > (maxWidth - offsetX - paddingX) ) {
//advance down
offsetY += currentLargestHeight + paddingY;
//we have a new line so the current largest height is 0 again
currentLargestHeight = 0;
//start left again
offsetX = paddingX;
}
//render the block
g2.fillRect(offsetX, offsetY, block.getWidth(), block.getHeight());
offsetX += block.getWidth() + paddingX;
if( block.getHeight() > currentLargestHeight ) {
currentLargestHeight = block.getHeight();
}
}
Related
I've been stuck at something recently.
What I want to do is to get multiple sub-images out of 1 big image.
So take this example. I have a frame of 128x128 pixels where all the images need to be in.
I'm putting all the bufferedImages inside a list and scaling all those images to 128x128.
The image you see on that link is showing that I need 4 sub-images from that image, so at the end, I have 4 images which are 128x128 but 4 times.
Or if you have an image with 128x384 it will give 3 sub-images going from top to bottom.
https://i.stack.imgur.com/RsCkf.png
I know there is a function called
BufferedImage.getSubimage(int x, int y, int w, int h);
But the problem is that I can't figure out what math I need to implement.
What I tried is if the height or width is higher than 200 then divide it by 2 but that never worked for me.
I'm not sure I fully understand what you are asking, but I think what you want is something like this:
First, loop over the image in both dimensions.
Then compute the size of the tile (the smaller value of 128 and (image dimension - start pos)). This is to make sure you don't try to fetch a tile out of bounds. If your images are always a multiple of 128 in any dimension, you could just skip this step and just use 128 (just make sure you validate that input images follow this assumption).
If you only want tiles of exactly 128x128, you could also just skip the remainder, if the tile is less than 128x128, I'm not sure what your requirement is here. Anyway, I'll leave that to you. :-)
Finally, get the subimage of that size and coordinates and store in the list.
Code:
BufferedImage image = ...;
int tileSize = 128;
List<BufferedImage> tiles = new ArrayList<>();
for (int y = 0; y < image.height(); y += tileSize) {
int h = Math.min(tileSize, image.height() - y);
for (int x = 0; x < image.width(); x += tileSize) {
int w = Math.min(tileSize, image.width() - x);
tiles .add(image.getSubimage(x, y, w, h));
}
}
I am using a code I got from a site for a heartbeat sensor. The signal, when displayed by this code looks something like this:
Could you help me add a check which will increment an integer every time a signal goes above a certain threshold? This needs to happen for 10 seconds only, after 10 seconds the check stops and then gets multiplied by 6 to display the amount of beats per minute.
The code I'm using gets the imaging done, I would like to add the beats per minute onto it.
import processing.serial.*;
Serial myPort; // The serial port
int xPos = 1; // horizontal position of the graph
float oldHeartrateHeight = 0; // for storing the previous reading
void setup () {
// set the window size:
size(600, 400);
frameRate(25);
// List available serial ports.
println(Serial.list());
// Setup which serial port to use.
// This line might change for different computers.
myPort = new Serial(this, Serial.list()[1], 9600);
// set inital background:
background(0);
}
void draw () {
}
void serialEvent (Serial myPort) {
// read the string from the serial port.
String inString = myPort.readStringUntil('\n');
if (inString != null) {
// trim off any whitespace:
inString = trim(inString);
// convert to an int
println(inString);
int currentHeartrate = int(inString);
// draw the Heartrate BPM Graph.
float heartrateHeight = map(currentHeartrate, 0, 1023, 0, height);
stroke(0,255,0);
line(xPos - 1, height - oldHeartrateHeight, xPos, height - heartrateHeight);
oldHeartrateHeight = heartrateHeight;
// at the edge of the screen, go back to the beginning:
if (xPos >= width) {
xPos = 0;
background(0);
} else {
// increment the horizontal position:
xPos++;
}
}
}
Disclaimer: Of course, it goes without saying, this is only a guideline. Don't hook it up to someone's heart without thorough testing!
The simplest of checks is to look out for whenever the signal crosses a virtual line - typically the midpoint, like so:
That immediately makes things easier - we just need to check when our latest value is above the line, and the previous one is below it; whenever that happens, our signal must've crossed the line. That's as simple as this, using 750 as your midpoint:
int currentHeartrate = int(inString);
int midpoint=750;
if(currentHeartrate >= midpoint && oldHeartrateHeight < midpoint){
// It crossed the line!
beats++;
}
Looking closer at your signal, it's really noisy, which means we might get pretty unlucky with a sample which goes above the line then immediately drops below it giving us a false reading. To deal with that, you could add a moving average to your currentHeartrate value - that'll smooth out the fine noise for you. Add this to your project:
public class MovingAverage {
private final float[] window;
private float sum = 0f;
private int fill;
private int position;
public MovingAverage(int size) {
this.window=new float[size];
}
public void add(float number) {
if(fill==window.length){
sum-=window[position];
}else{
fill++;
}
sum+=number;
window[position++]=number;
if(position == window.length){
position=0;
}
}
public float getAverage() {
return sum / fill;
}
}
Rather than using currentHeartrate and oldHeartrateHeight, you'd instead first obtain the moving average - let's call it averageHeartrate - then cache that in oldAverageHeartrate and perform the same comparison with these two values instead.
Taking this a little further, you could make your BPM indicator realtime by counting the number of samples between these beat marks. As you've got a fixed number of samples per second, you then divide those and apply another moving average to this time reading. That then gives us this:
public int samplesSinceBeat; // Tracks the # of samples since the prev beat
public float oldAverageHeartrate; // Renamed
public int samplesPerSecond=10000; // However many samples/sec
public float midpoint=750; // The midpoint
public MovingAverage averageSamples=new MovingAverage(10); // Averaging over 10 samples
public MovingAverage beatTimeAverage=new MovingAverage(4); // Averaging over 4 beats
..
int currentHeartrate = int(inString);
// Add to your moving average buffer:
averageSamples.add(currentHeartrate);
float averageHeartrate=averageSamples.getAverage();
// Bump up the number of samples since the last beat:
samplesSinceBeat++;
if(averageHeartrate >= midpoint && oldAverageHeartrate < midpoint){
// It crossed the line - we have a beat!
// The time since the last beat is therefore:
float timeBetweenBeats=(float)samplesSinceBeat / (float)samplesPerSecond;
// Add time between beats to another moving average:
beatTimeAverage.add(timeBetweenBeats);
// Reset samples since beat:
samplesSinceBeat=0;
}
oldAverageHeartrate=averageHeartrate;
// The BPM is now this:
int realtimeBPM= (int)(60f / beatTimeAverage.getAverage() );
Some signals are evil and have a moving midpoint too. If this is the case, I would approach that by recording:
The largest value seen since the previous beat
The smallest value seen since the previous beat
Then simply take the midpoint from those. Essentially, you'd end up tracking the midpoint as you go:
if( currentHeartrate > maxHeartrateValue){
maxHeartrateValue=currentHeartrate;
}
if( currentHeartrate < minHeartrateValue){
minHeartrateValue=currentHeartrate;
}
// This line is unchanged:
if(averageHeartrate >= midpoint && oldAverageHeartrate < midpoint){
// It crossed the line - we have a beat!
midpoint=(minHeartrateValue + maxHeartrateValue) / 2;
// Clear!
minHeartrateValue=Integer.MAX_VALUE;
maxHeartrateValue=Integer.MIN_VALUE;
..
I have built a simple program to draw a city skyline within a frame with some random elements. The number of buildings will be a random number between [5 - 9], the width and height of each building are also random (within a given range) as is the gap between each building. The problem I am having comes when I try to select a different gap range depending on the amount of buildings. Here is the the section of code in question (I have imported java.util.*;):
Random gen = new Random();
int buildNum = gen.nextInt(5) + 5; //generate random number [5 - 9]
for (int i = 0; i < buildNum; i++)
{
//generate random width
buildWidth = gen.nextInt(26) + 35; //[35 - 60]
//generate random height
buildHeight = gen.nextInt(201) + 200; //[200 - 400]
//instantiate building
building = new Building (gap, 500 - buildHeight, buildWidth, buildHeight, Color.gray);
buildingsArray.add(building);
//draw building
buildingsArray.get(i).draw (page);
//generate random gap between building
if (buildNum < 7)
{
gap += buildWidth + gen.nextInt(16) + 40;
}
if (buildNum == 7)
{
gap += buildWidth + gen.nextInt(9) + 15;
}
if (buildNum > 7)
{
gap += buildWidth + gen.nextInt(9) + 8;
}
}
The buildNum variable is initialized with a random number, then that number is used to iterate through. The issue is that when I check the buildNum variable in the three ifs at the bottom, the output does not seem to correspond with what I would expect. I will get 5 buildings with very small gaps (as though it passed the buildNum > 7 test), or I will get 7 buildings with the first six normal, but a very small gap before the seventh building. I'm sure it's something very simple that I am overlooking, but I have been starting at this for hours and tried many different things (including testing buildNum at fixed values 5, 6, 7, 8, or 9, and putting an else clause instead of a third if.) Ultimately, I would like the program to pick a number of buildings between 5 and 9 and then scale the gaps accordingly. I tried factoring in both buildNum and buildWidth, but I couldn't get it to work and settled for this. If anyone has a better way or can shed some light on my problem, I would greatly appreciate it!
Update: I have tested with a constant number of buildings to see what the range of output looks like. The problem is that I would be expecting 5 or 6 buildings to have wide gaps due to the if (buildNum < 7) condition and be progressively smaller due to the other 2 conditions. Instead, I end up with 5 buildings having really really small gaps (6 buildings is fine) and seven and 8 buildings going off the page (even though I've done the math and the gap that is added within the if conditionals would not allow this). There are some helpful comments here already, to answer your questions: Yes, the gap is an absolute position from the left. A small gap is initialized so that the first building does not touch the left side of the frame. Then, buildings are instantiated one at a time with a gap size chosen from those if statements. The position of the next building's left side is determined from a compounding gap variable that includes all previous gaps and all previous building widths. I am especially curious about Lee's comment that the if (buildNum ==7) and (buildNum>7) clauses might never happen. Could you explain that a little further? Wouldn't 8 or 9 satisfy the >7 condition and 7 satisfy the ==7 condition? if not, what can I do to change it? also, here is my constructor:
/**
* Constructor: sets background color and frame size
*/
public SkylinePanel()
{
setPreferredSize (new Dimension(600, 500));
setBackground (Color.black);
}
There is not much to it, the real work (including the above shown for loop) is within paintComponent and I've shown everything that is related to the building gaps already. Here is the draw method (it's in my building class):
/**
* Draws a building or window with the specified parameters
*/
public void draw (Graphics page)
{
page.setColor (color);
page.fillRect (x, y, width, height);
winNum = gen.nextInt(8) + 2;
//generate a random number [2 - 9] of windows to draw on a given building
for (int i = 0; i < winNum; i++)
{
page.setColor (Color.yellow);
page.fillRect (gen.nextInt(width - WIN_X) + x, gen.nextInt(height - WIN_Y) + y, WIN_X, WIN_Y);
}
}
Ignore the code about windows, that works fine. I added that after I decided to move on from the gap problem for the time being.
Oh, and just in case anyone needs to see it, here is the constructor of the Building class:
public Building (int upperX, int upperY, int buildWidth, int buildHeight, Color shade)
{
x = upperX;
y = upperY;
width = buildWidth;
height = buildHeight;
color = shade;
}
Any other thoughts or questions would be greatly appreciated!
My assignment asks me to code an ellipse similar to a plate, and stack 20 of them using if else statements and everything included from chapters 1-6 in shiffmans processing beginner's book. I need to use a button pressed function to stack 20 plates, once they reach to 20 THE "PLATES" MUST INDIVIDUALLY DISAPPEAR TO 0 ONE BY ONE WITH A MOUSE CLICK FUNCTION. This is what I have came up with so far, the plates have to start at the bottom of the screen.
// Declare global (shared) variables here
float plate1X = 50;
float plate1Y = 200;
int plateColor = (255);
// Do not write any statements here (must be inside methods)
void setup()`enter code here`
{
// Add statements to run once when program starts here. For example:
size(400,400);
plate1X = 200;
plate1Y = 50;
background(255);
plate1X = width/2;
plate1Y = height/2;
} // end of setup method
void draw()
{
// Declare local variables here (new each time through)
// Add statements to run each time screen is updated here
ellipse(plate1X, plate1Y, 200,50);
if(ellipse <= 1 || ellipse <= 0; //draw another plate);
// Screen will be repainted automatically at the end of draw method
} // end of draw method
// Add other methods here
"does not detect the variable 'ellipse'": That's because you didn't declare it. You need to let the compiler know what kind of variable it is before you use it. In this case, it is probably an integer, so you should have the line int ellipse; above the first time you use it.
You also never set ellipse to hold any value! You are trying to check if(ellipse <= 1 || ellipse <= 0), but right now ellipse doesn't have any value.
Your ellipse function seems like it should draw an ellipse at the coordinates that you tell it--
ellipse(x, y, width, height)
so if you want to draw another ellipse, you should call ellipse(...) again with a new x and y value.
You will want to use if statements to make sure you only do this some of the time--specifically when you haven't gotten to 20 plates yet. Do you know what if and else statements do?
I;m making a game in which I have to move little squares on an n by n grid, and they have to transition smoothly. This is the swap method I made which should be able to paint my transition on screen, but for some reason it is not doing it. I tried a much simpler version of my code on a simple project to move a Square back and forth and it worked like a charm, so I'm really not sure why this isn't repainting. This is just a bit of my code so if there's doubts about anything else on my code, ask away.
Thanks in advance. (:
public void swap( int y, int x ) {
long time = System.currentTimeMillis();
int counter = 0;
swapNum = tiles[y][x];
rect = (Rectangle) rectangles[y][x].clone();
while(counter < rect.height) {
if(System.currentTimeMillis() - time > 5) {
rect.translate(this.y-y, this.x-x);
time = System.currentTimeMillis();
counter++;
repaint();
}
}
swapNum = 0;
rect = new Rectangle();
int temporary = tiles[this.y][this.x];
tiles[this.y][this.x] = tiles[y][x];
tiles[y][x] = temporary;
this.x = x;
this.y = y;
}
If this block of code is running on the Event/Dispatch thread, which is used for drawing to the screen, then this will block the screen from updating.
Instead of doing the entire animation in one loop, consider designing an update method to do the animation, that will be called once every 15-30 milliseconds, and update the rectangle's position accordingly. Even better for smooth graphics is to draw to an image buffer and then have the actual draw method paint that buffer to the screen (double buffering).
Java3D has animations built-in, so it may be worth a look.