From baf872fdf2df8f8eae02dfbb4ca1bb6913983c8f Mon Sep 17 00:00:00 2001 From: HGimself Date: Sun, 28 Nov 2021 15:38:46 -0500 Subject: [PATCH 1/3] added lots of changes to the circular bits --- www/charts/circular.js | 57 +++++++++++++++++++++------- www/components/Circular.jsx | 74 +++++++++++++++++++++++++++++++------ www/containers/Circular.jsx | 2 +- www/public/gif.js | 3 ++ www/public/gif.worker.js | 3 ++ www/utils/frontend-tools.js | 12 ++++++ 6 files changed, 125 insertions(+), 26 deletions(-) create mode 100644 www/public/gif.js create mode 100644 www/public/gif.worker.js diff --git a/www/charts/circular.js b/www/charts/circular.js index 6fb54413..63a156dd 100644 --- a/www/charts/circular.js +++ b/www/charts/circular.js @@ -2,26 +2,33 @@ import * as d3 from "d3" import theme from "../theme" import { simpleHarmonicMotionCos, simpleHarmonicMotionSin } from "../utils/maths-tools.js" +import { getSpectrumPosition } from "../utils/color-tools.js" class Circular { constructor(containerEl, props) { this.containerEl = containerEl this.props = props - const { width, height } = props + const { width, height, count } = props + + const data = Array.from({ length: count }, (_, i) => i) + // give us a canvas to draw on this.svg = d3.select(containerEl) .append('svg') .attr('width', width) .attr('height', height) + // background + // this.svg.append("rect") + // .attr('width', "100%") + // .attr('height', "100%") + // .attr('fill', 'blue') // bring in a line - this.svg.selectAll('path.lines') - .data([0]) + this.svg.selectAll('path') + .data(data) .enter() .append("path") - .attr("class", "lines") .attr("fill", "none") - .attr("stroke", theme.colors.black) .attr("stroke-width", "1") // draw with the line this.update() @@ -35,7 +42,7 @@ class Circular { props.width = width props.height = height - props.amplitude = width * 0.4 > 300 ? 300 : width * 0.4 + // props.amplitude = width * 0.4 > 300 ? 300 : width * 0.4 this.update() } @@ -51,7 +58,19 @@ class Circular { } setCount(count) { + const { svg, props } = this this.props.count = count + + const data = Array.from({ length: count }, (_, i) => i) + + svg.selectAll('path') + .data(data) + .join( + enter => enter + .append("path") + .attr("fill", "none") + .attr("stroke-width", "1"), + ) this.update() } @@ -68,19 +87,24 @@ class Circular { setColor(color) { const { svg } = this - svg.selectAll('path.lines') + svg.selectAll('path') .attr("fill", color) } - getDrawer() { + setSpectrum(spectrum) { + this.props.spectrum = spectrum + this.update() + } + + getDrawer(batch) { const { mode, count, amplitude, offset, frequency, multiplierX, multiplierY, width, height } = this.props const originX = (width/2) const originY = (height/2) - const arc = Array.from({ length: count }, (_, i) => [ - simpleHarmonicMotionSin(originX, amplitude, multiplierX * frequency, i - offset), - simpleHarmonicMotionCos(originY, amplitude, multiplierY * frequency, i - offset) + const arc = Array.from({ length: 1 + 1 }, (_, i) => [ + simpleHarmonicMotionSin(originX, amplitude, multiplierX * frequency, (i + batch) - offset), + simpleHarmonicMotionCos(originY, amplitude, multiplierY * frequency, (i + batch) - offset) ]) return d3.line()(arc) @@ -89,9 +113,14 @@ class Circular { update() { const { svg } = this - const drawer = this.getDrawer() - svg.selectAll('path.lines') - .attr("d", drawer) + svg.selectAll('path') + .attr("d", d => this.getDrawer(d)) + .attr("stroke", d => getSpectrumPosition(this.props.spectrum + (d/(this.props.count * 0.4)))) + } + + getSvg() { + const { svg } = this + return svg } } diff --git a/www/components/Circular.jsx b/www/components/Circular.jsx index 616a8648..1fa8207f 100644 --- a/www/components/Circular.jsx +++ b/www/components/Circular.jsx @@ -4,43 +4,65 @@ import { css } from "@emotion/css" import circular from "../charts/circular.js" import theme from "../theme" import { gcd } from "../utils/maths-tools.js" -import { copyToClipboard } from "../utils/frontend-tools.js" +import { copyToClipboard, imgFromSvg } from "../utils/frontend-tools.js" import Animator from "./Animator.jsx" import Button from "./Button.jsx" import FlexRow from "./FlexRow.jsx" import Switch from "./Switch.jsx" +// const GIF = require("../utils/gif.js") + let vis = null; const setVis = (v) => { vis = v } +const gif = new GIF({ + workers: 7, + quality: 1, + background: "#fff" +}) + +gif.on('finished', function(blob) { + window.open(URL.createObjectURL(blob)); +}); + +/* +origin + (amplitude * f(time * (frequency * (2 * 3.14)))) +(600/2) + (300 * sin(t * (283/53) * 2pi))) +(600/2) + (300 * cos(t * (274/53) * 2pi))) + +(600/2) + (300 * sin(t * (1/1) * 2pi))) +*/ + export default function Circular( props ) { const time = 10 - const step = 5 + const step = 1 const limit = 1000 const defaultColor = 'transparent' const sliderMin = 0 const sliderMax = 1974 const [color, setColorState] = useState(props.color || defaultColor) + const [spectrum, setSpectrumState] = useState(props.s || 1) const [multiplierX, setMultiplierXState] = useState(props.x || 1) const [multiplierY, setMultiplierYState] = useState(props.y || 1) const [period, setPeriodState] = useState(props.p || 1) - const [count, setCount] = useState(props.count || 200) + const [count, setCount] = useState(props.c || 1000) const [running, setRunningState] = useState(false) const [offset, setOffsetState] = useState(0) const options = { count, - height: 600, - width: 1300, + height: 800, + width: 1400, offset, - amplitude: 300, + amplitude: 400, frequency: 1 / period, multiplierY, multiplierX, + spectrum, } // ref to stick into the timer @@ -48,6 +70,11 @@ export default function Circular( props ) { runningRef.current = running const toggleRunning = () => setRunningState(!running) + const offsetRef = useRef() + offsetRef.current = offset + + // useEffect(addFrame) + const bumpOffset = (offset) => { const off = (offset + step) % sliderMax // const off = (offset + step) @@ -60,6 +87,13 @@ export default function Circular( props ) { setOffsetState(bumpOffset) } + const addFrame = () => { + console.log({m: "adding frame", offset: offsetRef.current}) + const callback = offsetRef.current >= 1960 ? () => {} : addFrame + setOffsetState(bumpOffset) + imgFromSvg(gif, vis.getSvg(), callback) + } + const startOrStopButton = running ? : @@ -89,7 +123,7 @@ export default function Circular( props ) { } const shareHandler = () => { - copyToClipboard(`http://${window.location.host}/Circular?x=${multiplierX}&y=${multiplierY}&p=${period}`) + copyToClipboard(`http://${window.location.host}/RadialCartesian?x=${multiplierX}&y=${multiplierY}&p=${period}&s=${spectrum}&c=${count}`) } const setColorHandler = (type) => (_e, newState) => { @@ -116,6 +150,12 @@ export default function Circular( props ) { setOffsetState(multiplierInput) } + const setSpectrumHandler = ({target}) => { + const spectrumInput = +target.value + vis.setSpectrum(spectrumInput) + setSpectrumState(spectrumInput) + } + const makeSwitch = (type, i) => RANDOMIZE - - {types.map(makeSwitch)} - - + { + // + // + // + // {types.map(makeSwitch)} + // + } + + + + + diff --git a/www/containers/Circular.jsx b/www/containers/Circular.jsx index 4d1ccf5f..010c72ce 100644 --- a/www/containers/Circular.jsx +++ b/www/containers/Circular.jsx @@ -12,7 +12,7 @@ export default function CircularContainer(props) { <>

Radial Cartesian

Draw Squiggles with polar coordinates and simple harmonic motion. Experiment with the values and see what happens.

- + ) } diff --git a/www/public/gif.js b/www/public/gif.js new file mode 100644 index 00000000..2e4d2042 --- /dev/null +++ b/www/public/gif.js @@ -0,0 +1,3 @@ +// gif.js 0.2.0 - https://github.com/jnordberg/gif.js +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.GIF=f()}})(function(){var define,module,exports;return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o0&&this._events[type].length>m){this._events[type].warned=true;console.error("(node) warning: possible EventEmitter memory "+"leak detected. %d listeners added. "+"Use emitter.setMaxListeners() to increase limit.",this._events[type].length);if(typeof console.trace==="function"){console.trace()}}}return this};EventEmitter.prototype.on=EventEmitter.prototype.addListener;EventEmitter.prototype.once=function(type,listener){if(!isFunction(listener))throw TypeError("listener must be a function");var fired=false;function g(){this.removeListener(type,g);if(!fired){fired=true;listener.apply(this,arguments)}}g.listener=listener;this.on(type,g);return this};EventEmitter.prototype.removeListener=function(type,listener){var list,position,length,i;if(!isFunction(listener))throw TypeError("listener must be a function");if(!this._events||!this._events[type])return this;list=this._events[type];length=list.length;position=-1;if(list===listener||isFunction(list.listener)&&list.listener===listener){delete this._events[type];if(this._events.removeListener)this.emit("removeListener",type,listener)}else if(isObject(list)){for(i=length;i-- >0;){if(list[i]===listener||list[i].listener&&list[i].listener===listener){position=i;break}}if(position<0)return this;if(list.length===1){list.length=0;delete this._events[type]}else{list.splice(position,1)}if(this._events.removeListener)this.emit("removeListener",type,listener)}return this};EventEmitter.prototype.removeAllListeners=function(type){var key,listeners;if(!this._events)return this;if(!this._events.removeListener){if(arguments.length===0)this._events={};else if(this._events[type])delete this._events[type];return this}if(arguments.length===0){for(key in this._events){if(key==="removeListener")continue;this.removeAllListeners(key)}this.removeAllListeners("removeListener");this._events={};return this}listeners=this._events[type];if(isFunction(listeners)){this.removeListener(type,listeners)}else if(listeners){while(listeners.length)this.removeListener(type,listeners[listeners.length-1])}delete this._events[type];return this};EventEmitter.prototype.listeners=function(type){var ret;if(!this._events||!this._events[type])ret=[];else if(isFunction(this._events[type]))ret=[this._events[type]];else ret=this._events[type].slice();return ret};EventEmitter.prototype.listenerCount=function(type){if(this._events){var evlistener=this._events[type];if(isFunction(evlistener))return 1;else if(evlistener)return evlistener.length}return 0};EventEmitter.listenerCount=function(emitter,type){return emitter.listenerCount(type)};function isFunction(arg){return typeof arg==="function"}function isNumber(arg){return typeof arg==="number"}function isObject(arg){return typeof arg==="object"&&arg!==null}function isUndefined(arg){return arg===void 0}},{}],2:[function(require,module,exports){var UA,browser,mode,platform,ua;ua=navigator.userAgent.toLowerCase();platform=navigator.platform.toLowerCase();UA=ua.match(/(opera|ie|firefox|chrome|version)[\s\/:]([\w\d\.]+)?.*?(safari|version[\s\/:]([\w\d\.]+)|$)/)||[null,"unknown",0];mode=UA[1]==="ie"&&document.documentMode;browser={name:UA[1]==="version"?UA[3]:UA[1],version:mode||parseFloat(UA[1]==="opera"&&UA[4]?UA[4]:UA[2]),platform:{name:ua.match(/ip(?:ad|od|hone)/)?"ios":(ua.match(/(?:webos|android)/)||platform.match(/mac|win|linux/)||["other"])[0]}};browser[browser.name]=true;browser[browser.name+parseInt(browser.version,10)]=true;browser.platform[browser.platform.name]=true;module.exports=browser},{}],3:[function(require,module,exports){var EventEmitter,GIF,browser,extend=function(child,parent){for(var key in parent){if(hasProp.call(parent,key))child[key]=parent[key]}function ctor(){this.constructor=child}ctor.prototype=parent.prototype;child.prototype=new ctor;child.__super__=parent.prototype;return child},hasProp={}.hasOwnProperty,indexOf=[].indexOf||function(item){for(var i=0,l=this.length;iref;i=0<=ref?++j:--j){results.push(null)}return results}.call(this);numWorkers=this.spawnWorkers();if(this.options.globalPalette===true){this.renderNextFrame()}else{for(i=j=0,ref=numWorkers;0<=ref?jref;i=0<=ref?++j:--j){this.renderNextFrame()}}this.emit("start");return this.emit("progress",0)};GIF.prototype.abort=function(){var worker;while(true){worker=this.activeWorkers.shift();if(worker==null){break}this.log("killing active worker");worker.terminate()}this.running=false;return this.emit("abort")};GIF.prototype.spawnWorkers=function(){var j,numWorkers,ref,results;numWorkers=Math.min(this.options.workers,this.frames.length);(function(){results=[];for(var j=ref=this.freeWorkers.length;ref<=numWorkers?jnumWorkers;ref<=numWorkers?j++:j--){results.push(j)}return results}).apply(this).forEach(function(_this){return function(i){var worker;_this.log("spawning worker "+i);worker=new Worker(_this.options.workerScript);worker.onmessage=function(event){_this.activeWorkers.splice(_this.activeWorkers.indexOf(worker),1);_this.freeWorkers.push(worker);return _this.frameFinished(event.data)};return _this.freeWorkers.push(worker)}}(this));return numWorkers};GIF.prototype.frameFinished=function(frame){var i,j,ref;this.log("frame "+frame.index+" finished - "+this.activeWorkers.length+" active");this.finishedFrames++;this.emit("progress",this.finishedFrames/this.frames.length);this.imageParts[frame.index]=frame;if(this.options.globalPalette===true){this.options.globalPalette=frame.globalPalette;this.log("global palette analyzed");if(this.frames.length>2){for(i=j=1,ref=this.freeWorkers.length;1<=ref?jref;i=1<=ref?++j:--j){this.renderNextFrame()}}}if(indexOf.call(this.imageParts,null)>=0){return this.renderNextFrame()}else{return this.finishRendering()}};GIF.prototype.finishRendering=function(){var data,frame,i,image,j,k,l,len,len1,len2,len3,offset,page,ref,ref1,ref2;len=0;ref=this.imageParts;for(j=0,len1=ref.length;j=this.frames.length){return}frame=this.frames[this.nextFrame++];worker=this.freeWorkers.shift();task=this.getTask(frame);this.log("starting frame "+(task.index+1)+" of "+this.frames.length);this.activeWorkers.push(worker);return worker.postMessage(task)};GIF.prototype.getContextData=function(ctx){return ctx.getImageData(0,0,this.options.width,this.options.height).data};GIF.prototype.getImageData=function(image){var ctx;if(this._canvas==null){this._canvas=document.createElement("canvas");this._canvas.width=this.options.width;this._canvas.height=this.options.height}ctx=this._canvas.getContext("2d");ctx.setFill=this.options.background;ctx.fillRect(0,0,this.options.width,this.options.height);ctx.drawImage(image,0,0);return this.getContextData(ctx)};GIF.prototype.getTask=function(frame){var index,task;index=this.frames.indexOf(frame);task={index:index,last:index===this.frames.length-1,delay:frame.delay,transparent:frame.transparent,width:this.options.width,height:this.options.height,quality:this.options.quality,dither:this.options.dither,globalPalette:this.options.globalPalette,repeat:this.options.repeat,canTransfer:browser.name==="chrome"};if(frame.data!=null){task.data=frame.data}else if(frame.context!=null){task.data=this.getContextData(frame.context)}else if(frame.image!=null){task.data=this.getImageData(frame.image)}else{throw new Error("Invalid frame")}return task};GIF.prototype.log=function(){var args;args=1<=arguments.length?slice.call(arguments,0):[];if(!this.options.debug){return}return console.log.apply(console,args)};return GIF}(EventEmitter);module.exports=GIF},{"./browser.coffee":2,events:1}]},{},[3])(3)}); +//# sourceMappingURL=gif.js.map diff --git a/www/public/gif.worker.js b/www/public/gif.worker.js new file mode 100644 index 00000000..269624e6 --- /dev/null +++ b/www/public/gif.worker.js @@ -0,0 +1,3 @@ +// gif.worker.js 0.2.0 - https://github.com/jnordberg/gif.js +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o=ByteArray.pageSize)this.newPage();this.pages[this.page][this.cursor++]=val};ByteArray.prototype.writeUTFBytes=function(string){for(var l=string.length,i=0;i=0)this.dispose=disposalCode};GIFEncoder.prototype.setRepeat=function(repeat){this.repeat=repeat};GIFEncoder.prototype.setTransparent=function(color){this.transparent=color};GIFEncoder.prototype.addFrame=function(imageData){this.image=imageData;this.colorTab=this.globalPalette&&this.globalPalette.slice?this.globalPalette:null;this.getImagePixels();this.analyzePixels();if(this.globalPalette===true)this.globalPalette=this.colorTab;if(this.firstFrame){this.writeLSD();this.writePalette();if(this.repeat>=0){this.writeNetscapeExt()}}this.writeGraphicCtrlExt();this.writeImageDesc();if(!this.firstFrame&&!this.globalPalette)this.writePalette();this.writePixels();this.firstFrame=false};GIFEncoder.prototype.finish=function(){this.out.writeByte(59)};GIFEncoder.prototype.setQuality=function(quality){if(quality<1)quality=1;this.sample=quality};GIFEncoder.prototype.setDither=function(dither){if(dither===true)dither="FloydSteinberg";this.dither=dither};GIFEncoder.prototype.setGlobalPalette=function(palette){this.globalPalette=palette};GIFEncoder.prototype.getGlobalPalette=function(){return this.globalPalette&&this.globalPalette.slice&&this.globalPalette.slice(0)||this.globalPalette};GIFEncoder.prototype.writeHeader=function(){this.out.writeUTFBytes("GIF89a")};GIFEncoder.prototype.analyzePixels=function(){if(!this.colorTab){this.neuQuant=new NeuQuant(this.pixels,this.sample);this.neuQuant.buildColormap();this.colorTab=this.neuQuant.getColormap()}if(this.dither){this.ditherPixels(this.dither.replace("-serpentine",""),this.dither.match(/-serpentine/)!==null)}else{this.indexPixels()}this.pixels=null;this.colorDepth=8;this.palSize=7;if(this.transparent!==null){this.transIndex=this.findClosest(this.transparent,true)}};GIFEncoder.prototype.indexPixels=function(imgq){var nPix=this.pixels.length/3;this.indexedPixels=new Uint8Array(nPix);var k=0;for(var j=0;j=0&&x1+x=0&&y1+y>16,(c&65280)>>8,c&255,used)};GIFEncoder.prototype.findClosestRGB=function(r,g,b,used){if(this.colorTab===null)return-1;if(this.neuQuant&&!used){return this.neuQuant.lookupRGB(r,g,b)}var c=b|g<<8|r<<16;var minpos=0;var dmin=256*256*256;var len=this.colorTab.length;for(var i=0,index=0;i=0){disp=dispose&7}disp<<=2;this.out.writeByte(0|disp|0|transp);this.writeShort(this.delay);this.out.writeByte(this.transIndex);this.out.writeByte(0)};GIFEncoder.prototype.writeImageDesc=function(){this.out.writeByte(44);this.writeShort(0);this.writeShort(0);this.writeShort(this.width);this.writeShort(this.height);if(this.firstFrame||this.globalPalette){this.out.writeByte(0)}else{this.out.writeByte(128|0|0|0|this.palSize)}};GIFEncoder.prototype.writeLSD=function(){this.writeShort(this.width);this.writeShort(this.height);this.out.writeByte(128|112|0|this.palSize);this.out.writeByte(0);this.out.writeByte(0)};GIFEncoder.prototype.writeNetscapeExt=function(){this.out.writeByte(33);this.out.writeByte(255);this.out.writeByte(11);this.out.writeUTFBytes("NETSCAPE2.0");this.out.writeByte(3);this.out.writeByte(1);this.writeShort(this.repeat);this.out.writeByte(0)};GIFEncoder.prototype.writePalette=function(){this.out.writeBytes(this.colorTab);var n=3*256-this.colorTab.length;for(var i=0;i>8&255)};GIFEncoder.prototype.writePixels=function(){var enc=new LZWEncoder(this.width,this.height,this.indexedPixels,this.colorDepth);enc.encode(this.out)};GIFEncoder.prototype.stream=function(){return this.out};module.exports=GIFEncoder},{"./LZWEncoder.js":2,"./TypedNeuQuant.js":3}],2:[function(require,module,exports){var EOF=-1;var BITS=12;var HSIZE=5003;var masks=[0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535];function LZWEncoder(width,height,pixels,colorDepth){var initCodeSize=Math.max(2,colorDepth);var accum=new Uint8Array(256);var htab=new Int32Array(HSIZE);var codetab=new Int32Array(HSIZE);var cur_accum,cur_bits=0;var a_count;var free_ent=0;var maxcode;var clear_flg=false;var g_init_bits,ClearCode,EOFCode;function char_out(c,outs){accum[a_count++]=c;if(a_count>=254)flush_char(outs)}function cl_block(outs){cl_hash(HSIZE);free_ent=ClearCode+2;clear_flg=true;output(ClearCode,outs)}function cl_hash(hsize){for(var i=0;i=0){disp=hsize_reg-i;if(i===0)disp=1;do{if((i-=disp)<0)i+=hsize_reg;if(htab[i]===fcode){ent=codetab[i];continue outer_loop}}while(htab[i]>=0)}output(ent,outs);ent=c;if(free_ent<1<0){outs.writeByte(a_count);outs.writeBytes(accum,0,a_count);a_count=0}}function MAXCODE(n_bits){return(1<0)cur_accum|=code<=8){char_out(cur_accum&255,outs);cur_accum>>=8;cur_bits-=8}if(free_ent>maxcode||clear_flg){if(clear_flg){maxcode=MAXCODE(n_bits=g_init_bits);clear_flg=false}else{++n_bits;if(n_bits==BITS)maxcode=1<0){char_out(cur_accum&255,outs);cur_accum>>=8;cur_bits-=8}flush_char(outs)}}this.encode=encode}module.exports=LZWEncoder},{}],3:[function(require,module,exports){var ncycles=100;var netsize=256;var maxnetpos=netsize-1;var netbiasshift=4;var intbiasshift=16;var intbias=1<>betashift;var betagamma=intbias<>3;var radiusbiasshift=6;var radiusbias=1<>3);var i,v;for(i=0;i>=netbiasshift;network[i][1]>>=netbiasshift;network[i][2]>>=netbiasshift;network[i][3]=i}}function altersingle(alpha,i,b,g,r){network[i][0]-=alpha*(network[i][0]-b)/initalpha;network[i][1]-=alpha*(network[i][1]-g)/initalpha;network[i][2]-=alpha*(network[i][2]-r)/initalpha}function alterneigh(radius,i,b,g,r){var lo=Math.abs(i-radius);var hi=Math.min(i+radius,netsize);var j=i+1;var k=i-1;var m=1;var p,a;while(jlo){a=radpower[m++];if(jlo){p=network[k--];p[0]-=a*(p[0]-b)/alpharadbias;p[1]-=a*(p[1]-g)/alpharadbias;p[2]-=a*(p[2]-r)/alpharadbias}}}function contest(b,g,r){var bestd=~(1<<31);var bestbiasd=bestd;var bestpos=-1;var bestbiaspos=bestpos;var i,n,dist,biasdist,betafreq;for(i=0;i>intbiasshift-netbiasshift);if(biasdist>betashift;freq[i]-=betafreq;bias[i]+=betafreq<>1;for(j=previouscol+1;j>1;for(j=previouscol+1;j<256;j++)netindex[j]=maxnetpos}function inxsearch(b,g,r){var a,p,dist;var bestd=1e3;var best=-1;var i=netindex[g];var j=i-1;while(i=0){if(i=bestd)i=netsize;else{i++;if(dist<0)dist=-dist;a=p[0]-b;if(a<0)a=-a;dist+=a;if(dist=0){p=network[j];dist=g-p[1];if(dist>=bestd)j=-1;else{j--;if(dist<0)dist=-dist;a=p[0]-b;if(a<0)a=-a;dist+=a;if(dist>radiusbiasshift;if(rad<=1)rad=0;for(i=0;i=lengthcount)pix-=lengthcount;i++;if(delta===0)delta=1;if(i%delta===0){alpha-=alpha/alphadec;radius-=radius/radiusdec;rad=radius>>radiusbiasshift;if(rad<=1)rad=0;for(j=0;j { document.execCommand('copy'); window.removeEventListener('copy', copy); } + +export const imgFromSvg = (gif, vector, cb) => { + const img = new Image() + const serialized = new XMLSerializer().serializeToString(vector.node()) + const svg = new Blob([serialized], {type: "image/svg+xml"}) + const url = URL.createObjectURL(svg) + img.onload = () => { + gif.addFrame(img, {delay: 1}) + cb() + } + img.src = url +} From 7adc245e6807240681d78f46892fd38e8ebec5a5 Mon Sep 17 00:00:00 2001 From: HGimself Date: Sun, 28 Nov 2021 15:40:15 -0500 Subject: [PATCH 2/3] forgot some fixes --- www/components/Circular.jsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/www/components/Circular.jsx b/www/components/Circular.jsx index 1fa8207f..37c2267b 100644 --- a/www/components/Circular.jsx +++ b/www/components/Circular.jsx @@ -16,15 +16,15 @@ import Switch from "./Switch.jsx" let vis = null; const setVis = (v) => { vis = v } -const gif = new GIF({ - workers: 7, - quality: 1, - background: "#fff" -}) - -gif.on('finished', function(blob) { - window.open(URL.createObjectURL(blob)); -}); +// const gif = new GIF({ +// workers: 7, +// quality: 1, +// background: "#fff" +// }) +// +// gif.on('finished', function(blob) { +// window.open(URL.createObjectURL(blob)); +// }); /* origin + (amplitude * f(time * (frequency * (2 * 3.14)))) From 39b91136693101edf968bafe587b0ec91bf878e1 Mon Sep 17 00:00:00 2001 From: HGimself Date: Thu, 2 Dec 2021 17:04:32 -0500 Subject: [PATCH 3/3] saving the savers place --- .gitignore | 5 +++ server/src/main.rs | 45 +++++++++++++++++---- www/App.jsx | 7 ++-- www/charts/circular.js | 10 ++--- www/components/Circular.jsx | 79 ++++++++++++++++++++++++++++++------- www/public/index.html | 1 + 6 files changed, 116 insertions(+), 31 deletions(-) diff --git a/.gitignore b/.gitignore index 0d70285d..d9578c81 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,12 @@ www/node_modules www/dist +server/svgs + target articles dist .env +.Ds_Store + +svgs* diff --git a/server/src/main.rs b/server/src/main.rs index 439f3345..9c19932a 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -5,8 +5,8 @@ use log::info; use serde_derive::{Deserialize, Serialize}; use std::{ env, - fs::{self, DirEntry}, - io, + fs::{self, DirEntry, File}, + io::{self, Write}, net::SocketAddr, path::Path, }; @@ -17,6 +17,16 @@ struct Dada { message: String, } +#[derive(Deserialize, Serialize)] +struct Svg { + x: i32, + y: i32, + period: i32, + count: i32, + spectrum: i32, + svg: String, +} + #[tokio::main] async fn main() { env_logger::Builder::from_env(Env::default().default_filter_or("info")).init(); @@ -66,17 +76,36 @@ async fn main() { }); let dada = warp::options() - .and(warp::path!("dada")) + .and(warp::path!("svg")) .map(|| warp::reply()) .or(warp::post() - .and(warp::path!("dada")) - .and(warp::body::content_length_limit(1024 * 16)) + .and(warp::path!("svg")) + .and(warp::body::content_length_limit(1024 * 256)) .and(warp::body::json()) - .map(|dada: Dada| { - let res = dada_poem_generator::dada(&dada.message); - warp::reply::html(res) + .map(|svg: Svg| { + let s = format!( + "./svgs/{}_{}_{}_{}_{}.svg", + svg.x, svg.y, svg.period, svg.count, svg.spectrum + ); + let mut file = File::create(s.clone()).unwrap(); + println!("{}", s); + write!(file, "{}", svg.svg).unwrap(); + warp::reply::html(s) })) .with(with_content_allow); + // + // let dada = warp::options() + // .and(warp::path!("dada")) + // .map(|| warp::reply()) + // .or(warp::post() + // .and(warp::path!("dada")) + // .and(warp::body::content_length_limit(1024 * 16)) + // .and(warp::body::json()) + // .map(|dada: Dada| { + // let res = dada_poem_generator::dada(&dada.message); + // warp::reply::html(res) + // })) + // .with(with_content_allow); let end = home .or(dada.or(blog_home.or(blog_page)).with(with_control_origin)) diff --git a/www/App.jsx b/www/App.jsx index 148f57b5..22688ba5 100644 --- a/www/App.jsx +++ b/www/App.jsx @@ -77,15 +77,16 @@ export default function App( props ) { - - - + + + + diff --git a/www/charts/circular.js b/www/charts/circular.js index 63a156dd..cdea8ff6 100644 --- a/www/charts/circular.js +++ b/www/charts/circular.js @@ -37,12 +37,12 @@ class Circular { resize(width, height) { const { svg, props } = this - svg.attr('width', width) - .attr('height', height) - props.width = width - props.height = height - // props.amplitude = width * 0.4 > 300 ? 300 : width * 0.4 + props.height = width * 0.4 > 300 ? 600 : 300 + props.amplitude = width * 0.4 > 300 ? 300 : width * 0.4 + + svg.attr('width', props.width) + .attr('height', props.height) this.update() } diff --git a/www/components/Circular.jsx b/www/components/Circular.jsx index 37c2267b..30d1fca3 100644 --- a/www/components/Circular.jsx +++ b/www/components/Circular.jsx @@ -1,5 +1,6 @@ import React, { useState, useEffect, useRef } from "react" import { css } from "@emotion/css" +import * as axios from "axios" import circular from "../charts/circular.js" import theme from "../theme" @@ -16,6 +17,8 @@ import Switch from "./Switch.jsx" let vis = null; const setVis = (v) => { vis = v } +const s = new XMLSerializer() + // const gif = new GIF({ // workers: 7, // quality: 1, @@ -34,20 +37,24 @@ origin + (amplitude * f(time * (frequency * (2 * 3.14)))) (600/2) + (300 * sin(t * (1/1) * 2pi))) */ +const target = (v) => ({target: {value: v}}) + export default function Circular( props ) { - const time = 10 + const { backendUrl } = props + + const time = 500 const step = 1 const limit = 1000 const defaultColor = 'transparent' const sliderMin = 0 const sliderMax = 1974 - const [color, setColorState] = useState(props.color || defaultColor) + const [color, setColorState] = useState(defaultColor) const [spectrum, setSpectrumState] = useState(props.s || 1) - const [multiplierX, setMultiplierXState] = useState(props.x || 1) - const [multiplierY, setMultiplierYState] = useState(props.y || 1) - const [period, setPeriodState] = useState(props.p || 1) + const [multiplierX, setMultiplierXState] = useState(props.x || 4) + const [multiplierY, setMultiplierYState] = useState(props.y || 3) + const [period, setPeriodState] = useState(props.p || 5) const [count, setCount] = useState(props.c || 1000) const [running, setRunningState] = useState(false) @@ -55,10 +62,10 @@ export default function Circular( props ) { const options = { count, - height: 800, - width: 1400, + height: 400, + width: 400, offset, - amplitude: 400, + amplitude: 200, frequency: 1 / period, multiplierY, multiplierX, @@ -73,18 +80,47 @@ export default function Circular( props ) { const offsetRef = useRef() offsetRef.current = offset - // useEffect(addFrame) + const multiplierXRef = useRef() + multiplierXRef.current = multiplierX + + const multiplierYRef = useRef() + multiplierYRef.current = multiplierY + + const spectrumRef = useRef() + spectrumRef.current = spectrum + + const periodRef = useRef() + periodRef.current = period + + const countRef = useRef() + countRef.current = count const bumpOffset = (offset) => { const off = (offset + step) % sliderMax // const off = (offset + step) - vis.setOffset(off) + // vis.setOffset(off) return off } const intervalHandler = () => { if ( !runningRef.current ) return + saveSvg() setOffsetState(bumpOffset) + + if (offsetRef.current % 15 == 0) { + setMultiplierXHandler(target(multiplierXRef.current + 1)) + setMultiplierYHandler(target(1)) + } else { + setMultiplierYHandler(target(multiplierYRef.current + 1)) + } + + if (multiplierYRef.current == 15 && multiplierXRef.current == 15) { + setMultiplierXHandler(target(1)) + setMultiplierYHandler(target(1)) + setPeriodHandler(target(periodRef.current + 1)) + } + + setSpectrumHandler(target(spectrumRef.current + 1)) } const addFrame = () => { @@ -123,7 +159,7 @@ export default function Circular( props ) { } const shareHandler = () => { - copyToClipboard(`http://${window.location.host}/RadialCartesian?x=${multiplierX}&y=${multiplierY}&p=${period}&s=${spectrum}&c=${count}`) + copyToClipboard(`http://${window.location.host}/RadialCartesian?x=${multiplierX}&y=${multiplierY}&p=${period}&c=${count}&s=${spectrum}`) } const setColorHandler = (type) => (_e, newState) => { @@ -165,15 +201,28 @@ export default function Circular( props ) { const types = Object.keys(theme.colors) .filter(type => type !== defaultColor) - const greatestCommonDivisor = gcd(multiplierY, multiplierX) - const ratioX = multiplierX / greatestCommonDivisor - const ratioY = multiplierY / greatestCommonDivisor + // const greatestCommonDivisor = gcd(multiplierY, multiplierX) + // const ratioX = multiplierX / greatestCommonDivisor + // const ratioY = multiplierY / greatestCommonDivisor + + const saveSvg = () => { + // window.hg = vis.getSvg() + axios.post(`${backendUrl}/svg`,{ + x: multiplierXRef.current, + y: multiplierYRef.current, + period: periodRef.current, + count: countRef.current, + spectrum: spectrumRef.current, + svg: s.serializeToString(vis.getSvg().node()) + }) + } return ( <> {startOrStopButton} + { // // @@ -234,7 +283,7 @@ export default function Circular( props ) { onChange={setMultiplierHandler} />
-
Ratio: {ratioX}:{ratioY} - {offset}
+
Ratio: {offset}
+