Solved Replicate WorldEdit rotate

Discussion in 'Spigot Plugin Development' started by SeanTMG, Oct 17, 2020.

  1. I need to rotate a cuboid of blocks on the Z axis, to sort of make them sideways.

    I'm really not a math person, and so far all I've figured out is that WorldEdit can already do this (//rotate X Y Z).

    I already have a Cuboid class with pos1 and pos2, and I can grab all the blocks inside the cuboid.

    I need to replicate this rotate method, but WorldEdit source is a huge mess. Their rotate methods are in some kind of Transform interface which has bukkit wrappers and just ties all into the WorldEdit API and crap. I can't figure out how they're actually doing the rotation calculations.

    Basically, I need to get the right from the left in this image by rotating 45 degrees

    ac679d3e00eed92d8e023fc1bfd0cea9.png
     
  2. From the center of the element, each element (block) has to be rotated by 45°. Either replicate the behavior yourself, or use Vector#rotateAroundZ(angle). The rotated block represents the coordinate of the block which only needs some rounding.
     
  3. org.bukkit.util.Vector and java.util.Vector have no methods named rotateAroundZ
     
  4. this not present in 1.12?
     
  5. No, it's not. Here is the full 1.12.2 Vector class
     
  6. Then you’ll have to mimic that behavior yourself. Either have a look at newer implementation where that method is implemented, or have a look at Rotation matrices. It’s not as hard as it looks like :p
     
  7. These are the rotation matricies you want to use:
    aefd54065686f7560f8c7558dc24c2d1.png
    They will rotate about the x,y, and z axis.

    To implement this, first define your origin however you like (world edit uses the player location, so I'll show that):
    Code (Text):

    Location loc = p.getLocation();
    Vector origin = new Vector(loc.getX(),loc.getY(),loc.getZ());
     
    Then you want the RELATIVE vectors of all the blocks in your selection, assuming you have a set of blocks defined as "selection" the code would look like this:
    Code (Text):

    List<Vector> vectors = new ArrayList<>();
    for (Block block : selection) {
        Location blockLocation = block.getLocation();
        Vector blockVector = new Vector(block.getX(),block.getY(),block.getZ());
        Vector blockRelativeVector = blockVector.subtract(origin);
        vectors.add(blockRelativeVector);
    }
     
    I'm not sure if there is any pre-existing methods for matrix multiplication using spigot vectors. Nonetheless, it's pretty easy to code just the rotation case, I'll show you rotation x:

    Code (Text):

    public Vector rotateX(Vector inputVector,double thetaInRadians) {
        Vector v1 = new Vector(1,0,0).multiply(inputVector.getX());
        Vector v2 = new Vector(0,Math.cos(thetaInRadians),Math.sin(thetaInRadians)).multiply(inputVector.getY());
        Vector v3 = new Vector(0,-1*Math.sin(thetaInRadians),Math.cos(thetaInRadians)).multiply(inputVector.getZ());
        Vector resultVector = v1.add(v2).add(v3);
        return resultVector;
    }
     
    (Note that the results will be in decimal value, you'll need to do some rounding)


    If you want to rotate around some general axis like in spigot's rotateAroundAxis then you can do one of two things:
    1) Apply 2 rotations (one or both may be 0 degrees) to rotate your general axis so it becomes the x (or y or z)-axis. Then rotate around the x (or y, or z) axis by the inputted rotation. Finally, reverse the initial 2 rotations.

    2) Change your basis so that the new axis is your first component (or second or third) (i.e your basis changed general axis should be mapped to <1,0,0> (or <0,1,0> or <0,0,1>) Then rotate around x (or y or z). Then apply the inverse transformation of your basis change (change back to the original basis)

    I think 2) is easier because you don't need to figure out any angles, but can also be much more confusing. So I'll show you how to do 1) assuming you've written rotateY(Vector inputVector,double thetaInRadians) and rotateZ(Vector inputVector,double thetaInRadians) methods.

    First you're going to want the spherical coordinate angles because they will help you rotate to
    3578287b7097acc921b5b4f1decff156.png
    Let's choose to rotate our general direction so it becomes the z vector (z is the easiest when using spherical coordinate angles)
    Take a look at this image:
    1024px-3D_Spherical.svg.png
    From this, image, it's clear if we rotate our vector by -φ around the z axis we'll be in the x-z plane.
    Then, we can rotate by -θ around the y axis and we'll be at the z axis! (some multiple of the vector <0,0,1>) Then we rotate through our inputted angle (let's call it alpha) α around the z axis. Then we just reverse the original rotations by rotating θ around the y axis then φ around the z axis.

    Our full transformation looks like this when written out:
    gif.gif

    And in code:
    Code (Text):

    public Vector rotateGeneral(Vector inputVector,Vector generalAxis,double alphaInRadians) {
        double generalX = inputVector.getX();
        double generalY = inputVector.getY();
        double generalZ = inputVector.getZ();
        double length = Math.sqrt(generalX*generalX + generalY*generalY + generalZ*generalZ);
        double theta = Math.acos(generalZ/length);
        double phi = Math.atan(generalY/generalX);
        return rotateZ(rotateY(rotateZ(rotateY(rotateZ(inputVector,-1*phi),-1*theta),alphaInRadians),theta),phi);
    }
     
     
    #7 suppusmac, Oct 18, 2020
    Last edited: Oct 18, 2020
    • Useful Useful x 2
  8. Thank you! This was very helpful.