@hhenrichsen/motion-canvas-graphing
Version:
Some graphing components for Motion Canvas.
2 lines (1 loc) • 7.15 kB
JavaScript
import{initial as t,vector2Signal as i,canvasStyleSignal as e,signal as o,computed as s,Layout as r,resolveCanvasStyle as a,drawRect as l,parser as h,Line as n}from"@motion-canvas/2d";import{Vector2 as d,BBox as x,range as p,clamp as c,useLogger as y}from"@motion-canvas/core";function m(t,i,e,o){var s,r=arguments.length,a=r<3?i:null===o?o=Object.getOwnPropertyDescriptor(i,e):o;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)a=Reflect.decorate(t,i,e,o);else for(var l=t.length-1;l>=0;l--)(s=t[l])&&(a=(r<3?s(a):r>3?s(i,e,a):s(i,e))||a);return r>3&&a&&Object.defineProperty(i,e,a),a}class f extends r{labelFormatterX;labelFormatterY;edgePadding(){return this.labelSize().add(this.labelPadding()).add(this.tickLabelSize().mul([Math.log10(this.max().y)+1,2])).add(this.tickOverflow()).add(this.axisStrokeWidth())}constructor(t){super(t),this.labelFormatterX=t.labelFormatterX??(t=>t.toFixed(0)),this.labelFormatterY=t.labelFormatterY??(t=>t.toFixed(0))}cacheBBox(){return x.fromSizeCentered(this.size().add(this.edgePadding().mul(2)))}draw(t){const i=this.computedSize().mul(-.5);for(let e=0;e<=this.ticks().floored.x;e++){const o=i.add(this.computedSize().mul([e/this.ticks().x,1]));t.beginPath(),t.moveTo(o.x,o.y+this.tickOverflow().x+this.axisStrokeWidth().x/2+this.axisStrokeWidth().x/2),t.lineTo(o.x,i.y),t.strokeStyle=a(this.axisColorX(),t),t.lineWidth=this.gridStrokeWidth().x,t.stroke(),t.fillStyle=a(this.axisTextColorX(),t),t.font=`${this.tickLabelSize().y}px sans-serif`,t.textAlign="center",t.textBaseline="top",t.fillText(`${this.labelFormatterX(this.mapToX(e/this.ticks().x))}`,o.x,o.y+this.axisStrokeWidth().x+this.tickOverflow().x+Math.floor(this.tickPadding().x/2))}for(let e=0;e<=this.ticks().floored.y;e++){const o=i.add(this.computedSize().mul([1,1-e/this.ticks().y]));t.beginPath(),t.moveTo(o.x,o.y),t.lineTo(i.x-this.tickOverflow().y,o.y),t.strokeStyle=a(this.axisColorY(),t),t.lineWidth=this.gridStrokeWidth().y,t.stroke(),t.fillStyle=a(this.axisTextColorY(),t),t.font=`${this.tickLabelSize().y}px ${this.fontFamily()}`,t.textAlign="right",t.textBaseline="middle",t.fillText(`${this.labelFormatterY(this.mapToY(e/this.ticks().y))}`,i.x-this.axisStrokeWidth().y-this.tickOverflow().y-Math.floor(this.tickPadding().y/2),o.y)}t.beginPath();const e=this.getPointFromPlotSpace([0,this.min().y]),o=this.getPointFromPlotSpace([0,this.max().y]);t.moveTo(e.x-this.gridStrokeWidth().y/2,e.y-this.gridStrokeWidth().y/2),t.lineTo(o.x-this.gridStrokeWidth().y/2,o.y+this.gridStrokeWidth().y/2),t.strokeStyle=a(this.axisColorX(),t),t.lineWidth=this.axisStrokeWidth().x,t.stroke(),t.beginPath();const s=this.getPointFromPlotSpace([this.min().x,0]),r=this.getPointFromPlotSpace([this.max().x,0]);t.moveTo(s.x-this.gridStrokeWidth().x/2,s.y+this.gridStrokeWidth().x/2),t.lineTo(r.x+this.gridStrokeWidth().x/2,r.y+this.gridStrokeWidth().x/2),t.strokeStyle=a(this.axisColorY(),t),t.lineWidth=this.axisStrokeWidth().y,t.stroke(),t.fillStyle=a(this.axisTextColorX(),t),t.font=`${this.labelSize().y}px ${this.fontFamily()}`,t.textAlign="center",t.textBaseline="alphabetic",t.fillText(this.labelX(),0,-i.y+this.axisStrokeWidth().x+this.tickOverflow().x+this.tickLabelSize().x+this.tickPadding().x+Math.floor(this.labelPadding().x)+this.labelSize().x),t.fillStyle=a(this.axisTextColorY(),t),t.font=`${this.labelSize().y}px ${this.fontFamily()}`,t.textAlign="center",t.textBaseline="alphabetic",t.save(),t.translate(i.x-this.axisStrokeWidth().y-this.tickOverflow().y-this.tickLabelSize().y-this.tickPadding().y-Math.floor(this.labelPadding().y/2)-this.labelSize().y,0),t.rotate(-Math.PI/2),t.fillText(this.labelY(),0,0),t.restore(),this.clip()&&t.clip(this.getPath()),this.drawChildren(t)}getPath(){const t=new Path2D,i=x.fromSizeCentered(this.size());return l(t,i),t}getPointFromPlotSpace(t){const i=this.computedSize().mul([-.5,.5]);return this.toRelativeGridSize(t).mul([1,-1]).mul(this.computedSize()).add(i)}mapToX(t){return this.min().x+t*(this.max().x-this.min().x)}mapToY(t){return this.min().y+t*(this.max().y-this.min().y)}toRelativeGridSize(t){return new d(t).sub(this.min()).div(this.max().sub(this.min()))}makeGraphData(t,i){return p(this.min().x,this.max().x+t,t).map((t=>[t,i(t)]))}}m([t(d.zero),i("min")],f.prototype,"min",void 0),m([t(d.one.mul(100)),i("max")],f.prototype,"max",void 0),m([t(d.one.mul(10)),i("ticks")],f.prototype,"ticks",void 0),m([t(d.one.mul(30)),i("labelSize")],f.prototype,"labelSize",void 0),m([t(d.one.mul(5)),i("labelPadding")],f.prototype,"labelPadding",void 0),m([t(d.one.mul(10)),i("tickLabelSize")],f.prototype,"tickLabelSize",void 0),m([t(d.one.mul(5)),i("tickOverflow")],f.prototype,"tickOverflow",void 0),m([t(d.one.mul(6)),i("tickPadding")],f.prototype,"tickPadding",void 0),m([t(d.one.mul(1)),i("gridStrokeWidth")],f.prototype,"gridStrokeWidth",void 0),m([t(d.one.mul(2)),i("axisStrokeWidth")],f.prototype,"axisStrokeWidth",void 0),m([t("white"),e()],f.prototype,"axisColorX",void 0),m([t("white"),e()],f.prototype,"axisTextColorX",void 0),m([t(""),o()],f.prototype,"labelX",void 0),m([t("white"),e()],f.prototype,"axisColorY",void 0),m([t("white"),e()],f.prototype,"axisTextColorY",void 0),m([t(""),o()],f.prototype,"labelY",void 0),m([s()],f.prototype,"edgePadding",null);class g extends r{firstIndex(){return Math.ceil(this.data().length*this.start()+1)}firstPointProgress(){return this.firstIndex()-this.start()*this.data().length}lastIndex(){return Math.floor(this.data().length*this.end()-1)}pointProgress(){return this.end()*this.data().length-this.lastIndex()}constructor(t){super({...t})}draw(t){t.save(),t.fillStyle=a(this.pointColor(),t);const i=this.parent();if(!(i instanceof f))return void y().warn("Using a ScatterPlot outside of a Plot does nothing");if(this.firstIndex()<this.lastIndex()){const e=this.data()[this.firstIndex()-1],o=i.getPointFromPlotSpace(e);t.beginPath(),t.arc(o.x,o.y,this.pointRadius()*this.firstPointProgress(),0,2*Math.PI),t.fill()}const e=this.data();if(e.slice(this.firstIndex(),this.lastIndex()).forEach((e=>{const o=i.getPointFromPlotSpace(e);t.beginPath(),t.arc(o.x,o.y,this.pointRadius(),0,2*Math.PI),t.fill()})),this.lastIndex()>this.firstIndex()){const o=e[this.lastIndex()],s=i.getPointFromPlotSpace(o);t.beginPath(),t.arc(s.x,s.y,this.pointRadius()*this.pointProgress(),0,2*Math.PI),t.fill()}t.restore()}}m([t(5),o()],g.prototype,"pointRadius",void 0),m([t("white"),e()],g.prototype,"pointColor",void 0),m([o()],g.prototype,"data",void 0),m([t(0),h((t=>c(0,1,t))),o()],g.prototype,"start",void 0),m([t(1),h((t=>c(0,1,t))),o()],g.prototype,"end",void 0),m([s()],g.prototype,"firstIndex",null),m([s()],g.prototype,"firstPointProgress",null),m([s()],g.prototype,"lastIndex",null),m([s()],g.prototype,"pointProgress",null);class P extends n{constructor(t){super({...t,points:t.data})}parsedPoints(){const t=this.parent();if(!(t instanceof f))return y().warn("Using a LinePlot outside of a Plot is the same as a Line"),super.parsedPoints();return this.data().map((i=>t.getPointFromPlotSpace(i)))}childrenBBox(){return x.fromPoints(...this.parsedPoints())}}m([o()],P.prototype,"data",void 0);export{P as LinePlot,f as Plot,g as ScatterPlot};