How can I pass an array as three arguments to a function in Java? (Forgive me, I'm very new to Java).
I have the following function which takes float r, float g, float b, float a as arguments.
renderer.prepare(r, g, b, 1);
And I want to pass the output from this function in. (Or figure out how to return three separate unpacked floats).
public static float[] rgbToFloat(int r, int g, int b) {
return new float[] {(float) r / 255f, (float) g / 255f, (float) b / 255f};
}
How can I do this? In some other languages it would look something like this:
renderer.prepare(...rgbToFloat(25, 60, 245), 1);
This is a typical example of an "X-Y Problem". Your original quest was to somehow group the 3 different parameters that you want to pass to a function. That's "problem X". Then you came up with the idea of using an array for this, but you were still unsure how to go about it, so you posted this question asking how to best use an array to achieve what you want. But that's "problem Y", not "problem X".
Using an array may and may not be the right way of solving "problem X". (Spoiler: it isn't.)
Honoring the principle of the least surprise, the best way of solving your problem "X" in my opinion is by declaring a new class, FloatRgba which encapsulates the four floats in individual float members: final float r; final float g; final float b; final float a;.
So, then your rgbToFloat() method does not have to return an unidentified array of float, instead it becomes a static factory method of FloatRgba:
public static FloatRgba fromIntRgb( int r, int g, int b )
{
return new FloatRgba( r / 255f, g / 255f, b / 255f, 1.0f );
}
Finally, you introduce a utility function prepareRenderer which accepts a Renderer and a FloatRgba and invokes renderer.prepare(float, float, float, float) passing it the individual members of FloatRgba.
This way you keep everything clear, self-documenting, and strongly typed. Yes, it is a bit inconveniently verbose, but that's java for you.
Maybe late for the original question, might help late readers stumbling over here (like me), though: I rather recommend converting just one single parameter to float:
public static float rgbToFloat(int value)
{
return value / 255.0f;
// don't need the cast, value will be promoted to float anyway
// as second parameter is already
}
Call now gets to
renderer.prepare(rgbToFloat(25), rgbToFloat(60), rgbToFloat(245), 1);
Sure, the draw back is that you have to call it three times now (as the other way round, you would have had to store the array in a temporary as shown in the comments, you wouldn't, in comparison, have gained much either), in the end, you gain flexibility for and additionally avoid the temporary array object when none is needed.
If you still insist on the array, you'll need a temporary
float[] rgb = rgbToFloat(r, g, b);
renderer.prepare(rgb[0], rgb[1], rgb[2], 1.0f);
But then I wonder why you don't consider alpha as well:
public static float[] rgbToFloat(int r, int g, int b)
{
return rgbToFloat(r, g, b, 255);
}
public static float[] rgbToFloat(int r, int g, int b, int a)
{
return new float[] { r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f };
}
Related
I'm trying to convert some C++ code into Java. As there is no default Vector3 datatype in java, I have created my own custom Vector3 class. However, I saw that it is possible to do this: 1/VECTOR3 in C++. This had me sort of confused as a Vector3 has 3 values within it. So I was wondering could someone explain to me what is actually happening to each of the 3 values when 1/VECTOR3 is used?
The code I'm trying to convert is from here, at the bottom of the page:
https://www.scratchapixel.com/lessons/3d-basic-rendering/minimal-ray-tracer-rendering-simple-shapes/ray-box-intersection
class Ray { public:
Ray(const Vec3f &orig, const Vec3f &dir) : orig(orig), dir(dir)
{
invdir = 1 / dir;
sign[0] = (invdir.x < 0);
sign[1] = (invdir.y < 0);
sign[2] = (invdir.z < 0);
}
Vec3 orig, dir; // ray orig and dir
Vec3 invdir;
int sign[3]; };
First of all there is no Vec3 in C++. If you read that page carefully, its all their types. The text is a bit sloppy. They call it "inverse of the ray-direction" but for a vector I am not aware of a commonly used defintion for "inverse". Anyhow...
You just need to scroll to the end of the page, follow the link to the complete example, then look at geometry.h. There is the definition of that division:
friend Vec3 operator / (const T &r, const Vec3 &v)
{ return Vec3<T>(r / v.x, r / v.y, r / v.z); }
(T is the element type)
They define division of a scalar by a vector to be element-wise division.
Thanks to User: idclev 463035818. I found out that for this specific paper essentially this operation was a custom operation made in c# (because apparently, c# is just that overpowered). When you have a number, that we'll call n divided by vector3 that we'll call VECTOR3. Essentially, each this operation returns the following as a vector3 (n / VECTOR3.x, n / VECTOR3.y, n / VECTOR3.z)
I have looked around but all conversions have used more than one line and variables. I am trying to paint objects in a certain co-ordinates times a double that changes when you change the size of the frame.
Width = getWidth();
Height = getHeight();
cWidth = 1900/Width;
cHeight = 1030/Height;
Inside the paint class,
g.fillOval (PlayerX/cWidth, PlayerY/cHeight, 50/cWidth, 50/cHeight);
but I get the error:
The method fillOval(int, int, int, int) in the type Graphics is not applicable for the arguments (double, double, double, double).
Would I have to make a separate variable for all 4 for every object painted or is there an easier way?
You have to cast them to int.
g.fillOval ((int)(PlayerX/cWidth), (int)(PlayerY/cHeight), (int)(50/cWidth), (int)(50/cHeight);
If your dont have a method like:
fillOval(double a, double b, double c, double d)
you can not do
g.fillOval (PlayerX/cWidth, PlayerY/cHeight, 50/cWidth, 50/cHeight);
you need to pass integers not doubles, if loosing the presision is not a problem then try casting
g.fillOval ((int)(PlayerX/cWidth), (int)(PlayerY/cHeight), (int)(50/cWidth), (int)(50/cHeight));
Note:
you need to cast carefully grouping the result.
the reason is:
PlayerX is a double, same as cWidth,
so doing :
(int)PlayerX/cWidth
will not work, since the result is the same as int/double → double
PlayerX/(int)cWidth
will not work either, since the result is the same as doublet/int → double
the option that will work is
(int)(PlayerX/cWidth)
So I just started learning Java yesterday coming from a different language, and I am reading through my textbook and finding it to be pretty nice so far. However I did an exercise that basically required me to create a new Object use Rectangle and find the area. Below is the working code I came up with.
Now coming from other programming languages I was just toying around with this and did int area,width,height;and then it gave me an error saying that I had to use double in order to utilize .getWidth();, .getHeight(). I couldn't find anything in my book telling me why I had to make this a double and I started looking online and found this link
Now I found some documentation online where It told me to use double as well, but I'm not really sure why would I need to set these as doubles. Is it because the people who made Java, knew that precision is needed when we are working with coordinates and doing math with widths, heights and coordinates? My book says that it takes more memory to make a double variable rather than an int ( I come from doing lots of javascript and PHP, so reading on what a float and double does was something good for me).
I.E. Why do I need to make my area,height,width variable doubles in order to use .getWidth,.getHeight
package keepo;
import java.awt.Rectangle;
public class tuna{
public static void main(String [] args){
Rectangle rect = new Rectangle(10,20,50,40);
double area,width,height;
width = rect.getWidth();
height = rect.getHeight();
area = width * height;
System.out.println("Width is : " + width + "Height is : " + height);
System.out.println("Area is : " + area);
}
}
It is because this is how these methods have been defined in the java api. As you can see under the modifier and type column that the methods getWidth(), getHeight() all return value of type double.
Because in this case, you should not use those methods. The AWT class Rectangle does store coordinates as ints. You can easily read them back as ints if that's what you want to do, by accessing the fields instead of calling the getter methods:
int area, width, height;
width = rect.width; // not getWidth()
height = rect.height; // not getHeight()
area = width * height;
The getWidth() and getHeight() methods serve zero purpose here, as they will always return the same value as the fields, except as a different type (and you can already assign any int value to a double anyway, when a double is what you want to use).
So why do those two methods (along with getX() and getY()) exist at all? Because in Java 1.2 the geometry stuff in the API was expanded. People wanted to be able to work with floating-point coordinates, which Rectangle cannot do. And the Java maintainers couldn't change the fields of Rectangle from int to double because that would break backwards compatibility with how old code was already using it. So two new classes, Rectangle2D.Float and Rectangle2D.Double were added, which store coordinates as floats and doubles respectively.
But what if you want to work generically with any rectangle, without writing separate code for all the rectangle flavors? A new abstract class, Rectangle2D was also added, as the superclass of the three rectangle classes. This class is abstract (meaning it cannot be created on its own, as it is incomplete) and it does not store any coordinates itself. It does however, specify a contract that its subclasses follow (meaning that any Rectangle2D method is available in all three of its implementations). That includes the getWidth() and getHeight() methods that return doubles, regardless of the actual storage type of the particular rectangle.
Taking the abstraction an extra, perhaps superfluous, level, they also added RectangularShape as the superclass of several shapes with rectangular bounds: Rectangle2D, RoundRectangle2D, Ellipse2D and Arc2D. That is the class that actually declares the getWidth() and getHeight() methods, which all RectangularShape subclasses must provide:
// What's this shape? A rectangle? An ellipse? Does it use ints? floats? doubles?
RectangularShape something = ......;
// We don't care!
System.out.println("The shape (whatever it is) occupies an area of:");
System.out.println(something.getWidth() + " × " + something.getHeight());
So you can call those getter methods on any rectangle (or "rectangular shape") to get its coordinates, but if you know you have a particular shape class, you can/should access its fields directly, as that is simpler, and it gives you the values without converting them to a different type.
P.S. It is a similar story with Point, which uses int coordinates, but provides double getX() and double getY() methods, because of the later-added classes Point2D.Float, and Point2D.Double, and the abstract superclass Point2D.
P.P.S. There is actually a small advantage to using double (or long) for your rectangle's area, even if your rectangle coordinates are ints. Large multiplications could overflow the 32-bit range of an int, producing the wrong result. If you convert at least one of the values to a larger type, it will cause the multiplication to be done in that larger type, which you can then safely store without overflow:
Rectangle big = new Rectangle(0, 0, 1000000, 1000000);
int area = big.width * big.height;
long bigArea = (long)big.width * big.height;
System.out.println(area); // -727379968 (uh oh!)
System.out.println(bigArea); // 1000000000000
Imran Ali is right.
This is java documentations for getHeight() and for getWidth() it's same.
java.awt.Rectangle
public double getHeight()
Returns the height of the bounding Rectangle in double precisionReturns:
the height of the bounding Rectangle.
But if you want/need to use int instead of double, use following codes for height and repeat them for width too:
using getSize() method which returns rectangle dimension then use it's fields (width and height)
int height = rect.getSize().height;
using data type casting
int height = (int) rect.getHeight();
int height = (int) rect.getSize().getHeight();
The Rectangle.getWidth() and Rectangle.getHeight()methods both return their values with double precision, as stated by others. It is easier if you just keep using them, in order to prevent the Rectangle's values from being changed on accident, by simply casting the value to an int:
int width = (int)rect.getWidth()
and int height = (int)rect.getHeight()
I'm trying to wrap my head around three-dimensional arrays. I understand that they are arrays of two-dimensional arrays, but the book I'm reading said something that confuses me.
In an exercise for the book I'm reading, it asks me to make a three-dimensional array for a full-color image. It gives a small example saying this:
If we decide to choose a three-dimensional array, here's how the array might be declared:
int[][][] colorImage = new int[numRows][numColumns][3];
However, wouldn't it be more effective like this?
int[][][] colorImage = new int[3][numRows][numColumns];
Where 3 is the rgb values, 0 being red, 1 being green, and 2 being blue. With the latter, each two-dimensional array would be storing the color value of the row and column, right? I just want to make sure I understand how to effectively use a three-dimensional array.
Any help will be greatly appreciated, thanks.
Order doesn't matter, and in fact the former form is more readable:
final const int RED = 0;
final const int GREEN = 1;
final const int BLUE = 2;
int[][][] colorImage = new int[numRows][numColumns][3];
//...
int x = getSomeX();
int y = getSomeY();
int redComponent = colorImage[x][y][RED];
int greenComponent = colorImage[x][y][GREEN];
int blueComponent = colorImage[x][y][BLUE];
The order shouldn't matter, so one isn't more effective than the other. The only thing that matters is that whatever accesses colorImage knows which dimension is used for what. Bit more context on multidimensional arrays here.
I'm not sure if its a good idea to put everything in an 3dimensional array of int.
Your first mistake is the dataytpe:
RGB is a int.
But R is a byte, G is a byte, B is a byte too.. (Color.getXXX() delivers an int, I dont know why because its a byte 0-255)
You need an int because you want to address more than 256 cols&rows. (Thats okay).
But i think that its much better to encapsulate the color information in a extra object. Perhaps a private datastructure like
class MyColor {
public byte r, g, b; //public for efficient access;
public int color; //public for efficient access;
public MyColor(final int rgb) {
this(new Color(rgb));
}
public MyColor(final Color c) {
this((byte) c.getRed(), (byte) c.getGreen(), (byte) c.getBlue(), c.getRGB());
}
public MyColor(final byte red, final byte green, final byte blue, final int c) {
this.r = red;
this.g = green;
this.b = blue;
this.color = c;
}
}
and put this in an 2dim array of MyColor[numRows][numColumns]
But if you make the class MyColor public to your whole app - i would change the design of the class to be more secure.
I've been trying for the past few days to make a working implementation of a virtual trackball for the user interface for a 3D graphing-like program. But I'm having trouble.
Looking at the numbers and many tests the problems seems to be the actual concatenation of my quaternions but I don't know or think so. I've never worked with quaternions or virtual trackballs before, this is all new to me. I'm using the Quaternion class supplied by JOGL. I tried making my own and it worked (or at least as far a I know) but it was a complete mess so I just went with JOGL's.
When I do not concatenate the quaternions the slight rotations I see seem to be what I want, but of course It's hard when it's only moving a little bit in any direction. This code is based off of the Trackball Tutorial on the OpenGL wiki.
When I use the Quaternion class's mult (Quaternion q) method the graph hardly moves (even less than not trying to concatenate the quaternions).
When I tried Quaternionclass'sadd (Quaternion q)` method for the fun of it I get something that at the very least rotates the graph but not in any coherent way. It spazzes out and rotates randomly as I move the mouse. Occasionally I'll get quaternions entirely filled with NaN.
In my code I will not show either of these, I'm lost with what to do with my quaternions. I know I want to multiply them because as far as I'm aware that's how they are concatenated. But like I said I've had no success, I'm assuming the screw up is somewhere else in my code.
Anyway, my setup has a Trackball class with a public Point3f projectMouse (int x, int y) method and a public void rotateFor (Point3f p1, Point3f p2), Where Point3f is a class I made. Another class called Camera has a public void transform (GLAutoDrawable g) method which will call OpenGL methods to rotate based on the trackball's quaternion.
Here's the code:
public Point3f projectMouse (int x, int y)
{
int off = Screen.WIDTH / 2; // Half the width of the GLCanvas
x = x - objx_ - off; // obj being the 2D center of the graph
y = off - objy_ - y;
float t = Util.sq(x) + Util.sq(y); // Util is a class I made with
float rsq = Util.sq(off); // simple some math stuff
// off is also the radius of the sphere
float z;
if (t >= rsq)
z = (rsq / 2.0F) / Util.sqrt(t);
else
z = Util.sqrt(rsq - t);
Point3f result = new Point3f (x, y, z);
return result;
}
Here's the rotation method:
public void rotateFor (Point3f p1, Point3f p2)
{
// Vector3f is a class I made, I already know it works
// all methods in Vector3f modify the object's numbers
// and return the new modify instance of itself
Vector3f v1 = new Vector3f(p1.x, p1.y, p1.z).normalize();
Vector3f v2 = new Vector3f(p2.x, p2.y, p2.z).normalize();
Vector3f n = v1.copy().cross(v2);
float theta = (float) Math.acos(v1.dot(v2));
float real = (float) Math.cos(theta / 2.0F);
n.multiply((float) Math.sin(theta / 2.0F));
Quaternion q = new Quaternion(real, n.x, n.y, n.z);
rotation = q; // A member that can be accessed by a getter
// Do magic on the quaternion
}
EDIT:
I'm getting closer, I found out a few simple mistakes.
1: The JOGL implementation treats W as the real number, not X, I was using X for real
2: I was not starting with the quaternion 1 + 0i + 0j + 0k
3: I was not converting the quaternion into an axis/angle for opengl
4: I was not converting the angle into degrees for opengl
Also as Markus pointed out I was not normalizing the normal, when I did I couldn't see much change, thought it's hard to tell, he's right though.
The problem now is when I do the whole thing the graph shakes with a fierceness like you would never believe. It (kinda) moves in the direction you want it to, but the seizures are too fierce to make anything out of it.
Here's my new code with a few name changes:
public void rotate (Vector3f v1, Vector3f v2)
{
Vector3f v1p = v1.copy().normalize();
Vector3f v2p = v2.copy().normalize();
Vector3f n = v1p.copy().cross(v2p);
if (n.length() == 0) return; // Sometimes v1p equals v2p
float w = (float) Math.acos(v1p.dot(v2p));
n.normalize().multiply((float) Math.sin(w / 2.0F));
w = (float) Math.cos(w / 2.0F);
Quaternion q = new Quaternion(n.x, n.y, n.z, w);
q.mult(rot);
rot_ = q;
}
Here's the OpenGL code:
Vector3f p1 = tb_.project(x1, y1); // projectMouse [changed name]
Vector3f p2 = tb_.project(x2, y2);
tb_.rotate (p1, p2);
float[] q = tb_.getRotation().toAxis(); // Converts to angle/axis
gl.glRotatef((float)Math.toDegrees(q[0]), q[1], q[2], q[3]);
The reason for the name changes is because I deleted everything in the Trackball class and started over. Probably not the greatest idea, but oh well.
EDIT2:
I can say with pretty good certainty that there is nothing wrong with projecting onto the sphere.
I can also say that as far as the whole thing goes it seems to be the VECTOR that is the problem. The angle looks just fine, but the vector seems to jump around.
EDIT3:
The problem is the multiplication of the two quaternions, I can confirm that everything else works as expected. Something goes whacky with the axis during multiplication!
The problem is the multiplication of the two quaternions, I can confirm that everything else works as expected. Something goes whacky with the axis during multiplication!
You are absolutely correct!! I just recently submitted a correct multiplication and Jogamp has accepted my change. They had incorrect multiplication on mult(quaternion).
I am sure if you get the latest jogl release, it'll have the correct mult(Quaternion)
I did it!
Thanks to this C++ implementation I was able to develop a working trackball/arcball interface. My goodness me, I'm still not certain what the problem was, but I rewrote everything and even wrote my own Quaternions class and suddenly the whole thing works. I also made a Vectors class for vectors. I had a Vector3f class before but the Quaternions and Vectors classes are full of static methods and take in arrays. To make it easy to do vector computations on quaternions and vice versa. I will link the code for those two classes below, but only the Trackball class will be show here.
I made those two classes pretty quickly this morning so if there are any mathematical errors, well, uh, oops. I only used what I needed to use and made sure they were correct. These classes are below:
Quaternions: http://pastebin.com/raxS4Ma9
Vectors: http://pastebin.com/fU3PKZB9
Here is my Trackball class:
public class Trackball
{
private static final float RADIUS_ = Screen.DFLT_WIDTH / 2.0F;
private static final int REFRESH_ = 50;
private static final float SQRT2_ = (float) Math.sqrt(2);
private static final float SQRT2_INVERSE_ = 1.0F / SQRT2_;
private int count_;
private int objx_, objy_;
private float[] v1_, v2_;
private float[] rot_;
public Trackball ()
{
v1_ = new float[4];
v2_ = new float[4];
rot_ = new float[] {0, 0, 0, 1};
}
public void click (int x, int y)
{
v1_ = project(x, y);
}
public void drag (int x, int y)
{
v2_ = project(x, y);
if (Arrays.equals(v1_, v2_)) return;
float[] n = Vectors.cross(v2_, v1_, null);
float[] o = Vectors.sub(v1_, v2_, null);
float dt = Vectors.len(o) / (2.0F * RADIUS_);
dt = dt > 1.0F ? 1.0F : dt < -1.0F ? -1.0F : dt;
float a = 2.0F * (float) Math.asin(dt);
Vectors.norm_r(n);
Vectors.mul_r(n, (float) Math.sin(a / 2.0F));
if (count_++ == REFRESH_) { count_ = 0; Quaternions.norm_r(rot_); }
float[] q = Arrays.copyOf(n, 4);
q[3] = (float) Math.cos(a / 2.0F);
rot_ = Quaternions.mul(q, rot_, rot_);
}
public float[] getAxis ()
{
return Quaternions.axis(rot_, null);
}
public float[] project (float x, float y)
{
x = RADIUS_ - objx_ - x;
y = y - objy_ - RADIUS_;
float[] v = new float[] {x, y, 0, 0};
float len = Vectors.len(v);
float tr = RADIUS_ * SQRT2_INVERSE_;
if (len < tr)
v[2] = (float) Math.sqrt(RADIUS_ * RADIUS_ - len * len);
else
v[2] = tr * tr / len;
return v;
}
}
You can see there's a lot of similarities from the C++ example. Also I'd like to note there is no method for setting the objx_ and objy_ values yet. Those are for setting the center of the graph which can be moved around. Just saying, so you don't scratch your head about those fields.
The cross-product of two normalized vectors is not normalized itself. It's length is sin(theta). Try this instead:
n = n.normalize().multiply((float) Math.sin(theta / 2.0F));