I am currently working on a 3D-library including quaternions. As every expert, I coded my methods based on what I found on Wikipedia and it doesn't work quite right.
This is the method for calculating the product between two quaternions:
public static Quaternion product(Quaternion a, Quaternion b) {
return a.clone().multiply(b);
}
And here is its implementation:
public Quaternion multiply(Quaternion q) {
float rx = q.getX(), ry = q.getY(), rz = q.getZ(), rw = q.getW();
this.w = w*rw - x*rx - y*ry - z*rz;
this.x = w*rx + x*rw + y*rz - z*ry;
this.y = w*ry - x*rz + y*rw + z*rx;
this.z = w*rz + x*ry - y*rx + z*rw;
return this;
}
And here is a small test that I wrote:
Quaternion a = Quaternion.create(1, 1, 1, Spatium.DEG_TO_RAD * 15);
Quaternion b = Quaternion.create(1, 1, 1, Spatium.DEG_TO_RAD * 15);
Quaternion c = Quaternion.product(a, b);
System.out.println(a + " * " + b + " = " + c);
Note that the create() method initializes quaternions with x, y, z, w.
Unfortunately, the tests yields results that don't make any sense despite my amazing skills of copying and pasting Wikipedia formulas:
(0.2617994 + 1.0i + 1.0j + 1.0k) * (0.2617994 + 1.0i + 1.0j + 1.0k) = (-2.931461 + -2.6696618i + 1.0j + -6.3393235k)
For comparison, Wolfram solves it correctly and returns -2.93146 + 0.523599i + 0.523599j + 0.523599k.
Here's the formulas I so skillfully copied from Wikipedia:
I don't really have anything to say other than please help me, Wikipedia couldn't.
I think you should store the current values of x, y, z and w in a temp variable before updating it with new values.
public Quaternion multiply(Quaternion q) {
float rx = q.getX(), ry = q.getY(), rz = q.getZ(), rw = q.getW();
float cx = x; // cx = current X
float cy = y;
float cz = z;
float cw = w;
this.w = cw*rw - cx*rx - cy*ry - cz*rz;
this.x = cw*rx + cx*rw + cy*rz - cz*ry;
this.y = cw*ry - cx*rz + cy*rw + cz*rx;
this.z = cw*rz + cx*ry - cy*rx + cz*rw;
return this;
}
In your code, when you use this.w = w*rw - x*rx - y*ry - z*rz;, you are overridind the current value of w, and the next 3 operations will be affected by this changes.
Related
I am working on a desktop app that will help students with algebra. I have a class called Vector2 that just stores two variables, x and y. I am working on a method that will return the endpoint of a line given an endpoint and a midpoint. Just to show how I have been doing this here is a method
public static Vector2 midpoint(double xa, double ya, double xb, double yb){
Vector2 v = new Vector2(0, 0);
v.x = (xa + xb) / 2;
v.y = (ya + yb) / 2;
return v;
}
Given this how would I make a method that will give me the other endpoint?
Example: if the given endpoint is (-3, -5) and the given midpoint is (-6, -2) then the output should be (-9, 1)
EDIT FOR NEW PPL: I have the answer, it is pretty simple. Here is the final method
public static Vector2 otherEndpoint(double endPointX, double EndPointY, double midPointX, double midPointY){
Vector2 v = new Vector2(0,0);
v.x = (endPointX + midPointX) / 2;
v.y = (EndPointY + midPointY) / 2;
v.multiply(2);
return v;
}
v.multiply() is a method that I made that will multiply each point by whatever number you put in
public final class Point {
private final int x;
private final int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
public static Point getMidpoint(Point one, Point two) {
int x = (one.x + two.x) / 2;
int y = (one.y + two.y) / 2;
return new Point(x, y);
}
public static Point getEndpoint(Point one, Point mid) {
int x = (2 * mid.x) - one.x;
int y = (2 * mid.y) - one.y;
return new Point(x, y);
}
public static Vector2 fromEndAndMidpoint(double xa, double ya, double xmid, double ymid) {
Vector2 v = new Vector2();
v.x = 2 * xmid - xa;
v.y = 2 * ymid - ya;
return v;
}
Here is the method that shows how to compute it.
Note that this works because you know that one point is in the middle. So the desired endpoint is just one step away from the middle or two steps away from the other end.
public static Vector2 midpoint(double xe, double ye, double xm,
double ym) {
// calculate the step to go from the end point to the middle
// for both x and y.
double xstep = xm - xe;
double ystep = ym - ye;
// then just add the step to the middle to get the end point.
return new Vector2(xm + xstep, ym + ystep);
}
Note that the above will not work to find any point. For that you must simply find the equation of the line y = mx + b by computing the slope, m, and y-intercept, b.
In an STL format this is what my current landscape looks like. And this is what the landscape is suppossed to look like. I think I know what the problem is, but I have no clue how to solve it.
I think I need to set the Z coordinate relative to other points around it so the whole landscape's Z coordinate isn't between 0 and 1 but they rather "add up".
Don't want a solution, just a hint in the right direction.
import java.io.*;
import java.util.Random;
class Point3D {
double x, y, z;
Point3D(double dx, double dy, double dz) {
x = dx;
y = dy;
z = dz;
}
Point3D middlePoint(Point3D p) {
Point3D m = new Point3D(0.0, 0.0, 0.0);
m.x = (this.x + p.x) / 2.0;
m.y = (this.y + p.y) / 2.0;
m.z = (this.z + p.z) / 2.0;
return m;
}
}
public class Aufgabe3 {
public static void recursion(Point3D p1, Point3D p2, Point3D p3, int n) {
if (n > 0) {
if (n == 1) {
System.out.println(" facet normal 0.0 0.0 0.0");
System.out.println(" outer loop");
System.out.println(" vertex " + p1.x + " " + p1.y + " " + p1.z);
System.out.println(" vertex " + p2.x + " " + p2.y + " " + p2.z);
System.out.println(" vertex " + p3.x + " " + p3.y + " " + p3.z);
System.out.println(" endloop");
System.out.println(" endfacet");
}
Random r = new Random();
Point3D a = p1.middlePoint(p2);
Point3D b = p3.middlePoint(p1);
Point3D c = p2.middlePoint(p3);
long seedA = (long) ((p1.x + p1.y + p1.z + p2.x + p2.y + p2.z) * 1000000);
r.setSeed(seedA);
a.z = r.nextDouble() / 10;
long seedB = (long) ((p3.x + p3.y + p3.z + p1.x + p1.y + p1.z) * 1000000);
r.setSeed(seedB);
b.z = r.nextDouble() / 10;
long seedC = (long) ((p2.x + p2.y + p2.z + p3.x + p3.y + p3.z) * 1000000);
r.setSeed(seedC);
c.z = r.nextDouble() / 10;
recursion(p1, a, b, n-1);
recursion(a, p2, c, n-1);
recursion(b, c, p3, n-1);
recursion(a, b, c, n-1);
}
}
public static void main(String args[]) throws FileNotFoundException {
int n;
try {
n = Integer.parseInt(args[0]);
}
catch (Exception e) {
n = 7;
}
System.out.println("Aufgabe 3: Landschaftsgenerator");
System.out.println("n = " + n);
Random r = new Random();
Point3D p1 = new Point3D(0.8, -1.2, 0.0);
Point3D p2 = new Point3D(1.0, 1.3, 0.0);
Point3D p3 = new Point3D(-1.0, 0.0, 0.0);
System.setOut(new PrintStream(new FileOutputStream("Aufgabe3.stl")));
System.out.println("solid Aufgabe3");
recursion(p1, p2, p3, n);
System.out.println("endsolid");
}
}
The problem is the frequency distribution of the displacements you're adding. Displacements to a fractal landscape have to follow a 1/(f^b) distribution, otherwise you get random noise.
In this case, no matter what the scale of subdivision, you're adding the same vertical displacement, which is going to result in a landscape dominated by the highest frequency. Formally, a fractal surface is one that has a 'fractional' or 'fractal' geometric dimension, higher than the topological dimension of the surface, but lower than that of the embedding space. For instance, for a 2D surface being displaced in the 3rd dimension, the fractal dimension should be between 2 and 3.
For subdivision and displacement, the fractal dimension is related to beta as follows:
Dim = (7 - b)/2
With fractal behaviour therefore occurring between b = 1 and b = 3, and the random displacements follow this profile:
displacement = k * rand / (f^b)
This means that if you divide your triangle in half each time, you have to at least halve the displacement, or you'll end up with a noise surface rather than a fractal one. The best choice for a landscape is typically somewhere around b = 2.
Reference: https://fractal-landscapes.co.uk/maths.html
I have recently been playing around with Bicubic Interpolation, as I am wanting to generate the earth, based on real heightmaps inside Minecraft. The reason I am using the interpolation, is because I would like to make the world have more detail. After a lot of research, and a lot of trial and error, I decided to come ask here. :)
Because of limited memory, I can't scale the image on startup, and keep that loaded, I have to do the interpolation on the fly.
I seem to have gotten Cubic Interpolation to work, as seen here:
Visualisation of the interpolation
However, I can not get Bicubic Interpolation to work. For testing purposes, I am using a small image, and scaling it by 4. This is what the code does: Input -> Output
This is my current code:
public static double cubicInterpolate(double[] points, double x, double scale)
{
x /= scale;
double inBetweenPoint = x;
int xInHeightmap = (int) x;
inBetweenPoint -= xInHeightmap;
double beforePoint1 = safe(points, xInHeightmap - 1);
double point1 = safe(points, xInHeightmap);
double point2 = safe(points, xInHeightmap + 1);
double afterPoint2 = safe(points, xInHeightmap + 2);
double p = (afterPoint2 - point2) - (beforePoint1 - point1);
double q = (beforePoint1 - point1) - p;
double r = point2 - beforePoint1;
double s = point1;
return (p * Math.pow(inBetweenPoint, 3)) + (q * Math.pow(inBetweenPoint, 2)) + (r * inBetweenPoint) + s;
}
public static double bicubicInterpolate(double[][] points, double x, double y, double scale)
{
x /= scale;
double inBetweenPoint = x;
int xInHeightmap = (int) x;
inBetweenPoint -= xInHeightmap;
double beforePoint1 = cubicInterpolate(safe(points, xInHeightmap - 1), y, scale);
double point1 = cubicInterpolate(safe(points, xInHeightmap), y, scale);
double point2 = cubicInterpolate(safe(points, xInHeightmap + 1), y, scale);
double afterPoint2 = cubicInterpolate(safe(points, xInHeightmap + 2), y, scale);
return cubicInterpolate(new double[]{beforePoint1, point1, point2, afterPoint2}, inBetweenPoint + 1, scale);
}
public static double[] safe(double[][] p, int i)
{
return p[Math.max(0, Math.min(i, p.length - 1))];
}
public static double safe(double[] p, int i)
{
return p[Math.max(0, Math.min(i, p.length - 1))];
}
Thank you for your help :)
To my understanding, your implementaion treats the given x and y coordinates in a totally different way, which does not lead to the desired result. You should basically do the following.
First, you need to identify the four points (coordinate pairs) in the grid which will form the basis of the interpolation, as well as the distances in both directions from the grid to to the interpolation. This can be done as follows.
int xfloor = (int)x;
int yfloor = (int)y;
int xdelta = x - (double)xfloor;
int ydelta = y - (double)yfloor;
The desired coordinate pairs are then (depending on the orientation of the axes)
P1 = (xfloor, yfloor ) // left upper corner
P2 = (xfloor, yfloor + 1) // left lower corner
P3 = (xfloor + 1 ,yfloor + 1) // right lower corner
P4 = (xfloor + 1, yfloor ) // left upper corner
and finally you wouöd first interpolate along parallel axes and then in the middle, which can be done as follows using intermediate values.
val1 = cubic(value(P1), value(P2), deltay) // interpolate cubically on the left edge
val2 = cubic(value(P4), value(P3), deltay) // interpolate cubically on the right edge
val = cubic (val1, val2, deltax) // interpolate cubically between the intermediates
Interpolation methods are also discussed here.
In your bicubic-interpolation method you write:
//double inBetweenPoint = x;
//int xInHeightmap = (int) x;
//inBetweenPoint -= xInHeightmap;
return cubicInterpolate(new double[]{beforePoint1, point1, point2, afterPoint2}, inBetweenPoint, scale);
As you can easily see, inBetweenPoint will be in the interval [0, 1) at the call to cubicInterpolate. This means that interpolation will be between beforePoint1 and point1. not, as wanted, between point1 and point2.
The simple fix is writing
return cubicInterpolate(new double[]{beforePoint1, point1, point2, afterPoint2}, inBetweenPoint + 1, scale);
I have to create a class to calculate the distance between 2 given points for class. The instructor gave us the top half of the assignment with all the necessary code without modifying, the problem that im having is creating the class part. This is what i have so far...
class Point{
int x;
int y;
public Point(){
this.x = 0;
this.y = 0;
}
public Point(int x, int y){
this.x = x;
this.y = y;
}
public double distance(int x, int y) {
double d = Math.sqrt( Math.pow(x2-x1, 2) + Math.pow(y2-y1, 2) );
return distance;
}
}
the top half of the assignment looks like this:
import java.util.Scanner;
class Assignment4{
public static void main(String[] args){
// first and second points
Point first, second;
// try parsing points from command line args
if(args.length==4){
// new Point(int x, int y) creates a new Point located at position (x,y)
first = new Point(Integer.valueOf(args[0]), Integer.valueOf(args[1]));
second = new Point(Integer.valueOf(args[2]), Integer.valueOf(args[3]));
}
// if not specified as argument, get points from user
else{
Scanner input = new Scanner(System.in);
System.out.println("Enter first point: ");
first = new Point(input.nextInt(),input.nextInt());
System.out.println("Enter second point: ");
second = new Point(input.nextInt(),input.nextInt());
}
//calculate distance
//double d = Math.sqrt( Math.pow(x2-x1, 2) + Math.pow(y2-y1, 2) );
double d = first.distance(second.x, second.y);
System.out.println("Distance between " +
"(" + first.x + "," + first.y + ")" +
" and " +
"(" + second.x + "," + second.y + ")" +
" is " + d);
System.out.println();
}
}
When i try and compile the program, it says "cannot find symbol" referring to x2, x1, y2, y1, and distance.
Here :
class Point{
int x;
int y;
.....
.....
public double distance(int x, int y) {
double d = Math.sqrt( Math.pow(x2-x1, 2) + Math.pow(y2-y1, 2) ); //ERROR IN THIS LINE
return distance; //ERROR HERE TOO...(2)
}
}
There is no x1,x2,y1,y2 defined in the class or in the method parameter.
Swap it with the following line:
double d = Math.sqrt( Math.pow(this.x-x, 2) + Math.pow(this.y-y, 2) );
(2)
Error 2 Swap with this line:
return d;
The purpose of the method is to take in a Point (c), then move the current centroid (x,y) to the new coordinated provided. This is done through finding the difference of the two and then translating the point to the new coordinate. However, I do not know how to set the new centroid value since I am setting the methods as voids.... Any ideas?
Sorry for all of the code, tried to put as little as possible. Translate takes the point(x,y) and adds the values given (dx,dy).
private Point centroid;
Triangle(Point a, Point b, Point c) {
this.a = a;
this.b = b;
this.c = c;
triPoints = new Point[] { a, b, c };
}
public Point getCentroid() {
Point centroid;
double x = 0.0;
double y = 0.0;
x = (a.getX() + b.getX() + c.getX()) / 3;
y = (a.getY() + b.getY() + c.getY()) / 3;
centroid = new Point(x, y);
return centroid;
}
public void move(double dx, double dy) {
centroid = getCentroid();
System.out.println(centroid.getX() + " "+ centroid.getY());
centroid = centroid.translate(dx, dy);
Point newCentroid = new Point(centroid.getX(),centroid.getY());
System.out.println(newCentroid.getX() + " "+ newCentroid.getY());
if(getCentroid().equals(newCentroid)){
}else{
}
for (int index = 0; index < triPoints.length - 1; index++) {
triPoints[index].translate(dx, dy);
}
}
public void move(Point c) {
double firstx = 0.0;
double firsty = 0.0;
Point f = getCentroid();
System.out.println(f.getX()+ " "+ f.getY() + " "+ c.getX()+ " "+ c.getY());
if (f.getX() >= c.getX()) {
firstx = c.getX() - f.getX();}
else{
firstx = Math.abs(f.getX() - c.getX());
}
if (f.getY()>= c.getY()){
firsty = c.getY() - f.getY();
}
else {
firsty = Math.abs(f.getY() -c.getY());
}
move(firstx, firsty);
//System.out.println(centroid.getX() + " " + centroid.getY());
}
I am assuming you are using java.awt.Point?
If so, centroid.translate(dx, dy) is a void method: no value is actually returned, so
centroid =centroid.translate(dx, dy)
does not actually do anything.
Since you are just wanting a new centroid, try doing
centroid = getCentroid();
System.out.println(centroid.getX() + " "+ centroid.getY());
Point newCentroid = new Point(centroid.getX() + dx,centroid.getY() + dy);
System.out.println(newCentroid.getX() + " "+ newCentroid.getY());
There is no need to do the translate if all you want to do once you translate is make a new centroid.
Also, neither the translate function nor the constructor take a double (which makes sense if you are on a standard x,y graph), so you can make dx and dy int.