I need a JavaFX program to set text to a random color and opacity I'm not sure on how to do it.
Here is a sample of my code:
Text text1 = new Text();
text1.setText("Java");
text1.setFont(Font.font("Times New Roman", FontWeight.BOLD, FontPosture.ITALIC, 22));
text1.setRotate(90);
gridpane.add(text1, 3, 1);
You can use Math.random() to generate a Double in the range [0,1), so you need to do:
text.setOpacity(Math.random());
Color took a little more digging through the docs, but can be accomplished with:
text.setFill(Color.color(Math.random(), Math.random(), Math.random());
setFill comes from Shape, which Text inherits from. setFill takes a Paint, which Color is the simplest implementation of. Color.color(double, double, double) takes the rgb value with doubles in the range [0,1].
Learn how to navigate through the docs and you'll be able to find these sorts of things on your own quickly in the future!
Note: opacity/rgb color all take doubles of the range [0,1] where Math.random() produces in the range [0,1). If you're unfamiliar with this notation, this means Math.random() will NEVER produce 1, only a number smaller than 1 by possible accuracy. This means that you will never have a 100% fully opaque/r/g/b with this method but in reality you probably can't tell the difference, so it's better to use the less complicated method.
Note 2: javafx.scene.paint.Color#color actually provides a four-argument constructor that includes opacity, but I would recommend setting the opacity of the Text node itself as above rather than the opacity of the Paint.
Like this:
Text randomColorText(String txt) {
Text t = new Text(txt);
Random rng = new Random();
int c = rng.nextInt();
int r = c & 255;
int g = (c >>> 8) & 255;
int b = (c >>> 16) & 255;
double op = (c >>> 24) / 255.0;
t.setFill(Color.rgb(r, g, b, op));
// or use only r,g,b above and set opacity of the Text shape: t.setOpacity(op);
return t;
}
Note that the other answer that mentions how Random will never return a double == 1.0, says you won't get the full range of colours is wrong. Color RGB values do not have the same range as double - typically they end up as 8-bit values in the range 0-255 at some point, on some high end applications you might use 16-bits per channel. You will get the full range of colours using the doubles from Random.
You will note that I avoided calling into the random number generator multiple times for what is usually represented by a 32-bit value. (Micro-optimization: Calling nextInt does half the work of nextDouble, and we only need to call it once. I would typically save the instance of Random as a static variable rather than create one every time that method is called. java.util.Random is threeadsafe.)
Related
I have the following java code, which takes three double values (between 0 and 1) of the colors RGB and converts them to decimal format. I understood how the first 8 bit save color x, the second 8 bit color y ... and also how to get the decimal value from the resulting binary. what i dont understand 100% is why we multiply with 255 (i know 128+64+32+16+8+4+2+1). What exactly do we get from multiplying the double value with 255. is it a value which can be stored in 8 bit? And why dont we use 256 (possible amount of one color)?
public final double getR() {
return (1 - cyan);
}
public final double getG() {
return (1 - magenta);
}
public final double getB() {
return (1 - yellow);
}
/**
* Gets the rgb color in one integer.
*
* #return an integer containing the red component in bits 16-23, the green component in bits 8-15
* and the blue component in bits 0-7. Bits 24-32 are zero.
*/
public int getRGB() {
int r = (int) Math.round(getB() * 255);
r |= (int) Math.round(getR() * 255) << 16;
r |= (int) Math.round(getG() * 255) << 8;
return r;
}
Thanks
You need the conversion from double because you cannot store the double value into 8 bit.
Your double values are between 0.0 and 1.0. You can see them as the proportion of color used (e.g. 0.33333333 in yellow means that one-third of the possible yellow is used). As you can see such a double can have many decimal places, which means we need a lot of memory (64-bit) to store such a color.
Your function now tries to store the double value into only 8 bit (256 values). As we said the double value can be seen as a portion (between 0 and 1) and the function calculates the same for 8 bit (between 0 and 255). This can simply be done by multiplying the double value with 255. For the example with yellow (0.33333333 of yellow is used) it is: 0.33333333 * 255 = 84,99999915. The meaning is still the same 84,99999915 yellow parts of 255 yellow parts are used, which is still a third.
In order to get a compressed version of this number, it is rounded to the next integer value. In our example, this is 85, which is really close to the actual portion, but we save a lot of memory.
It makes also sense for the lowest double value 0.0, which is converted to the lowest int value 0. The highest double value 1.0 is converted to 255 (highest 8-bit number).
In conclusion, we convert a double (64-bit) into an only 8-bit number, which has the same proportion of the color, but it is not as accurate.
Edit: As there is also a confusion with the 255: 8 bit can store 256 values (0 to 255). If you can choose 256 as a color value somewhere they use the range 1-256 without 0. Essentially it is the same one shifted by 1.
I'm making a program in which a JPanel is created with a random RGB value and the user has to use buttons to match the color in another JPanel.
I want the random R, G, and B values to be multiples of 15, though, so the user can match the color more easily.
Right now my code looks like this:
int randRed = rand.nextInt(255);
and the same for green and blue. I could use a modulus to repeat the code until it happens to be a multiple of 15 but that would be terribly inefficient.
What is the best method to achieve a random multiple of 15 less than 255?
RIght after posting I figured it out...
int randRed = (rand.nextInt(17)+1)*15;
15 goes into 255 17 times, so just multiple a random int between 0 and 17, add 1, and multiply by 15.
In general,
static int randomBoundedMultiple(int bound, int multiplier){
return new Random().nextInt(bound) * multiplier % bound
}
then, in particular,
randomBoundedMultiple(255, 15);
I'm trying to create a function that will translate float values to a color. I created a simple linear scale:
float value;
float maxValue;
float scaleStep = maxValue / 5;
if (value < scaleStep) {
color = blue
}
if (value > scaleStep && value <= scaleStep * 2) {
color = green
}
if (value > scaleStep * 2 && value <= scaleStep * 3) {
color = yellow
}
if (value > scaleStep * 3 && value <= scaleStep * 4) {
color = orange
}
if (value > scaleStep * 4 && value <= scaleStep * 5) {
color = red
}
but since most (but not all) of the values from the sets that I'm trying to represent are in close proximity from one particular value, graphical representation using linear scale isn't very useful (almost everything is translated to one color).
How can I create a non-linear scale so that differences between the values are more visible?
Interpolation is what you want. Interpolation generates samples between known samples in a dataset.
Here, your known samples are your colors; blue, green, yellow, orange, and red. The colors between those known colors are what you're looking for.
Here's a link to a nice visualizer of interpolation functions.
And here's a few interpolation functions for your convenience. Play with them, find the one that works best for you!
public float linearInterpolation(float start, float end, float normalizedValue) {
return start + (end - start) * normalizedValue;
}
public float sinInterpolation(float start, float end, float normalizedValue){
return (start+(end-start)* (1 - Math.cos(normalizedValue * Math.PI)) / 2;
}
//usage
linearInterpolation(red, green, .5f);//halfway between red and green.
//same with other demonstrations.
Edit:
Here, start and end refer to a starting and ending sample. The normalizedValue is some value between [0, 1] inclusive (that means it can equal exactly 0 or 1, or any value in between 0 and 1. That's what the term normalized means typically.)
So, for you, start and end will be two colors, and normalizedValue will represent how near you are to the starting or ending color.
Take linearInterpolation for example.
red = 1;
green = 2;
float midway = 1 + (2 - 1) * .5;
//midway = 1.5, which is halfway between red and green.
float allRed = 1 + (2 - 1) * 0;
//allRed = 1, which is the value of red (or start)
float allGreen = 1 + (2 - 1) * 1;
//allGreen = 2, which is the value of green (or end)
So, for linear interpolation, the closer the normalizedValue is to 1, the nearer the returned value it is to end. The closer normalizedValue is to 0, the closer the returned value is to start.
This isn't necessarily true for other interpolation functions. You can think linear interpolation as a simple line segment connecting values. Want a value halfway between those segments? Use a normalized value of .5, viola!
Other functions might have steeper slopes, or even oscillate between start and end!
Try and stop thinking in terms of color, and start thinking more abstractly. Colors are a certain distance apart. Interpolation helps you define what values lie in the distance between them.
Since the float values are in a set, you know how many there are and can calculate a color interval. You can then iterate over them, assigning colors and incrementing by the color interval.
Edit: The downside of this approach is that the same float value will not map to the same color when the number of values changes.
I suggest a logarithmic scale. If you use base 10 logs, the range will be from -39 to +39.
Depending on your distribution, a double or triple log can be better. I made a very quick test, and for the sample { 1.00, 1.20, 1.10, 1.05, 1.15, 9.70, 1.20, 2.00, 1.01, 1.03, 1.16, 1.02, 9.00, 1.20, 1.10, 1.50, 1.05, 1.15, 2.00, 3.00 }, function
int f(float x) {
return (int)(Math.log(Math.log(x)*100+1)*2.5) ;
}
Produces the following distribution:
f(x) color count
0 blue 4
1 green 4
2 yellow 6
3 orange 3
4 red 3
Not bad for 5 minutes of work. However, if you post a reasonable sample of numbers (say 100), a distribution graph, or, much better, a distribution histogram, we could help you better. The trick is to find the distribution function of the data. From that function it is very easy to come with a second function that makes the distribution uniform ("flat").
A second alternative in your case (which is relatively simple as you want to use just a few colors), is to use scaleSteps of different "width".
if( value < greenMin ) color= blue ;
else if( value < yellowMin ) color= green ;
else if( value < orangeMin ) color= yellow ;
else if( value < redMin ) color = orange ;
else color= red ;
I took the liberty of condensing the code a bit. Let me know if it's not clear. You need to determine the values of greenMin, yellowMin, orangeMin, and redMin, of course. For that, grab a big, representative data sample, sort it, and divide it in 5 groups of equal size. The first value of the second group is greenMin, first value of the third is yellowMin, and so on. You can use an office spreadsheet program to do this, as it's a one-time activity.
I am building an android application. In my app, i need to be able to round up a double (42.42 for example) and also get how much i added to the original number in order to round it up. My current code isn't working, and its outputting 0.. Anyway to fix this?
My current code:
float rounded = FloatMath.ceil(val);
double getDecimal = (val - FloatMath.floor(val))*100;
int noDecimal = (int) ((int) 100-getDecimal);
float toadd = (noDecimal/100);
In my code the "rounded" variable is the simpel rounding, and "toadd" should be how much i added to it. For some reason toadd always comes back as 0. Any help?
You're dividing noDecimal by 100. Both are ints, and the result will always be an int. In this case, it's an int between 0 and 1, which will always be truncated to 0.
What's wrong with just getting the number modulo 1 (%1), then getting the ceiling of the original number?
For completeness, you could simply change the last line to preserve the rest of the logic:
float toadd = noDecimal/100.0;
This changes the divisor to a float, and an int divided by a float yields a float.
float toadd = (noDecimal/100);
This will give you 0, as you are dividing smaller integer by larger one..
Try to do like this: -
float toadd = (Float.valueOf(noDecimal)/100);
Also, you don't need to do typecast twice in the below code: -
int noDecimal = (int) ((int) 100-getDecimal);
Just, outer cast is enough: -
int noDecimal = (int) (100-getDecimal);
Edit: - Also, you might want to use BigDecimal for this kind of problems..
Maybe I'm missing something or not getting your intention right, but if you just want to know what you added, why don't just use the difference?
float rounded = FloatMath.ceil(val);
float toadd = rounded-val;
Edit: As mentioned in the comments, this might not always give the absolutely accurate result. But it's the general idea which can be used with BigDecimal, which offers a higher precision.
How can I find similar coloured pixels using colour objects? I know to see if two colors are equal you can use:
a.equals(b);
where a and b are colour objects, but what if I want to find similar shades of blue for example?
Comparing colours is not a trivial problem, there are a variety of different metrics. I can't find a library (easily) that does it but if you have a thorough search I'm sure there'll be something out there Have a look at this class. In the meantime do some reading!
When it comes to programmatically tweaking color values you have lots of options. A very easy solution would be to simply offset the channel values of the color object randomly. Think of it as mutating colors - just take the color you'd like to mutate and generate a few more colors from it:
Color mutateColor(int range){
int r = a.getRed() + (int)(Math.random() * 2 * range - range);
r = Math.min(255, Math.max(0, r));
int g = a.getGreen() + (int)(Math.random() * 2 * range - range);
g = Math.min(255, Math.max(0, g));
int b = a.getBlue() + (int)(Math.random() * 2 * range - range);
b = Math.min(255, Math.max(0, b));
return new Color(r, g, b);
}
This is the simplest example, a range is given and each channel is offset by that same range, resulting in something like this:
This was done with a range value of 10. For added control you could add three arguments to the mutateColor function (offsets for each individual channel). You could also take one range but alter it based on the values already in the channel. For instance:
range = 0.25
red = 100
green = 10
blue = 0
redRange = 100 + rand(-25, 25)
greenRange = 10 + rand(-2.5, 2.5);
etc...
That's just one of many other possibilities.
If you were looking to compare two colors with a tolerance, I ported the code from fredley's link and it works nicely for getting the difference between two colors:
double colorDist(Color e1, Color e2){
long rmean = ( (long)e1.getRed() + (long)e2.getRed() ) / 2;
long r = (long)e1.getRed() - (long)e2.getRed();
long g = (long)e1.getGreen() - (long)e2.getGreen();
long b = (long)e1.getBlue() - (long)e2.getBlue();
return Math.sqrt((((512+rmean)*r*r)>>8) + 4*g*g + (((767-rmean)*b*b)>>8));
}
what if I want to find similar shades of blue for example?
I have no idea what a technical definition for this might mean. However you might be able to use HSL Color to create your own definition.
Basically, I guess you could start by making sure the the Hue of each color is +/- a fixed number of degrees. Then you could narrow it down further by checking if the Saturation and/or Luminosity is within your desired range.