Advertisement
Mangus875

Inverse Kinematics

Dec 20th, 2023
1,271
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /*
  2. <html><head>
  3. <style>
  4.     ::root, body {
  5.         background-color: #333;
  6.        
  7.         --segment-length: 50px;
  8.         --segment-width:  10px;
  9.     }
  10.  
  11.     div {
  12.         position: absolute;
  13.         width: 50px;
  14.         height: 50px;
  15.        
  16.         background-color: #1fc0cf;
  17.     }
  18.  
  19.     .arm {
  20.         width: var(--segment-length);
  21.         height: var(--segment-width);
  22.        
  23.         left: 100%;
  24.         top: 50%;
  25.         transform-origin: left center;
  26.         transform: translateY(-50%);
  27.     }
  28.  
  29.     :not(.arm) > .arm {
  30.         left: 50%;
  31.     }
  32. </style>
  33. </head>
  34.    
  35. <body>
  36. </body></html>
  37. */
  38.  
  39. /****  HACK: shorthand for loop
  40.   *
  41.   * Syntax:
  42.   * from(min).to(max).run(callbackFn)
  43.   *
  44.   * Parameters:
  45.   *     min
  46.   *         index to start the loop at
  47.   *
  48.   *     max
  49.   *         index to end the loop at
  50.   *
  51.   *     callbackFn
  52.   *         function to execute in the loop
  53.   *
  54.   * Equivalent to:
  55.   *     for (let i = min; i < max; i++) {
  56.     *           callbackFn(i);
  57.     *       }
  58.   *
  59. ***/
  60.  
  61. function from(min) {
  62.     let fromObj = [];
  63.     fromObj.to = (max) => {
  64.         let toObj = {};
  65.         toObj.run = (callbackFn) => {
  66.             if (min > max) throw new RangeError(`loop start must be below loop max`);
  67.             let results = [];
  68.             for (let i = min; i < max; i++) {
  69.                 results.push(callbackFn(i));
  70.             }
  71.         }
  72.         return toObj;
  73.     }
  74.     return fromObj;
  75. }
  76.  
  77. function getType(obj) {
  78.     if (obj === null) return 'null';
  79.     if (obj === undefined) return 'undefined';
  80.     return obj.__proto__.constructor.name;
  81. }
  82.  
  83. function from(min) {
  84.     let fromObj = [];
  85.     fromObj.to = (max) => {
  86.         let toObj = {};
  87.         toObj.run = (statement) => {
  88.             if (min > max) throw new RangeError(`loop start must be below loop max`)
  89.             for (let i = min; i <= max; i++) {
  90.                 statement(i);
  91.             }
  92.         }
  93.         return toObj;
  94.     }
  95.     return fromObj;
  96. }
  97.  
  98. function desmosLerp(a,b) {
  99.     if (getType(a) != getType(b)) throw new TypeError(`cannot lerp between a point and a number`);
  100.     if (getType(a) == "Vector2") {
  101.         a = a.toString(10000);
  102.         // a.replace("(", "\\left(");
  103.         // a.replace(")", "\\right)");
  104.         // a.replace(" ", "");
  105.         b = b.toString(10000);
  106.         // b.replace("(", "\\left(");
  107.         // b.replace(")", "\\right)");
  108.         // b.replace(" ", "");
  109.     }
  110.     return `l_{erp}(t,${a},${b})`
  111. }
  112.  
  113. function desmosify(points, lerp, name) {
  114.     let result = name[0]+"_{"+name.substring(1)+"}=\\left[";
  115.    
  116.     for (let i = 0; i < points.length; i++) {
  117.         let pt = points[i];
  118.         if (i == points.length-1 && lerp) continue;
  119.         if (lerp) {
  120.             result += desmosLerp(points[i], points[i+1]);
  121.             result += (i != points.length-2 ? ',' : '');
  122.             continue;
  123.         }
  124.         result += pt.toString(10000);
  125.         result += (i != points.length-1 ? ',' : '');
  126.     }
  127.     /*
  128.     points.forEach((pt,i) => {
  129.         if (lerp) {
  130.             result += desmosLerp(pt
  131.         }
  132.         result += pt.toString(10000);
  133.         result += (i != points.length-1 ? ',' : '');
  134.     });
  135.     */
  136.     result += "\\right]";
  137.     // result.replaceAll("(", "\\left(");
  138.     // result.replaceAll(")", "\\right)");
  139.     result.replaceAll(" ", "");
  140.     return result;
  141. }
  142.  
  143. function roundTo(n,p=1) {
  144.     return Math.round(n*p)/p;
  145. }
  146.  
  147. class Vector2 {
  148.     constructor(x=0, y=0) {
  149.         this.x = x;
  150.         this.y = y;
  151.     }
  152.    
  153.     static copy(v) {
  154.         return new Vector2(v.x, v.y);
  155.     }
  156.    
  157.     static add(v1, v2) {
  158.         return new Vector2(v1.x+v2.x, v1.y+v2.y);
  159.     }
  160.    
  161.     static sub(v1, v2) {
  162.         return new Vector2(v1.x-v2.x, v1.y-v2.y);
  163.     }
  164.    
  165.     add(v) {
  166.         this.x += v.x;
  167.         this.y += v.y;
  168.         return this;
  169.     }
  170.    
  171.     sub(v) {
  172.         this.x -= v.x;
  173.         this.y -= v.y;
  174.         return this;
  175.     }
  176.    
  177.     mult(s) {
  178.         this.x *= s;
  179.         this.y *= s;
  180.         return this;
  181.     }
  182.    
  183.     normalize() {
  184.         this.mult(1/this.magnitude);
  185.         return this;
  186.     }
  187.    
  188.     get magnitude() {
  189.         return Math.sqrt(this.x**2 + this.y**2);
  190.     }
  191.    
  192.     set magnitude(mag) {
  193.         this.normalize();
  194.         this.mult(mag);
  195.     }
  196.    
  197.     moveTo(v) {
  198.         this.x = v.x;
  199.         this.y = v.y;
  200.     }
  201.    
  202.     toString(d=100) {
  203.         return `(${roundTo(this.x, d)}, ${roundTo(this.y, d)})`;
  204.     }
  205. }
  206.  
  207. const rad = deg => deg*Math.PI / 180;
  208. const deg = rad => 180*rad / Math.PI;
  209.  
  210. function polar(...pt) {
  211.     let x,y;
  212.     if (pt.length == 1) {
  213.         x = pt[0].x;
  214.         y = pt[0].y;
  215.     } else {
  216.         x = pt[0];
  217.         y = pt[1];
  218.     }
  219.     let coord = [];
  220.     coord.r = Math.sqrt(x*x + y*y);
  221.     coord.theta = Math.atan(y,x);
  222.     return coord;
  223. }
  224.  
  225. function car(r,theta) {
  226.     let x,y;
  227.     x = r*Math.cos(theta);
  228.     y = r*Math.sin(theta);
  229.     return [x,y];
  230. }
  231.  
  232. function getAngles(points, degrees=false) {
  233.     let angs = [];
  234.     for (let i = 0; i < points.length; i++) {
  235.         let pt1 = points[i];
  236.         let pt2 = points[i-1] || new Vector2();
  237.        
  238.         let angle = polar(Vector2.sub(pt1, pt2)).theta;
  239.         if (degrees) angle = deg(angle.radians);
  240.        
  241.         angs.push(angle);
  242.     }
  243.     return angs;
  244. }
  245.  
  246. function getLengths(points) {
  247.     let lengths = [];
  248.     for (let i = 0; i < points.length; i++) {
  249.         let pt1 = points[i];
  250.         let pt2 = points[i-1] || new Vector2();
  251.        
  252.         let dist = polar(Vector2.sub(pt1, pt2)).r;
  253.         lengths.push(dist);
  254.     }
  255.     return lengths;
  256. }
  257.  
  258. class arm {
  259.     constructor(...points) {
  260.         this.points = points;
  261.         this.lengths = getLengths(this.points);
  262.         this.angles = getAngles(this.points);
  263.     }
  264.    
  265.     reach(target, iterations=1) {
  266.         let origin = Vector2.copy(this.points[0]);
  267.         for (let n = 0; n < iterations; n++) {
  268.             this.points[this.points.length-1].moveTo(target);
  269.             for (let i = this.points.length-2; i >= 0; i--) {
  270.                 this.points[i].moveTo(Vector2.sub(this.points[i],this.points[i+1]).normalize().mult(this.lengths[i+1]).add(this.points[i+1]));
  271.             }
  272.             this.points[0].moveTo(origin);
  273.             for (let i = 1; i < this.points.length; i++) {
  274.                 this.points[i].moveTo(Vector2.sub(this.points[i],this.points[i-1]).normalize().mult(this.lengths[i]).add(this.points[i-1]));
  275.             }
  276.         }
  277.        
  278.         this.updateAngles();
  279.     }
  280.    
  281.     updateAngles() {
  282.         this.angles = getAngles(this.points);
  283.     }
  284. }
  285.  
  286. function createArm(segs) {
  287.    
  288. }
  289. window.onload = () => {
  290.    
  291. }
  292. let segs = []
  293. for (let i = 0; i < 5; i++) {
  294.     segs.push(new Vector2(i, 0));
  295. }
  296. let ikArm = new arm(...segs);
  297.  
  298. ikArm.reach(new Vector2(0, 100), 100);
  299. console.log(ikArm.angles);
  300.  
  301. ikArm.angles.forEach((a,i) => {
  302.     let targSel = "> .arm";
  303.     let baseTransform = "translateY(-50%)";
  304.     let sel = ':not(.arm) ';
  305.     from(0).to(i).run(_=>sel += targSel+' ');
  306.     sel = sel.trim();
  307.    
  308.     document.querySelector(sel).style.transform = `${baseTransform} rotate(${a}rad)`;
  309. });
  310.  
  311. // <div class='arm'></div>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement