#JavaFX Fluid Simulation
1 messages Β· Page 3 of 1
ahh
I could squish it down to one single class for the simulation loop
non threaded
its 200 MB ram usage
missing the improvements to Fluid.java
my teacher will not approve π
I need the methods for the fluid (such as the index mapping function etc) so should i pass in the fluid as a param?
lots
shall i find another one of those wacky emails π
bruh tell him he has no idea and shouldnt be a teacher 
jk
but this makes no sense
and I wont continue to help
this is the best solution for this
other stuff makes no sense
might want to check/refer to this:
#today-i-teach message
nvm
it is missing AnimationTimer 
https://openjfx.io/javadoc/21/javafx.graphics/javafx/animation/AnimationTimer.html
The class AnimationTimer allows to create a timer, that is called in each frame while it is active. An extending class has to override the method handle(long) which will be called in every frame. The methods start() and stop() allow to start and stop the timer.
literally made for this purpose
literally
the nice thing is that the handle method is still called on the FX Thread
so you can do UI stuff
separate to updating?
wdym
lol
I will finalise this with my teacher
if he says no i guess i gotta just do the more convoluted way
it would be so giga cringe
because it makes no sense
its like creating your own arraylist instead of using the provided one by the standard library
he likes it when you make the stuff yourself and don't rely on too much other stuff
π
thats not reality
i mean it was a linked list but same idea i guess
i'm gonna have to make my own deque as well when it comes to storing recordings
this is a fluid simulation, you are creating it using javafx and so you should be allowed to use the features provided by javafx
the focus should be on the simulation itself
such a bad way of teaching ngl
its really important that you know your libraries
using them efficiently is important for good code quality
@steep elbow is it fine if i do use an interface as a parameter because i can't just pass the density array by itself as there is the fluid.index() method which i would require for setting pixels but idk if i should pass in the whole fluid
thing is
it's a fluid simulation for a computer science project not a physics project
they care more about the computational methods you use
yeah but still
e.g. concurrent programming
but its not like that you use concurrency even if its worse
thats not how it works in reality
i should've just done a graph visualiser everyuthing would've been so ez and i would've gotten all the marks quickly
wdym?
you can choose your project
ah ok
normally people do something easy like a small game or booking system with some cool database stuff
but for a graph visualiser you would also need to do all stuff your own because of that teacher lmao
i asked my teacher for recommendations and he said a fluid simulation
yeah but i think graph data structures aren't as bad
which is a cool project and pretty nice and easy to implement using javafx
if you know and are allowed to at least use the standard lib lol
yeahh
people get marked down so much for using drag and drops and just anythingn that saves them a lot of time π
because then they won't be doing much work and the project would be easy for them
he should at least read what AnimationTimer is about before just saying
yeah nah, do it yourself π€‘
and if he says no, ask him if you are allowed to use Platform.runlater
if he also says no just tell him that its not possible to finish this then
you would need either one
big trap
ok sure
also
how do i do it so that when i press the close button, my program fully stops?
i think the introduction of a new thread fully ruined it
make it a daemon thread
or instead handle the close button properly
so it shut downs the thread properly
also normally you shouldnt use Thread directly
instead use stuff like Executor services and stuff
ala said to use this Platform.runLater(() -> render(density.clone()));
but
the problem is
i can't just pass in the density
because i need the fluids methods to actually correspond a location in the density to a pixel
Tbh I wouldnβt continue this until your teacher answered or use the provided code by me temporarily to work on other stuff
hmm
ok i shall just wait for him to answer then
i'm still going to try edit this one myself though because he will most likely disagree with what you said
i did exactly this
but it just shows a plain canvas
within the mouse adapter
public int index(int i, int j) {
if (i < 0) {
i = 0;
}
if (i > 300 + 1) {
i = 300 + 1;
}
if (j < 0) {
j = 0;
}
if (j > 300 + 1) {
j = 300 + 1;
}
return (i + j * (300 + 2));
}
@Override
public void render(double[] dens) {
GraphicsContext gc = canvas.getGraphicsContext2D();
for (int y = 0; y < canvas.getHeight() ; y += 3) {
for (int x = 0; x < canvas.getWidth(); x += 3) {
double num = dens[index(x / 3, y / 3)];
byte pixelValue = (byte) ((num > 255) ? 255 : num);
int color = pixelValue & 0xFF;
gc.setFill(Color.grayRgb(color));
gc.fillRect(x, y, 3, 3);
}
}
}```
Detected code, here are some useful tools:
public int index(int i, int j) {
if (i < 0) {
i = 0;
}
if (i > 300 + 1) {
i = 300 + 1;
}
if (j < 0) {
j = 0;
}
if (j > 300 + 1) {
j = 300 + 1;
}
return (i + j * (300 + 2));
}
@Override
public void render(double [] dens) {
GraphicsContext gc = canvas.getGraphicsContext2D();
for (int y = 0; y < canvas.getHeight(); y += 3) {
for (int x = 0; x < canvas.getWidth(); x += 3) {
double num = dens[index(x / 3, y / 3) ] ;
byte pixelValue = (byte ) ((num > 255) ? 255 : num);
int color = pixelValue & 0xFF;
gc.setFill(Color.grayRgb(color));
gc.fillRect(x, y, 3, 3);
}
}
}
the render handler method
public void render() {
Platform.runLater(() -> mouseAdapter.render(fluid.dens.clone()));
}```
then pass two args, array + index ?
array.clone()
it probably was a concurrency issue
does it work ?
@grand garnet
Your question has been closed due to inactivity.
If it was not resolved yet, feel free to just post a message below
to reopen it, or create a new thread.
Note that usually the reason for nobody calling back is that your
question may have been not well asked and hence no one felt confident
enough answering.
When you reopen the thread, try to use your time to improve the quality
of the question by elaborating, providing details, context, all relevant code
snippets, any errors you are getting, concrete examples and perhaps also some
screenshots. Share your attempt, explain the expected results and compare
them to the current results.
Also try to make the information easily accessible by sharing code
or assignment descriptions directly on Discord, not behind a link or
PDF-file; provide some guidance for long code snippets and ensure
the code is well formatted and has syntax highlighting. Kindly read through
https://stackoverflow.com/help/how-to-ask for more.
With enough info, someone knows the answer for sure π
index is a whole function?
it shows a white screen and that's it
You got a response?
not yet π
@steep elbow for the time being, i just copied the index function again into the class that needs it
like this:
however, whenever i start it up it just shows this
whenever I don't use run later, it works
place a brakpoint and see if this method is getting called
that's the red circle thingy right?
yes
outside the argument?
otherwise copying is kinda useless
you want to copy so you don't have concurrency problems
oh
so this yeah?
double[] arrayToPass = fluid.dens.clone();
Platform.runLater(() -> mouseAdapter.render(arrayToPass));```
yes
does it work?
i mean starting density of the fluid is 0
it's normal to be that
lemem check with the awt one just to confirm
well the output isn't
nope
that's what i was saying
if i remove the runLater
it renders
but then freezes
after like 5 seconds
Pastebin.com is the number one paste tool since 2002. Pastebin is a website where you can store text online for a set period of time.
could it be to do with my main simulation thread?
why are coords an array instead of two ints
and you will have to debug this
try to see why it doesn't display on the canvas
also what is Color.grayRgb
i don't see such a method
does it make a big difference?
im not the best at this π
why would you do that ?
it's from jfx scene.paint.Color
idk i thought it was easier lmao
ok after like 10 seconds it turns fully black
this takes very long apparently
this as well
what takes very long ?
I shouldn't have to change this for now because it works completely fine in awt so i know jfx is the problem
the gc.setFill()
there is no way a setter would take 18s
18s over how much time ?
how do i check?~
how much time did yo run the profiler ?
i think i went off it i can't find how long π
lemme redo
oh wait
i found it
45 seconds
huh
this right?
ok so it seems to be accessing the rendering method on time
it's just not actually rendering it
hey @boreal socket how do i check the memory usage?
On the default profiler window on the top right
By default it shows CPU time
But you can change it to Memory
Not at home
Canβt show it
what
did you comment it?
what
So my solution works 
I do not know π
wasn't yours using the jfx animation timer though
teacher will kill me off π
you don't need this
just use a buffer
and use I think setPixels
i used a buffer in awt iirc
yes i did
public synchronized void render(DataProvider pixelStream) {
BufferedImage imageContainer = new BufferedImage(IMAGE_WIDTH, IMAGE_HEIGHT, BufferedImage.TYPE_BYTE_GRAY);
byte[] buffer = ((DataBufferByte) imageContainer.getRaster()
.getDataBuffer()).getData();
for (int y = 0; y < imageContainer.getHeight(); y++) {
for (int x = 0; x < imageContainer.getWidth(); x++) {
buffer[y * IMAGE_WIDTH + x] = pixelStream.provideData(x, y);
}
}
this.buffer = imageContainer;
repaint();
}
}```
Detected code, here are some useful tools:
You do if you want to do it the preferred way from the beginning
what's the equivalent of a BufferedImage in jfx?
like what shoudl i serach up for if i wanna start using a bfufer in jfx
use a pixel writer
PixelWriter p = gc.getPixelWriter();
p.setPixels(0, 0, W, H, pixelFormat, buffer, 0, W);
why would your teacher get upset for using an AnimationTimer? are you expected to implement a similar system?
does this rendering make sense?
@Override
public void render(double[] dens) {
PixelWriter pixelWriter = canvas.getGraphicsContext2D()
.getPixelWriter();
for (int y = 0; y < canvas.getHeight() ; y += 3) {
for (int x = 0; x < canvas.getWidth(); x += 3) {
double num = dens[index(x / 3, y / 3)];
byte pixelValue = (byte) ((num > 255) ? 255 : num);
int color = pixelValue & 0xFF;
for (int i = x; i < 3; i++) {
for (int j = y; j < 3; j++) {
pixelWriter.setColor(x+i, y+j, Color.grayRgb(color));
}
}
}
}
}```
well he likes when you make the stuff you need yourself
unless it's very necessary
use a buffer
try it and see if it causes problem
but no, that's not a buffer
see this
yeah it doesn't load and also π
yep will do rn
how is this even possible
no idea it ran for 10 seconds as well
yep
21mb a second
yea
π
so how do you even get 35Gb lol
I am running it on a quantum computer
my qbits travel at the speed of light
ok im quite unsure on what to assign into my buffer
i have this so far
@Override
public void render(double[] dens) {
PixelWriter pixelWriter = canvas.getGraphicsContext2D()
.getPixelWriter();
int[] buffer = new int[900 * 900];
for (int y = 0; y < canvas.getHeight() ; y += 3) {
for (int x = 0; x < canvas.getWidth(); x += 3) {
double num = dens[index(x / 3, y / 3)];
byte pixelValue = (byte) ((num > 255) ? 255 : num);
int color = pixelValue & 0xFF;
buffer[y*900 + x] = ;
}
}
}```
Detected code, here are some useful tools:
@Override
public void render(double [] dens) {
PixelWriter pixelWriter = canvas.getGraphicsContext2D().getPixelWriter();
int [] buffer = new int [900 * 900] ;
for (int y = 0; y < canvas.getHeight(); y += 3) {
for (int x = 0; x < canvas.getWidth(); x += 3) {
double num = dens[index(x / 3, y / 3) ] ;
byte pixelValue = (byte ) ((num > 255) ? 255 : num);
int color = pixelValue & 0xFF;
buffer[y * 900 + x] = ;
}
}
}
the buffer contains the pixels
yes
thing is
I am using Color.grayRgb(color) to get the value
but that is of type Color and not int
that's just the value for grayscale no?
it only worked in awt because my image was of type BufferedImage.TYPE_BYTE_GRAY
wot
well
if pixelvalue is 100, then the rgb is (100, 100, 100)
yeah i realised
i just did the pixelFormat as PixelFormat.getByteBgraInstance()
which i assume is to do with GRAY
assuming is not a good idea
Returns a WritablePixelFormat instance describing a pixel layout with the pixels stored in adjacent bytes with the non-premultiplied components stored in order of increasing index: blue, green, red, alpha.
gra probably means gradient
just use animation timer 
teacher gonna kill me fr π
who does animation timer solve this
not this what you are talking about, but the simulation in general
its the correct way in the first place
and I have no idea why javafx struggles so much here
i never had this problem
because it takes an byte[] instead of int[]

π
even if i change my method to
it still throws an OutOfMemoryError
somehwere it is saving everything
wut
π
so the problem wasn't this at the first place
there is no way
I think its correct
show your logic loop
nah
because you clone like 30 times a second
20mb per second
and that profiler showing all the ram used while running the profiler
and not at one time
but its still way toooooo much
anyways my solution works 
and since he is doing a lot of copies everywhere else
then this single copy can't be the problem
aka the run overrided method
@Override
public void run() {
double deltaTimeSeconds = 1.0 / TPS;
double lastFrameTime = nanosToSeconds(System.nanoTime());
double secondsToConsume = 0.0;
while (simulationThread != null) {
double currentFrameTime = nanosToSeconds(System.nanoTime());
double lastFrameNeeded = currentFrameTime - lastFrameTime;
lastFrameTime = currentFrameTime;
secondsToConsume += lastFrameNeeded;
while (secondsToConsume >= deltaTimeSeconds) {
update(deltaTimeSeconds);
secondsToConsume -= deltaTimeSeconds;
}
render();
double currentFPS = 1.0 / lastFrameNeeded;
if (currentFPS > FPS) {
double targetSecondsPerFrame = 1.0 / FPS;
double secondsToWaste = Math.abs(targetSecondsPerFrame - lastFrameNeeded) / 1000000;
try {
TimeUnit.SECONDS.sleep(secondsToMillis(secondsToWaste));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}```
Detected code, here are some useful tools:
https://gist.github.com/SquidXTV/a04bff2ac788d7f484587d8708c66074
My solution using concurrency instead of AnimationTimer
works fine as well
what's the main difference?
between which? between both my solution? or between my and yours?
mine and yours
can you share your full current state of your application pls
like update the branch

sure
mine is working π
fairs
it's rlly broken rn
why you got such a weird rendering method?
I am gonna use yours π
but im trying to understand it first
private static final ExecutorService EXECUTOR = Executors.newSingleThreadExecutor(r -> {
Thread thread = new Thread(r);
thread.setDaemon(true);
return thread;
});```
What does this do?
like the Daemon
do you know what an executor is in the first place?
never heard of it
the newer and better way of submitting Runnable tasks
instead of using:
Thread thread = new Thread(runnable);
thread.start();
you are using an executor
which will handle stuff for you etc
and uses stuff like Future
a daemon thread is basically a thread which wont stop your whole application from terminating
currently with your solution, you already noticed, if you click the close button the actual application continues running
because the thread is non daemon
and thus the application waits until all non daemon threads finished executing
the
r -> {
Thread thread = new Thread(r);
thread.setDaemon(true);
return thread;
}
is the thread factory called by the executor service
which gives you the ability to change the creation of the thread
and thus make it daemon
thread factory π
ahhh
whats wrong with it
it sounds funny
like i can just imagine like a factory
where they are making threads
yeah actually pretty good name for it
makes it self explanatory
you get a Runnable and need to create a thread for it
but you most likely want to change the usage of the executor in the controller later on
so that you store the future created by the initialize method
and properly stop the runnable if someone switches scenes
what exactly is future?
A Future represents the result of an asynchronous computation
thats returned by using EXECUTOR.submit
and then when switching the scene you should also stop the runnable
so the simulation actually stops
ahh ok
wdym?
like it doesn't respond to mouse inputs or anything
you can update it yeah
just need to update the run method
but thats usually not recommended anyways
ok depends
but yea
then you need to implement it
Hmm ok
updated branch btw
I think my project just hates me
u sure ur running the right project and everything?
im on master2
the thing that got overwritten now π
but it just consisted of the code shared in the gist
I found the problem
protected Fluid() {
this.WIDTH = 100;
this.HEIGHT = 100;
these need to be 300 and 300
the thing is you got smth like cell size
and I ignored that
and work with 1 pixel = 1 cell
lmao
actually a life saviour
i think i should be fine with the rest because no more concurrency problem πͺ
lemme try a much bigger size and check if it lags
oh it's not too laggy
roughly 30fps
yeah but your Fluid.java is weird anyways
yeah im gonna optimise it after finishing the prototype
needs some improvements
(not gonna ask for math help dw)
ke
have fun
LMAO
Maybe it's time i start expalining maths problems to you
nah
@grand garnet
Your question has been closed due to inactivity.
If it was not resolved yet, feel free to just post a message below
to reopen it, or create a new thread.
Note that usually the reason for nobody calling back is that your
question may have been not well asked and hence no one felt confident
enough answering.
When you reopen the thread, try to use your time to improve the quality
of the question by elaborating, providing details, context, all relevant code
snippets, any errors you are getting, concrete examples and perhaps also some
screenshots. Share your attempt, explain the expected results and compare
them to the current results.
Also try to make the information easily accessible by sharing code
or assignment descriptions directly on Discord, not behind a link or
PDF-file; provide some guidance for long code snippets and ensure
the code is well formatted and has syntax highlighting. Kindly read through
https://stackoverflow.com/help/how-to-ask for more.
With enough info, someone knows the answer for sure π
.
π
my simulation stopped working π
π
much
before I just kept dividing by 3
but i just changed it to CELL_LENGTH
and it started lagging
Canβt check right now
YOOO
ok
so lemme update master2 rq
done!
idk why it isn't working
did the branch update for you?
yea
the problem is the width (BoxFluid) vs width (window, visual) probably
like yesterday
but idk why
?
what?
.
this was the problem yesterday as well
idk how to fix it so you can have it separate
looking in some minutes
yeah idk
it must be some calculation issue
not really interested in checking all the calculations
double num = fluid.dens[fluid.index(x / 5, y / 5)];
this already confuses me
yeah i will try checking all of them
this one is quite easy to understand
oh i was checking if the variable was the problem
that's why i changed it to 5
my baddd
all good
i've just been staring at this fro 30 mins
try using a debugger π€·ββοΈ
I don't get how i would here π
the x, y logic is wrong no?
how?
what the 164 gb with fire mean?
storage it uses over the time it is run
also in this case I would use setPixels instead of creating a new double nested for loop
writer.setPixels(x * CELL_LENGTH, y * CELL_LENGTH, CELL_LENGTH, CELL_LENGTH, ...);
not sure about the PixelFormat
ingame right now
but you would want a pixel format/reader that only uses the one color
no worries
thing is
if i remove the runLater
it runs but just freezes
but if i don't remove runLater it doesnt run at all
withhout the runLater it works but then crashes straigth away π
you need the runLater
hmm
but then why does it run fine without it but just crash after a couple sends
i don't think the fluid class is the problem
i think it's the concurrency
I doubt that, if you use Platform.runLater then its fine
I think its your logic
as in the fluid logic?
yeah but this profiling just leads to guessing
umm
i just randomly started it up
and now it's working
π
like it didn't crash at all
ok when i change the cell length now it bugs
yeah exactly
ok
lemem try debug with a different cell length
but before i do so imma push onto master2
shitty thing seems to break whenever i change cell size π
im doing a wrong calculation somewhere
has to be
yeah exactly
i want to kms
I can't find it
because surely rendering a 24x24 fluid is a lot easier than rendering a 120x120 fluid
you know whta
imma go on my laptop
and check
fresh mind
how do i load master2 from git onto intelliJ
idk why iut does this when cell length = 5
try to find it out
im trying π
i've been jus trying random tests
but idk why it increases storage when i increase cell length
it should technically decrease
ok so it does paint a cell correctly
it paints the edge colors correctly according to cell length as well
the problem will be in the Fluid.java anyways
don't think so because the density is updated accordingly
but it already crashes with a different CELL_LENGTH even without updating the rendering
and it was only used in Fluid.java
most the time it doesn't
yeah so it doesnt work
but if you touch it like luckily
you can get somethin glike this
i did that
and then it froze
yeah so it doesnt work
oh
nooo
ok
i think i am getting confused
so
my problem is
this num
is correct
i believe
but it's just the writer.setColor which is bugging
like if i do this
it will output changing values
i.e. it wouldn't output 0 indefinitely
it's just that it's not rendering
so the calculations within Fluid.java are fine
it's just the way it's being rendered i believe
System.out.println("Writing value to coord: \n" + (x * CELL_LENGTH + i) + "\n" + (y * CELL_LENGTH + j) + "\n" + Color.grayRgb(color));
but the code worked before introducing CELL_LENGTH
so its probably not the concurrency making the issue
hmm
if i do this
it outputs stuff like this
which means that color is defo being given in
just not being displayed
π€
what if it's because CELL LENGTH is introducing an issue in the writer
because the fluid class is clearly giving colors to be written to the canvas at a given pixel
if the run later is taken away as well, it will display some sort of image but will ofc crash because it has no runLater
trying to remove the runLater is the first problem
maybe its your mouseAdapter?
that's what i was thinking maybe
this.x = (x / cellLength);
this.y = (y / cellLength);
ah ok didnt see it
but will it work here then:
private void addSourcesFromUI(Fluid fluid) {
synchronized (fluid) {
mouseAdapter.consumeSources(fluidInput -> {
fluid.u[fluid.index(fluidInput.x, fluidInput.y)] += fluidInput.forceX;
fluid.v[fluid.index(fluidInput.x, fluidInput.y)] += fluidInput.forceY;
fluid.u[fluid.index(fluidInput.x + 1, fluidInput.y)] += fluidInput.forceX;
fluid.v[fluid.index(fluidInput.x + 1, fluidInput.y)] += fluidInput.forceY;
fluid.u[fluid.index(fluidInput.x - 1, fluidInput.y)] += fluidInput.forceX;
fluid.v[fluid.index(fluidInput.x - 1, fluidInput.y)] += fluidInput.forceY;
fluid.u[fluid.index(fluidInput.x, fluidInput.y + 1)] += fluidInput.forceX;
fluid.v[fluid.index(fluidInput.x, fluidInput.y + 1)] += fluidInput.forceY;
fluid.u[fluid.index(fluidInput.x, fluidInput.y - 1)] += fluidInput.forceX;
fluid.v[fluid.index(fluidInput.x, fluidInput.y - 1)] += fluidInput.forceY;
fluid.dens[fluid.index(fluidInput.x, fluidInput.y)] += fluidInput.density;
});
}
}
I kinda doubt that
hmm
yep mouse inputs are fine
just checked whether these match the sizes
it works
so i click pixel at (599,599) on screen
and it adds to (299,299) on fluid
PixelWriter writer = canvas.getGraphicsContext2D()
.getPixelWriter();
for (int y = 0; y < fluid.HEIGHT; y++) {
for (int x = 0; x < fluid.WIDTH; x++) {
double num = fluid.dens[fluid.index(x, y)];
int color = (byte) (num > 255 ? 255 : num) & 0xFF;
for (int i = 0; i < CELL_LENGTH; i++) {
for (int j = 0; j < CELL_LENGTH; j++) {
writer.setColor((x * CELL_LENGTH + i), (y * CELL_LENGTH + j), Color.grayRgb(color));
}
}
}
}
});````
Detected code, here are some useful tools:
PixelWriter writer = canvas.getGraphicsContext2D().getPixelWriter();
for (int y = 0; y < fluid.HEIGHT; y++) {
for (int x = 0; x < fluid.WIDTH; x++) {
double num = fluid.dens[fluid.index(x, y) ] ;
int color = (byte ) (num > 255 ? 255 : num) & 0xFF;
for (int i = 0; i < CELL_LENGTH; i++) {
for (int j = 0; j < CELL_LENGTH; j++) {
writer.setColor((x * CELL_LENGTH + i), (y * CELL_LENGTH + j), Color.grayRgb(color));
}
}
}
}
}
);
problem is defo here
OKay
cell length = 1, time = 25 secs
ran smoothly
okay not bad for 25 seconds
now
cell length = 2, time = 26 secs
did not run smoothly
only saw 2 frames out of 1000+
as you can see, the fluid calculation times are roughly the same but the writing takes a lot longer
use a buffer or squid solution
and writing takes up more GB
and don't make the fps as fast as the tps
isn't that the pixelwriter class?
oh this is Squid's gameloop
no
is it the stuff you said yesterday with the WriteablePixel stuff
yes
and don't make the fps as fast as the tps
yeah ok
in terms of this, I should only render if I have time to render after an update tick right?
and that's if my current deltaTime is less than the max delta time?
no
you always have time
sicne you do a copy
wait
you don't
then it's wrong
you are not copying the array
also didn't you said you were using squid solution ?
then why are you using runlater ?
I shared two solutions
one with animation timer
and one threaded
both works
check pins
but he changed some stuff and now it doesnt work
whatttt π
im lost now
@Override
public void run() {
Fluid fluid = new BoxFluid(IMAGE_WIDTH / CELL_LENGTH, IMAGE_HEIGHT / CELL_LENGTH, CELL_LENGTH, 2, 2, 4);
long current = System.nanoTime();
while (true) {
long l = System.nanoTime();
double deltaMillis = (l - current) / 1_000_000_000.0;
fluid.step(deltaMillis);
Platform.runLater(() -> {
addSourcesFromUI(fluid);
PixelWriter writer = canvas.getGraphicsContext2D()
.getPixelWriter();
for (int y = 0; y < fluid.HEIGHT; y++) {
for (int x = 0; x < fluid.WIDTH; x++) {
double num = fluid.dens[fluid.index(x, y)];
int color = (byte) (num > 255 ? 255 : num) & 0xFF;
for (int i = 0; i < CELL_LENGTH; i++) {
for (int j = 0; j < CELL_LENGTH; j++) {
writer.setColor((x * CELL_LENGTH + i), (y * CELL_LENGTH + j), Color.grayRgb(color));
}
}
}
}
});
current = l;
}
}```
Detected code, here are some useful tools:
what's the usual way to make this into a loop where I limit the TPS and can vary the FPS?
and not eonly that
zabu showed me one a long time ago for a fractal algorithm and that's the one i use in awt π
but you shouldn't share fluid to the code in runlater
you can only work with code that won't be modified
ok i've done this and it seems to be rendering at least a few frames
time for the buffering stuff
thing is
i have a duplicated code fragment becuase I can't make a specific class in the fluid static
it's this method java public int index(int i, int j) { if (i < 0) { i = 0; } if (i > FLUID_WIDTH + 1) { i = FLUID_WIDTH + 1; } if (j < 0) { j = 0; } if (j > FLUID_HEIGHT + 1) { j = FLUID_HEIGHT + 1; } return (i + j * (FLUID_WIDTH + 2)); }
Detected code, here are some useful tools:
im not sure if i should leave it duplicated or what π
@grand garnet
Your question has been closed due to inactivity.
If it was not resolved yet, feel free to just post a message below
to reopen it, or create a new thread.
Note that usually the reason for nobody calling back is that your
question may have been not well asked and hence no one felt confident
enough answering.
When you reopen the thread, try to use your time to improve the quality
of the question by elaborating, providing details, context, all relevant code
snippets, any errors you are getting, concrete examples and perhaps also some
screenshots. Share your attempt, explain the expected results and compare
them to the current results.
Also try to make the information easily accessible by sharing code
or assignment descriptions directly on Discord, not behind a link or
PDF-file; provide some guidance for long code snippets and ensure
the code is well formatted and has syntax highlighting. Kindly read through
https://stackoverflow.com/help/how-to-ask for more.
With enough info, someone knows the answer for sure π
.
@steep elbow I'm quite unsure on what to put for the parameters here
writer.setPixels(0,0,IMAGE_WIDTH, IMAGE_HEIGHT, ..., buffer, 0, IMAGE_WIDTH);
I checked docs and saw something about PixelFormat but I was quite unsure on what one to use
ok i think i did it but it doeswn't seem to be rendering
public void render(double[] dens) {
int[] buffer = new int[IMAGE_HEIGHT * IMAGE_WIDTH];
int bufferIndex = 0;
for (int y = 0; y < FLUID_HEIGHT; y++) {
for (int x = 0; x < FLUID_WIDTH; x++) {
double num = dens[index(x, y)];
int color = (int) (num > 255 ? 255 : num) & 0xFF;
int rgb = (color << 16) | (color << 8) | color;
for (int i = 0; i < CELL_LENGTH; i++) {
for (int j = 0; j < CELL_LENGTH; j++) {
buffer[bufferIndex++] = rgb;
}
}
}
}
PixelWriter writer = canvas.getGraphicsContext2D()
.getPixelWriter();
writer.setPixels(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, PixelFormat.getIntArgbInstance(), buffer, 0, IMAGE_WIDTH);
}
Detected code, here are some useful tools:
ok this sounds weird
i fixed everything using the buffer
like it works
I still haven't done the variable FPS yet
but my program renders better without runLater than with it lmao
any idea on how i can implement a varaible delta time loop?
you shouldnt be runLater in a situation like that. what you should be doing is called "active rendering", where you disable passive rendering (letting the FX thread handle rendering), grabbing the graphics context & rendering on your own thread
have you looked into how to implement variable delta time?
where you disable passive rendering (letting the FX thread handle rendering)
I think this doesnt work in javafx, its not a game engine or similar
and its something you wouldnt want in javafx
instead you would use AnimationTimer which automatically runs handle on the fx thread
You would continue with a continuous while loop which always runs the fluid.step method, but you want to check some if statement for the rendering
@grand garnet
Your question has been closed due to inactivity.
If it was not resolved yet, feel free to just post a message below
to reopen it, or create a new thread.
Note that usually the reason for nobody calling back is that your
question may have been not well asked and hence no one felt confident
enough answering.
When you reopen the thread, try to use your time to improve the quality
of the question by elaborating, providing details, context, all relevant code
snippets, any errors you are getting, concrete examples and perhaps also some
screenshots. Share your attempt, explain the expected results and compare
them to the current results.
Also try to make the information easily accessible by sharing code
or assignment descriptions directly on Discord, not behind a link or
PDF-file; provide some guidance for long code snippets and ensure
the code is well formatted and has syntax highlighting. Kindly read through
https://stackoverflow.com/help/how-to-ask for more.
With enough info, someone knows the answer for sure π
How can i stop like a thread?
wdym
well one thing
i have this:
viscositySlider.valueProperty()
.addListener((obs, oldVal, newVal) -> {
viscosity = ExtraMath.roundToTwoDecimalPlaces(viscositySlider.getValue());
viscosityLabel.setText("Viscosity: " + viscosity);
simulation.setViscosity(viscosity);
});
flowspeedSlider.valueProperty()
.addListener((obs, oldVal, newVal) -> {
flowspeed = ExtraMath.roundToTwoDecimalPlaces(flowspeedSlider.getValue());
flowspeedLabel.setText("Flow Speed: " + flowspeed);
simulation.setDiffusionRate(flowspeed);
});```
and once i start the simulation, I want to stop this listening from happening
idk how to do that
and then with this:
@FXML
private void onStartSimulationClick() {
if (!simulationStarted) {
viscositySlider = new Slider();
flowspeedSlider = new Slider();
EXECUTOR.submit(simulation);
startSimulationButton.setText("Pause Simulation");
simulationStarted = true;
} else {
Fluid fluid = simulation.getFluid();
EXECUTOR.shutdown();
createListeners();
startSimulationButton.setText("Start Simulation");
}
}```
Detected code, here are some useful tools:
I don't think Executor.shutdown() stops the thread from running (stops the simulation)
no it doesnt
store the Future
created by EXECUTOR.submit
wait what
remove the listener?
call removeListener
there should be
I just did this intead:
viscositySlider = new Slider();
flowspeedSlider = new Slider();```
the method call:
EXECUTOR.submit(simulation);
returns an Future<?>
store that in a field variable
and that stops the thread?
no
so that you can later use that variable
these are the methods of Future<?>
just use the cancel method to stop the runnable
@grand garnet just toggle the variable which is in your while condition
so that you can pass it again to remove it
oh yeahh
or that
makes more sense in this context ig 
but what if i want to carry on the simulation
wdym
ah you want a pause mechanism?
you need both anyways
but you would need to create some pause mechanism yourself
in the runnable class
you have two different scenarios:
- stop the simulation ->
Future#cancel - pause the simulation -> exit the loop, therefore the runnable exits, but keep the stored state of
Fluid.java, then resubmit once you want to continue
you can exit the loop by creating some boolean state that you change in your controller
simple as that
this is long
you dont need that if you do the suggestion by ala
you dont need to remove the listener if you just disable the sliding
which even makes more sense here
because the user shouldnt be able to change the settings if they have no effects while the simulation runs
but the sliding is in a different class
isnt it in the same scene? therefore in the same controller?
I am not here to guess
give more context
yeah but the runnable isn't in the controller
yeah
the runnable is getting started inside the controller as well
why would the runnable need to know about the slider in the first place?
i don't want the slider to interfere with the runnable
exactly
which is why i need to stop the sliding mechanism when the runnable is about to start and start the sliding when it has stopped
then I have one question
yea
@FXML
private void onStartSimulationClick() {
if (!simulationStarted) {
viscositySlider = new Slider();
flowspeedSlider = new Slider();
EXECUTOR.submit(simulation);
startSimulationButton.setText("Pause Simulation");
simulationStarted = true;
} else {
Fluid fluid = simulation.getFluid();
EXECUTOR.shutdown();
createListeners();
startSimulationButton.setText("Start Simulation");
}
}
you create the slider at the same time someone starts the simulation, wouldnt they need to exist before?
and also you can easily disable them here in this method
you create the slider at the same time someone starts the simulation, wouldnt they need to exist before?
I don't want that i just thought it would remove the listening lol
yeahh
it also manages when the runnable starts and pauses
yea
I think i am going to remove flowlines though
but it seems like you got a check button for it though
you dont need to
oh
this?
ohh so i would change this:
private void createListeners() {
viscositySlider.valueProperty()
.addListener((obs, oldVal, newVal) -> {
viscosity = ExtraMath.roundToTwoDecimalPlaces(viscositySlider.getValue());
viscosityLabel.setText("Viscosity: " + viscosity);
simulation.setViscosity(viscosity);
});
flowspeedSlider.valueProperty()
.addListener((obs, oldVal, newVal) -> {
flowspeed = ExtraMath.roundToTwoDecimalPlaces(flowspeedSlider.getValue());
flowspeedLabel.setText("Flow Speed: " + flowspeed);
simulation.setDiffusionRate(flowspeed);
});
}```
into this:
```java
private void createListeners() {
viscositySlider.valueProperty()
.addListener((obs, oldVal, newVal) -> {
if (viscosityListening) {
viscosity = ExtraMath.roundToTwoDecimalPlaces(viscositySlider.getValue());
viscosityLabel.setText("Viscosity: " + viscosity);
simulation.setViscosity(viscosity);
}
});
flowspeedSlider.valueProperty()
.addListener((obs, oldVal, newVal) -> {
if (flowspeedListening) {
flowspeed = ExtraMath.roundToTwoDecimalPlaces(flowspeedSlider.getValue());
flowspeedLabel.setText("Flow Speed: " + flowspeed);
simulation.setDiffusionRate(flowspeed);
}
});
}```
Detected code, here are some useful tools:
no
you keep your old listener
and just disable the slider itself
there is a method for that
you can disable some node in javafx
it will still be there
but it cant be used
in this case the user cant slide while its disabled
I can't find it π
is it blockIncrementProperty?
setDisabled
ohhh
both exist actually
this is protected though
so you wont be able to use it anyways
@grand garnet
Your question has been closed due to inactivity.
If it was not resolved yet, feel free to just post a message below
to reopen it, or create a new thread.
Note that usually the reason for nobody calling back is that your
question may have been not well asked and hence no one felt confident
enough answering.
When you reopen the thread, try to use your time to improve the quality
of the question by elaborating, providing details, context, all relevant code
snippets, any errors you are getting, concrete examples and perhaps also some
screenshots. Share your attempt, explain the expected results and compare
them to the current results.
Also try to make the information easily accessible by sharing code
or assignment descriptions directly on Discord, not behind a link or
PDF-file; provide some guidance for long code snippets and ensure
the code is well formatted and has syntax highlighting. Kindly read through
https://stackoverflow.com/help/how-to-ask for more.
With enough info, someone knows the answer for sure π
if anyone wants to see how the project is doing you can always check master3
it's not bad
quite a few bugs but i'll fix them soon
I could give you a code review of your current state if you want
hmm
sure π
if you can, is it possible if you can ping me once you finish all the feedback and stuff?
i don't wanna get too distracted that's why
yeah all good
going to take a while anyways
ok so I should ignore bugs right?
because after pausing the simulation I cant restart it
Code Review
/resources
- dont just put everything inside it, structure it, one extra folder for fxml files, one folder for css
don't have to, it would be good if i find an unknown bug
com.fluidity.program.utilities
- might name the package just
util, but thats just minor
CircularLinkedList.java
- remove unused imports
- use varargs for constructor instead of array
ExtraMath.java
- remove unused imports
- might come up with a better class name
- add private constructor for static helper classes
FileHandler.java
- make logger final
writeLine()is supposed to append right? in that case its missing the OpenOption for that
LinkedListNode.java
- why final in the constructor parameters?
Queue.java
- empty class? probably work in progress ig
start being bothered by it. even if not for you, do it for us
com.fluidity.program.ui
ProgramState.java
- pls give this enum finally a better name

- use javadocs instead of normal comments
MouseListener.java
- why does this interface defines the
consumeSourcesmethod? it doesnt fit imo, its not common to all MouseListener
Manager.java
- use javadocs instead of normal comments
- why final for the constructor parameter?
- might make the title a static constant?
- why
window.show()being used in theloadScene?
GraphicsHandler.java
- dont use
Integer[], always prefer primitive - make the
fpsValues,cellSizeValues,iterationsValuesstatic constants instead - also why is this called
GraphicsHandler? isnt it related to the settings instead?
FluidUIAction.java
- as said before, instead of having a single enum value for primary and secondary just combine them into one enum? might be a bad idea based on context
- give the
IllegalArgumentExceptiona custom exception message
com.fluidity.program.ui.controllers
- might rename package to
controllerinstead
MainMenuController.java
- its overriding the
initializemethod, but its empty, do that in theControllerabstract class instead
ExitController.java
- its overriding the
initializemethod, but its empty, do that in theControllerabstract class instead
AboutController.java
- its overriding the
initializemethod, but its empty, do that in theControllerabstract class instead
RecordingsController.java
- its overriding the
initializemethod, but its empty, do that in theControllerabstract class instead
SettingsController.java
- rearrange the fields, statics first
- make
graphicsHandlerprivate - the constant
configPathis not used inFluidity.java, why? - use appropriate class for
primaryButtonColor/secondaryButtonColor, dont use String for it - instead of keyBindMap/buttonMap create a custom utility class for configuration that stores all that stuff, also use
java.util.Propertiesas said before - I dont like that there is a switch in the try - catch in
loadConfigurations
SimulationController.java
canvasX, why theXin the name?- some fields are not used at all
- might create some simulation logic class for handling all the properties of the simulation (viscosity, sourceQueue, ...), doing this will remove a lot of fields
previousCoordsshould not be anint[]array, instead use somePointclass or smth which actually represents whats getting stored
TestSimulationController.java
- skipping this, seems to get deleted later anyways
com.fluidity.program.simulation
SimulationThreaded.java
- check naming convention for the fields and constructor parameter
- your run method needs a revamp, dont use Thread.sleep here, your logic for the fps/tps stuff is bad and would do it differently
- index is a duplicate method
rendercan get some benefits
FluidInput.java
- why not using records?
com.fluidity.program.simulation.fluid
Fluid.java
- I wont check the full calculation and stuff, but:
- fix naming convention
- for nested loop start with height loop first
- try to fix the memory "issues" here, I doubt you always need to create multiple double arrays each step
BoxFluid.java
- fix naming convention
- implement setBarriers
TunnelFluid.java
- fix naming convention
- implement setBarriers
@grand garnet
yess tysmm
wdym fix naming convention btw π
The naming convention in Java is as follows:
Classes:
PascalCase. Example: GoldMiner, FoodDestroyer, DispenserBuilder
Methods / Fields / Variables:
camelCase. Example: foodAmount, integerValue, goodBoyAsADog
Packages:
All lowercase. Preferably the domain name backwards. Example: "google.com" ==> com.google, "ialistannen.me" ==> me.ialistannen
Followed by the project name. Example: Project "Builder" ==> com.google.builder
Constants/Enum entries:
UPPER_SNAKE_CASE. Where words are separated by underscores. Example: RED_DOG, I_AM_THE_BEST, CONQUER_WORLD
@grand garnet
Your question has been closed due to inactivity.
If it was not resolved yet, feel free to just post a message below
to reopen it, or create a new thread.
Note that usually the reason for nobody calling back is that your
question may have been not well asked and hence no one felt confident
enough answering.
When you reopen the thread, try to use your time to improve the quality
of the question by elaborating, providing details, context, all relevant code
snippets, any errors you are getting, concrete examples and perhaps also some
screenshots. Share your attempt, explain the expected results and compare
them to the current results.
Also try to make the information easily accessible by sharing code
or assignment descriptions directly on Discord, not behind a link or
PDF-file; provide some guidance for long code snippets and ensure
the code is well formatted and has syntax highlighting. Kindly read through
https://stackoverflow.com/help/how-to-ask for more.
With enough info, someone knows the answer for sure π
.
Looking good
need to make sure the pause button actually pauses the simulation rather than ending it fully π
I already explained how to do that, still need help?
it's very cool cos the different pieces of the simulation i made are coming together
umm if you can, can you just refer to where you did it?
i don't want to look through your review yet because i don't wanna get distracted
good idea, thats stuff you can fix later
yeah
you remember the Future<?> stuff I talked about before?
to do with the executor thing?
yeah
ok which exact point is starting your simulation? do you know?

yay
do you know what that method returns
a boolean?
this is your Future<?> which stores and represents the current state of this task (runnable) submitted in that line
store it in a field
so you can later refer to it again
thats a generic wildcard
its used to refer to an unknown type
you dont need it here anyways
you use just a Runnable which means that you dont have some "result"
other scenarios might have some result that get available after the task is finished
like some computation
but you dont have that
yeah
no
if you want to pause the simulation you actually use Future#cancel
and if you want to start/restart the simulation you just call submit again, which creates a new future which you again store
if you keep the same SimulationThreaded instance its going to have the same state and continue at the point it stopped
as long as you dont change the internal Fluid instance
yeah ok that's fine then
what if i want to change the simulationThreaded instance
why would you want that?
like if i want to change the viscosity of the fluid after pausing it
then you are doing smth wrong
then you dont need a new simulationThreaded instance
just update the existing one
im not sure what though
share the code
private void startSimulation() {
viscositySlider.setDisable(true);
diffusionRateSlider.setDisable(true);
simulationFuture = EXECUTOR.submit(simulation);
startSimulationButton.setText("Pause Simulation");
simulationStarted = true;
}
private void endSimulation() {
viscositySlider.setDisable(false);
diffusionRateSlider.setDisable(false);
simulationFuture.cancel(true);
createListeners();
startSimulationButton.setText("Start Simulation");
}```
