Advertisement
Mangus875

FABRIK Inverse Kinematics

Dec 19th, 2023 (edited)
839
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // TODO: finish fixing desmosify function
  2.  
  3. function getType(obj) {
  4.     if (obj === null) return 'null';
  5.     if (obj === undefined) return 'undefined';
  6.     return obj.__proto__.constructor.name;
  7. }
  8.  
  9. function from(min) {
  10.     let fromObj = [];
  11.     fromObj.to = (max) => {
  12.         let toObj = {};
  13.         toObj.run = (statement) => {
  14.             if (min > max) throw new RangeError(`loop start must be below loop max`)
  15.             for (let i = min; i <= max; i++) {
  16.                 statement(i);
  17.             }
  18.         }
  19.         return toObj;
  20.     }
  21.     return fromObj;
  22. }
  23.  
  24. function desmosLerp(a,b) {
  25.     if (getType(a) != getType(b)) throw new TypeError(`cannot lerp between a point and a number`);
  26.     if (getType(a) == "Vector2") {
  27.         a = a.toString(10000);
  28.         // a.replace("(", "\\left(");
  29.         // a.replace(")", "\\right)");
  30.         // a.replace(" ", "");
  31.         b = b.toString(10000);
  32.         // b.replace("(", "\\left(");
  33.         // b.replace(")", "\\right)");
  34.         // b.replace(" ", "");
  35.     }
  36.     return `l_{erp}(t,${a},${b})`
  37. }
  38.  
  39. function desmosify(points, lerp, name) {
  40.     let result = name[0]+"_{"+name.substring(1)+"}=\\left[";
  41.    
  42.     for (let i = 0; i < points.length; i++) {
  43.         let pt = points[i];
  44.         if (i == points.length-1 && lerp) continue;
  45.         if (lerp) {
  46.             result += desmosLerp(points[i], points[i+1]);
  47.             result += (i != points.length-2 ? ',' : '');
  48.             continue;
  49.         }
  50.         result += pt.toString(10000);
  51.         result += (i != points.length-1 ? ',' : '');
  52.     }
  53.     /*
  54.     points.forEach((pt,i) => {
  55.         if (lerp) {
  56.             result += desmosLerp(pt
  57.         }
  58.         result += pt.toString(10000);
  59.         result += (i != points.length-1 ? ',' : '');
  60.     });
  61.     */
  62.     result += "\\right]";
  63.     // result.replaceAll("(", "\\left(");
  64.     // result.replaceAll(")", "\\right)");
  65.     result.replaceAll(" ", "");
  66.     return result;
  67. }
  68.  
  69. function roundTo(n,p=1) {
  70.     return Math.round(n*p)/p;
  71. }
  72.  
  73. class Vector2 {
  74.     constructor(x=0, y=0) {
  75.         this.x = x;
  76.         this.y = y;
  77.     }
  78.    
  79.     static copy(v) {
  80.         return new Vector2(v.x, v.y);
  81.     }
  82.    
  83.     static add(v1, v2) {
  84.         return new Vector2(v1.x+v2.x, v1.y+v2.y);
  85.     }
  86.    
  87.     static sub(v1, v2) {
  88.         return new Vector2(v1.x-v2.x, v1.y-v2.y);
  89.     }
  90.    
  91.     add(v) {
  92.         this.x += v.x;
  93.         this.y += v.y;
  94.         return this;
  95.     }
  96.    
  97.     sub(v) {
  98.         this.x -= v.x;
  99.         this.y -= v.y;
  100.         return this;
  101.     }
  102.    
  103.     mult(s) {
  104.         this.x *= s;
  105.         this.y *= s;
  106.         return this;
  107.     }
  108.    
  109.     normalize() {
  110.         this.mult(1/this.magnitude);
  111.         return this;
  112.     }
  113.    
  114.     get magnitude() {
  115.         return Math.sqrt(this.x**2 + this.y**2);
  116.     }
  117.    
  118.     set magnitude(mag) {
  119.         this.normalize();
  120.         this.mult(mag);
  121.     }
  122.    
  123.     moveTo(v) {
  124.         this.x = v.x;
  125.         this.y = v.y;
  126.     }
  127.    
  128.     toString(d=100) {
  129.         return `(${roundTo(this.x, d)}, ${roundTo(this.y, d)})`;
  130.     }
  131. }
  132.  
  133. const rad = deg => deg*Math.PI / 180;
  134. const deg = rad => 180*rad / Math.PI;
  135.  
  136. function polar(...pt) {
  137.     let x,y;
  138.     if (pt.length == 1) {
  139.         x = pt[0].x;
  140.         y = pt[0].y;
  141.     } else {
  142.         x = pt[0];
  143.         y = pt[1];
  144.     }
  145.     let coord = [];
  146.     coord.r = Math.sqrt(x*x + y*y);
  147.     coord.theta = Math.atan(y,x);
  148.     return coord;
  149. }
  150.  
  151. function car(r,theta) {
  152.     let x,y;
  153.     x = r*Math.cos(theta);
  154.     y = r*Math.sin(theta);
  155.     return [x,y];
  156. }
  157.  
  158. function getAngles(points, degrees=false) {
  159.     let angs = [];
  160.     for (let i = 0; i < points.length; i++) {
  161.         let pt1 = points[i];
  162.         let pt2 = points[i-1] || new Vector2();
  163.        
  164.         let angle = polar(Vector2.sub(pt1, pt2)).theta;
  165.         if (degrees) angle = deg(angle.radians);
  166.        
  167.         angs.push(angle);
  168.     }
  169.     return angs;
  170. }
  171.  
  172. function getLengths(points) {
  173.     let lengths = [];
  174.     for (let i = 0; i < points.length; i++) {
  175.         let pt1 = points[i];
  176.         let pt2 = points[i-1] || new Vector2();
  177.        
  178.         let dist = polar(Vector2.sub(pt1, pt2)).r;
  179.         lengths.push(dist);
  180.     }
  181.     return lengths;
  182. }
  183.  
  184. class arm {
  185.     constructor(...points) {
  186.         this.points = points;
  187.         this.lengths = getLengths(this.points);
  188.         this.angles = getAngles(this.points);
  189.     }
  190.    
  191.     reach(target, iterations=1) {
  192.         let origin = Vector2.copy(this.points[0]);
  193.         for (let n = 0; n < iterations; n++) {
  194.             this.points[this.points.length-1].moveTo(target);
  195.             for (let i = this.points.length-2; i >= 0; i--) {
  196.                 this.points[i].moveTo(Vector2.sub(this.points[i],this.points[i+1]).normalize().mult(this.lengths[i+1]).add(this.points[i+1]));
  197.             }
  198.             this.points[0].moveTo(origin);
  199.             for (let i = 1; i < this.points.length; i++) {
  200.                 this.points[i].moveTo(Vector2.sub(this.points[i],this.points[i-1]).normalize().mult(this.lengths[i]).add(this.points[i-1]));
  201.             }
  202.         }
  203.        
  204.         this.updateAngles();
  205.     }
  206.    
  207.     updateAngles() {
  208.         this.angles = getAngles(this.points);
  209.     }
  210. }
  211.  
  212. let segs = []
  213. for (let i = 0; i < 30; i++) {
  214.     segs.push(new Vector2(i, 0));
  215. }
  216. let ikArm = new arm(...segs);
  217.  
  218. for (let i = 0; i < 100; i++) {
  219.     ikArm.reach(new Vector2(...car(Math.random()*ikArm.points.length, Math.random()*2*Math.PI)), 100);
  220. }
  221. console.group("Kinematic Arm");
  222. ikArm.points.forEach(pt => {
  223.     console.log(pt.toString(1000));
  224. });
  225. console.groupEnd();
  226. console.log(desmosify(ikArm.points, false, "Arm"));
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement