Question

I am trying to calculate 4 points of a square in three dimensional space that is perpendicular to a line formed by two given points. This square must be:

  1. Perpendicular to the line defined by two given points
  2. Of thickness (or width/length) 1
  3. aligned so that two sides are always perpendicular to the Y axis

I have tried coding this myself, and i somewhat succeeded, but it is not exactly what i wanted. Here is a code snippet of what i have:

Note: Vert is a class that stores a coordinate in 3 variables, x, y, and z. FourVert is a class that stores 4 Verts, and the constructor is what i am trying to develop. The two Vert parameters are the given points, and the float t is the thickness or separation required between the output 4 points. Add is a function that add the given Vert's coordinate value as an offset to all 4 points of the FourVert. This is only used to reposition the points on the right place, the first Vert. I might have weird naming conventions, but i write it in a way that i understand it.

EDIT: Added comments as requested

public FourVert(Vert V1, Vert V2, float t)
{
    float dx = V2.x - V1.x; //Position our points at the origin to convert into
    float dy = V2.y - V1.y; //polar coordinates using Math.atan2();
    float dz = V2.z - V1.z;
    float d = (float) Math.sqrt(dx*dx+dz*dz); //Get the lateral distance to use
    float axz = (float) -Math.atan2(dx, dz);  //in relation with the y value to
    float ady = (float) Math.atan2(d, dy);    //calculate the altitude angle
    float px = (float) (Math.cos(axz) * t);
    float py = (float) (Math.cos(ady + 1.570796327) * t); //add half PI
    float ny = (float) (Math.cos(ady - 1.570796327) * t);
    float pz = (float) (Math.sin(axz) * t);
    v1 = new Vert(px, py, pz);   //Use the calculated values to produce 4 points
    v2 = new Vert(-px, py, -pz); //v1, v2, v3, and v4 are our Vert fields of the
    v3 = new Vert(-px, ny, -pz); //FourVert class
    v4 = new Vert(px, ny, pz);
    add(V1); //take our FourVert back to where we want it, not on the origin.
}

I already have the rendering taken care of, and i know it works well because i have tested it with givens.

What works here is the azimuth values, they function perfectly (Lateral movement). The vertical movement, or altitude, or 'pitch' does not function correctly. I have tried many other ways to do this to no avail.

The problem/part that is not working is here I think:

    float py = (float) (Math.cos(ady + 1.570796327) * t);
    float ny = (float) (Math.cos(ady - 1.570796327) * t);
Was it helpful?

Solution

I'd avoid all that trigonometry. For one of the edge directions, you want a vector which is at the same time perpendicular to V1V2 and to the Y axis. The cross product can be used to obtain a vector which is perpendicular to two given ones. So you compute it like this:

[ 0 ]   [ x2 − x1 ]   [ z2 − z1 ]   [  dz ]
[ 1 ] × [ y2 - y1 ] = [    0    ] = [  0  ] = w₁
[ 0 ]   [ z2 - z1 ]   [ x1 − x2 ]   [ −dx ]

For the second edge direction, you want to be perpendicular to that first one, and also perpendicular to V1V2 again. So the same story:

[  dz ]   [ dx ]   [    dx*dy    ]    [ ex ]
[  0  ] × [ dy ] = [ − dx² − dz² ] =: [ ey ] = w₂
[ −dx ]   [ dz ]   [    dz*dy    ]    [ ez ]

You'll have to normalize these vectors to ensure they have length 1, then you are done. In. fact I'd normalize them to length ½ because then you can use ± that vector to describe positions which are symmetric around the origin, the way your own code does it as well. For the corners you then need ±w1±w2, which gives four possible sign combinations.

public FourVert(Vert V1, Vert V2, float t)
{
    float dx = V2.x - V1.x;
    float dy = V2.y - V1.y;
    float dz = V2.z - V1.z;
    float ex = dx*dz;
    float ey = -dx*dx-dz*dz;
    float ez = dz*dy;
    float d = (float)(t/(2*Math.sqrt(dx*dx+dz*dz)));
    float e = (float)(t/(2*Math.sqrt(ex*ex+ey*ey+ez*ez)));
    dx *= d; dz *= d;
    ex *= e; ey *= e; ez *= e;
    v1 = new Vert( dz+ex,  ey, -dx+ez);
    v2 = new Vert( dz-ex, -ey, -dx-ez);
    v3 = new Vert(-dz-ex, -ey,  dx-ez);
    v4 = new Vert(-dz+ex,  ey,  dx+ez);
    add(V1);
}

You probably won't need this now any more, but for the sake of completeness: instead of

float py = (float) (Math.cos(ady + 1.570796327) * t);
float ny = (float) (Math.cos(ady - 1.570796327) * t);

I'd write

float py = (float) (-Math.sin(ady) * t);
float ny = (float) ( Math.sin(ady) * t);

This makes use of the fact that sin and cos differ by a phase of π/2.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top