0

I have a random point defined by:

int x = rand.nextInt(9)-4;//-4 to +4
int y = rand.nextInt(4)+2;//+2 to +5
int z = rand.nextInt(9)-4;//-4 to +4

Y happens to be the "up" vector, but I suspect that's irrelevant.

I want to rotate this point so that it ends up relative to a vector that passes through (i,j,k) rather than relative to the vector that passes through (0,1,0).

Thus if (x,y,z) is (0,6,0) and (i,j,k) is (1,1,0) the result should be about (5,5,0).

Essentially I'm trying to draw a random deflection vector based on the initial input force vector. In this case, fractures through 3D voxel rock, but bullet deflection lines off armor make for a good visualization.

Qmechanic
  • 201,751

1 Answers1

1

Given a vector $$\vec{v} = \begin{pmatrix} x \\ y \\ z \end{pmatrix} $$ and a general rotation axis $$ \vec{k} = \begin{pmatrix} k_x \\ k_y \\ k_z \end{pmatrix} $$

then the resulting vector after a rotation by $\theta$ is

$$\vec{u} = \vec{v}\cos\theta + \left(\vec{k} \times \vec{v} \right) \sin \theta + \vec{k} \left( \vec{k} \cdot \vec{v} \right) (1-\cos \theta) $$

where $\times$ is the vector cross product and $\cdot$ the dot product.

If you are looking for the angle to rotate, then look up angle between two vectors. The the rotation axis is defined by the cross product of the original vector and the target vector.

I am not sure how familiar you are with C# but there is the code to do what I think you are asking.

class Program
{
    static void Main(string[] args)
    {
        // create a random vector within the -10 to 10 range
        Vector3 a=10*Vector3.Random();

        // target vector
        Vector3 b=new Vector3() { x=1, y=1, z=0 };

        // get rotation angle
        double θ=a.AngleTo(b);
        // get rotation axis
        Vector3 k=a.Cross(b).Normalized();


        // get rotated vector c
        Vector3 c=a.RotateAbout(k, θ);

        // If c is || to b then c×b=0

        Debug.Assert(c.Cross(b).Magnitude<1e-8);
    }
}

[DebuggerDisplay("({x},{y},{z})")]
public struct Vector3
{
    static Random rnd=new Random();

    public double x, y, z;

    public static Vector3 Random()
    {
        return new Vector3()
        {
            x=-1+2*rnd.NextDouble(),
            y=-1+2*rnd.NextDouble(),
            z=-1+2*rnd.NextDouble()
        };
    }

    public double Magnitude
    {
        get { return Math.Sqrt(x*x+y*y+z*z); }
    }

    public Vector3 Normalized()
    {
        double m=Magnitude;
        if (m>0)
        {
            return new Vector3()
            {
                x=x/m,
                y=y/m,
                z=z/=m
            };
        }
        return this;
    }

    public double Dot(Vector3 other)
    {
        return x*other.x+y*other.y+z*other.z;
    }

    public Vector3 Cross(Vector3 other)
    {
        return new Vector3()
        {
            x=y*other.z-z*other.y,
            y=z*other.x-x*other.z,
            z=x*other.y-y*other.x
        };
    }

    public static Vector3 operator*(double factor, Vector3 vector)
    {
        return new Vector3()
        {
            x=factor*vector.x,
            y=factor*vector.y,
            z=factor*vector.z
        };
    }

    public static Vector3 operator+(Vector3 a, Vector3 b)
    {
        return new Vector3()
        {
            x=a.x+b.x,
            y=a.y+b.y,
            z=a.z+b.z
        };
    }

    public double AngleTo(Vector3 other)
    {
        // |a·b| = |a| |b| COS(θ)
        // |a×b| = |a| |b| SIN(θ)

        double a=Magnitude;
        double b=other.Magnitude;

        double sin=this.Cross(other).Magnitude/(a*b);
        double cos=this.Dot(other)/(a*b);

        return Math.Atan2(sin, cos);
    }

    public Vector3 RotateAbout(Vector3 k, double θ)
    {
        // Assume k is normalized.
        double sin=Math.Sin(θ), cos=Math.Cos(θ);

        return cos*this+(-sin)*Cross(k)+(1-cos)*Dot(k)*k;
    }
}
John Alexiou
  • 38,341
  • If you want a matrix form of this formula, see my answer here. But I think ja72's form is likely the easiest to code. – Selene Routley Sep 13 '13 at 01:00
  • Oh my god, you guys are awesome. Something is going wrong though, my end result is a vector that is nearly parallel to the original, regardless of the random vector. That is, in the above example C and B have the same normalized direction, even if A was at a right angle to B... – Draco18s no longer trusts SE Sep 13 '13 at 13:36
  • Haha, figured out my error! I wasn't finding the angle and rotation axis from a vector-up, but rather from my random vector, so of course the result would be what it was! I'm clearly not getting enough caffeine. – Draco18s no longer trusts SE Sep 13 '13 at 14:18