website

source code for the ttmaths.uk website
Log | Files | Refs | README

Cubic.jsx (2135B)


      1 import { useContext } from 'preact/hooks';
      2 import { PlotContext } from '../../scripts/contexts.js';
      3 import { getColourClass, getSymmetricBounds } from '../../scripts/graphUtils.js';
      4 
      5 function getDepressedCubicRoots(a, b, c, d) {
      6   // https://en.wikipedia.org/wiki/Cubic_equation#Depressed_cubic
      7   const p = (3*a*c - b*b) / (3*a*a);
      8   const q = (2*b*b*b - 9*a*b*c + 27*a*a*d) / (27*a*a*a);
      9   const discriminantish = q*q/4 + p*p*p/27;
     10 
     11   if (discriminantish > 0) {
     12     // https://en.wikipedia.org/wiki/Cubic_equation#Cardano's_formula
     13     const sqrt = Math.sqrt(discriminantish);
     14     const root = Math.cbrt(-q/2 + sqrt) + Math.cbrt(-q/2 - sqrt);
     15     return [root];
     16   }
     17 
     18   if (discriminantish === 0) {
     19     // https://en.wikipedia.org/wiki/Cubic_equation#Multiple_root
     20     if (p === 0) return [0,0,0];
     21     const doubleRoot = -3*q/(2*p);
     22     return [3*q/p, doubleRoot, doubleRoot];
     23   }
     24 
     25   // https://en.wikipedia.org/wiki/Cubic_equation#Trigonometric_and_hyperbolic_solutions
     26   const cosCoef = 2*Math.sqrt(-p/3);
     27   const arccosine = 1/3 * Math.acos(3*q/(2*p) * Math.sqrt(-3/p));
     28   const kCoef = 2*Math.PI / 3;
     29   return [0,1,2].map(k => cosCoef * Math.cos(arccosine - kCoef * k));
     30 }
     31 
     32 export default function Cubic({ a=1, b=0, c=0, d=0, colour="auto" }) {
     33   const p = useContext(PlotContext);
     34   // https://math.stackexchange.com/questions/3356084/to-construct-a-polynomial-using-b%C3%A9zier-curves
     35   // http://graphics.stanford.edu/courses/cs164-09-spring/Handouts/handout19.pdf
     36   function G(u, v, w) { return a*u*v*w + b*(v*w + w*u + u*v)/3 + c*(u + v + w)/3 + d; }
     37 
     38   const midX = -b / (3*a);
     39 
     40   const intercepts = [
     41     ...getDepressedCubicRoots(a,b,c,d - p.minY),
     42     ...getDepressedCubicRoots(a,b,c,d - p.maxY)
     43   ];
     44   const dx = Math.max(...intercepts.map(x => Math.abs(x)));
     45 
     46   const [i, f] = getSymmetricBounds(midX, dx, p);
     47 
     48   // https://math.stackexchange.com/questions/2414459/draw-cubic-polynomial-using-2d-cubic-bezier-curve
     49   const x1 = 2/3 * i + 1/3 * f;
     50   const x2 = 1/3 * i + 2/3 * f;
     51 
     52   d = `M ${i},${G(i,i,i)} C ${x1},${G(i,i,f)} ${x2},${G(i,f,f)} ${f},${G(f,f,f)}`;
     53   return <path d={d} class={getColourClass(colour)} />
     54 }