ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [ JS ] rotate3d 값 얻기
    Javascript/이론 2023. 2. 8. 13:55
    반응형

      JS를 통해 요소의 rotate3d 프로퍼티를 얻기 위해서 만든 클래스입니다.

     

    1. Constructor

      constructor에서는 transform property를 얻고자 하는 요소를 parameter로 두었습니다. 따라서 클래스 성성시 요소를 받아 요소의 matrix3d 데이터를 저장합니다.

     

    class Transform {
      constructor(ele) {
        this.ele = ele;
        this.matrix3d = window
          .getComputedStyle(ele)
          .getPropertyValue("transform") // column priority
          .split("(")[1]
          .split(")")[0]
          .split(",")
          .map((ele) => Number(ele));
      }
    }

     

    2. Method

    1) show()

      요소의 matrix3d 데이터를 행렬꼴로 콘솔창에 출력합니다.

     

    show() {
        let temp = this.matrix3d.slice().map((ele) => Number(ele.toFixed(2)));
    
        if (temp.length === 6) {
          console.log(
            `
            ${temp[0]} / ${temp[2]} / ${temp[4]}
            ${temp[1]} / ${temp[3]} / ${temp[5]}
            0 / 0 / 1
            `
          );
        } else if (temp.length === 16) {
          console.log(
            `
            ${temp[0]} / ${temp[4]} / ${temp[8]} / ${temp[12]}
            ${temp[1]} / ${temp[5]} / ${temp[9]} / ${temp[13]}
            ${temp[2]} / ${temp[6]} / ${temp[10]} / ${temp[14]}
            ${temp[3]} / ${temp[7]} / ${temp[11]} / ${temp[15]}
            `
          );
        }
    
        return;
      }

     

     

    2) showAllFactors()

      메서드에서는 각 변환 인자를 콘솔창에 출력합니다.

     

    showAllFactors() {
        let t = this.getTranslateFactors();
        let s = this.getScaleFactors();
        let r = this.getRotateFactors();
    
        console.log("\n--------------translation--------------\n");
        Object.keys(t).forEach((ele) => console.log(`${ele} : ${t[ele]}`));
        console.log("\n---------------scaling-----------------\n");
        Object.keys(s).forEach((ele) => console.log(`${ele} : ${s[ele]}`));
        console.log("\n---------------rotation----------------\n");
        Object.keys(r).forEach((ele) => console.log(`${ele} : ${r[ele]}`));
      }

     

     

    3) getTranslateFactors

      tx, ty, tz의 키값을 가지는 객체로 translation에 사용된 인자를 반환합니다. 각 key에 대응하는 value의 자료형은 숫자입니다.

     

    getTranslateFactors() {
        if (this.matrix3d.length === 6)
          return {
            tx: this.matrix3d[4].toFixed(2),
            ty: this.matrix3d[5].toFixed(2),
          };
        if (this.matrix3d.length === 16)
          return {
            tx: this.matrix3d[12].toFixed(2),
            ty: this.matrix3d[13].toFixed(2),
            tz: this.matrix3d[14].toFixed(2),
          };
      }

     

     

    4) getScaleFactors

      sx, sy, sz의 키값을 가지는 객체로 scalling에 사용된 인자를 반환합니다.  각 key에 대응하는 value의 자료형은 숫자입니다.

     

    getScaleFactors() {
        if (this.matrix3d.length === 6)
          return {
            sx: Math.pow(
              this.matrix3d[0] ** 2 + this.matrix3d[2] ** 2,
              0.5
            ).toFixed(2),
            sy: Math.pow(
              this.matrix3d[1] ** 2 + this.matrix3d[3] ** 2,
              0.5
            ).toFixed(2),
          };
        if (this.matrix3d.length === 16)
          return {
            sx: Math.pow(
              this.matrix3d[0] ** 2 + this.matrix3d[4] ** 2 + this.matrix3d[8] ** 2,
              0.5
            ).toFixed(2),
            sy: Math.pow(
              this.matrix3d[1] ** 2 + this.matrix3d[5] ** 2 + this.matrix3d[9] ** 2,
              0.5
            ).toFixed(2),
            sz: Math.pow(
              this.matrix3d[2] ** 2 +
                this.matrix3d[6] ** 2 +
                this.matrix3d[10] ** 2,
              0.5
            ).toFixed(2),
          };
      }

     

     

    (5) getRotateFactors

      u, deg를 키값으로 갖는 객체로 rotate 인자를 반환합니다. 앞서 정의된 getScaleFactors()를 이용해 요소의 matrix3d 데이터에서 scaling factor를 제거하며, u와 deg를 구하는 원리는 아래 링크에서 확인 가능합니다.

     

     

    [Computer Graphics] 3D 변환 행렬과 행렬 분해

    1. 3D 변환 행렬의 구성 3D 공간에서의 변환은 보통 4x4 매트릭스를 통해 이루어집니다.(homogenous coordinate라고 표현하며 하나의 행렬을 통해 여러 종류의 연산을 나타낼 수 있다는 장점을 가집니다.)

    ojhallae.tistory.com

     

      각 key에 대응하는 value의 자료형은 사용의 편의성을 위해 문자열로 반환합니다.

     

    getRotateFactors() {
        let scaleFactors = Object.values(this.getScaleFactors());
        let dimension = scaleFactors.length;
        let tempMatrix = this.matrix3d.slice();
        let deg;
    
        if (dimension === 2) {
          [0, 1].forEach((ele) => {
            for (let i = 0; i < 2; i++) {
              tempMatrix[ele + 2 * i] /= scaleFactors[ele];
            }
          });
    
          deg = (Math.acos(tempMatrix[0]) * 180) / Math.PI;
    
          return { u: undefined, deg: deg.toFixed(2) + "deg" };
        } else if (dimension === 3) {
          [0, 1, 2].forEach((ele) => {
            for (let i = 0; i < 3; i++) {
              tempMatrix[ele + 4 * i] /= scaleFactors[ele];
            }
          });
    
          let u = [
            tempMatrix[6] - tempMatrix[9],
            tempMatrix[8] - tempMatrix[2],
            tempMatrix[1] - tempMatrix[4],
          ];
    
          if (u[0].toFixed(2) != 1) {
            deg =
              (Math.acos((tempMatrix[0] - u[0] ** 2) / (1 - u[0] ** 2)) * 180) /
              Math.PI;
          } else if (u[1].toFixed(2) != 1) {
            deg =
              (Math.acos((tempMatrix[5] - u[1] ** 2) / (1 - u[1] ** 2)) * 180) /
              Math.PI;
          } else if (u[2].toFixed(2) != 1) {
            deg =
              (Math.acos((tempMatrix[10] - u[2] ** 2) / (1 - u[2] ** 2)) * 180) /
              Math.PI;
          }
    
          return {
            u: u.map((ele) => ele.toFixed(2)).join(","),
            deg: deg.toFixed(2) + "deg",
          };
        }
      }

     

     

    6) setSameTransformTo

      클래스 생성시 인자로 전달받은 요소의 trasform property에 적용된 translation, scaling, rotation을 메서드의 인자로 받은 요소에 적용합니다. 

     

    setSameTransformTo(ele) {
        let t = this.getTranslateFactors();
        let s = this.getScaleFactors();
        let r = this.getRotateFactors();
        console.log(t, s, r);
    
        ele.style.transform = `translate3d(${t["tx"]}px, ${t["ty"]}px, ${
          t["tz"]
        }px) scale3d(${s["sx"] + "," + s["sy"] + "," + s["sz"]}) rotate3d(${
          r["u"]
        }, ${r["deg"]})`;
      }

     

     

    3. Full code

    class Transform {
      constructor(ele) {
        this.ele = ele;
        this.matrix3d = window
          .getComputedStyle(ele)
          .getPropertyValue("transform") // column priority
          .split("(")[1]
          .split(")")[0]
          .split(",")
          .map((ele) => Number(ele));
      }
    
      show() {
        let temp = this.matrix3d.slice().map((ele) => Number(ele.toFixed(2)));
    
        if (temp.length === 6) {
          console.log(
            `
            ${temp[0]} / ${temp[2]} / ${temp[4]}
            ${temp[1]} / ${temp[3]} / ${temp[5]}
            0 / 0 / 1
            `
          );
        } else if (temp.length === 16) {
          console.log(
            `
            ${temp[0]} / ${temp[4]} / ${temp[8]} / ${temp[12]}
            ${temp[1]} / ${temp[5]} / ${temp[9]} / ${temp[13]}
            ${temp[2]} / ${temp[6]} / ${temp[10]} / ${temp[14]}
            ${temp[3]} / ${temp[7]} / ${temp[11]} / ${temp[15]}
            `
          );
        }
    
        return;
      }
    
      showAllFactors() {
        let t = this.getTranslateFactors();
        let s = this.getScaleFactors();
        let r = this.getRotateFactors();
    
        console.log("\n--------------translation--------------\n");
        Object.keys(t).forEach((ele) => console.log(`${ele} : ${t[ele]}`));
        console.log("\n---------------scaling-----------------\n");
        Object.keys(s).forEach((ele) => console.log(`${ele} : ${s[ele]}`));
        console.log("\n---------------rotation----------------\n");
        Object.keys(r).forEach((ele) => console.log(`${ele} : ${r[ele]}`));
      }
    
      getTranslateFactors() {
        if (this.matrix3d.length === 6)
          return {
            tx: this.matrix3d[4].toFixed(2),
            ty: this.matrix3d[5].toFixed(2),
          };
        if (this.matrix3d.length === 16)
          return {
            tx: this.matrix3d[12].toFixed(2),
            ty: this.matrix3d[13].toFixed(2),
            tz: this.matrix3d[14].toFixed(2),
          };
      }
    
      getScaleFactors() {
        if (this.matrix3d.length === 6)
          return {
            sx: Math.pow(
              this.matrix3d[0] ** 2 + this.matrix3d[2] ** 2,
              0.5
            ).toFixed(2),
            sy: Math.pow(
              this.matrix3d[1] ** 2 + this.matrix3d[3] ** 2,
              0.5
            ).toFixed(2),
          };
        if (this.matrix3d.length === 16)
          return {
            sx: Math.pow(
              this.matrix3d[0] ** 2 + this.matrix3d[4] ** 2 + this.matrix3d[8] ** 2,
              0.5
            ).toFixed(2),
            sy: Math.pow(
              this.matrix3d[1] ** 2 + this.matrix3d[5] ** 2 + this.matrix3d[9] ** 2,
              0.5
            ).toFixed(2),
            sz: Math.pow(
              this.matrix3d[2] ** 2 +
                this.matrix3d[6] ** 2 +
                this.matrix3d[10] ** 2,
              0.5
            ).toFixed(2),
          };
      }
    
      getRotateFactors() {
        let scaleFactors = Object.values(this.getScaleFactors());
        let dimension = scaleFactors.length;
        let tempMatrix = this.matrix3d.slice();
        let deg;
    
        if (dimension === 2) {
          [0, 1].forEach((ele) => {
            for (let i = 0; i < 2; i++) {
              tempMatrix[ele + 2 * i] /= scaleFactors[ele];
            }
          });
    
          deg = (Math.acos(tempMatrix[0]) * 180) / Math.PI;
    
          return { u: undefined, deg: deg.toFixed(2) + "deg" };
        } else if (dimension === 3) {
          [0, 1, 2].forEach((ele) => {
            for (let i = 0; i < 3; i++) {
              tempMatrix[ele + 4 * i] /= scaleFactors[ele];
            }
          });
    
          let u = [
            tempMatrix[6] - tempMatrix[9],
            tempMatrix[8] - tempMatrix[2],
            tempMatrix[1] - tempMatrix[4],
          ];
    
          if (u[0].toFixed(2) != 1) {
            deg =
              (Math.acos((tempMatrix[0] - u[0] ** 2) / (1 - u[0] ** 2)) * 180) /
              Math.PI;
          } else if (u[1].toFixed(2) != 1) {
            deg =
              (Math.acos((tempMatrix[5] - u[1] ** 2) / (1 - u[1] ** 2)) * 180) /
              Math.PI;
          } else if (u[2].toFixed(2) != 1) {
            deg =
              (Math.acos((tempMatrix[10] - u[2] ** 2) / (1 - u[2] ** 2)) * 180) /
              Math.PI;
          }
    
          return {
            u: u.map((ele) => ele.toFixed(2)).join(","),
            deg: deg.toFixed(2) + "deg",
          };
        }
      }
    
      setSameTransformTo(ele) {
        let t = this.getTranslateFactors();
        let s = this.getScaleFactors();
        let r = this.getRotateFactors();
        console.log(t, s, r);
    
        ele.style.transform = `translate3d(${t["tx"]}px, ${t["ty"]}px, ${
          t["tz"]
        }px) scale3d(${s["sx"] + "," + s["sy"] + "," + s["sz"]}) rotate3d(${
          r["u"]
        }, ${r["deg"]})`;
      }
    }
    반응형

    댓글

Designed by Tistory.