Skip to content

Instantly share code, notes, and snippets.

@stellaremnant
Last active April 5, 2022 20:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stellaremnant/507febaf77c1c6692ce781af4d8843e3 to your computer and use it in GitHub Desktop.
Save stellaremnant/507febaf77c1c6692ce781af4d8843e3 to your computer and use it in GitHub Desktop.
desmosstd
// ==UserScript==
// @name DesmosSTD
// @version 0.2
// @description Desmos standard library
// @updateURL https://gist.githubusercontent.com/possiblynova/507febaf77c1c6692ce781af4d8843e3/raw
// @downloadURL https://gist.githubusercontent.com/possiblynova/507febaf77c1c6692ce781af4d8843e3/raw
// @author Nova
// @include http://www.desmos.com/calculator*
// @include https://www.desmos.com/calculator*
// @grant GM_addStyle
// @grant unsafeWindow
// @run-at document-start
// ==/UserScript==
/* globals globalThis */
console.log('demost-start')
const std = [
/*
{
name: '\\lambda',
comp: '{ tag: "reducer", minArity: 1, maxArity: 1 / 0, argumentTypes: [t.ListOfAny] }',
src: args => {
console.log(args[0][0]);
return 'test!'; // the eventual plan is that this would look for a user defined javascript function defined in comments like so:
}
},
*/
{
name: 'noaction',
comp: '{ tag: "reducer", minArity: 0, maxArity: 1 / 0, argumentTypes: [t.ListOfAny], returnType: t.Action }',
src: args => { return { type: 'Action', updateRules: [] } }
},
{
name: 'crand',
comp: '{ minArity: 0, maxArity: 1 }', // pass x or n or something so desmos doesn't optimize it away
src: args => {
if(!globalThis._crand) globalThis._crand = 0;
console.info('rand', globalThis._crand);
var t = globalThis._crand += 0x6D2B79F5;
t = Math.imul(t ^ t >>> 15, t | 1);
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
return ((t ^ t >>> 14) >>> 0) / 4294967296;
}
},
{
name: 'cseed',
comp: '{ argumentTypes: [t.Number] }',
src: args => {
globalThis._crand = args[0];
console.log('new seed', args[0]);
return 1;
}
},
{
name: 'time',
comp: '{ argumentTypes: [] }',
src: args => { return Date.now() } // {} required since tostring
},
/*
{
name: 'str',
comp: '{ argumentTypes: [t.Number] }',
src: args => {
console.log(args);
return 'test123'; // the eventual plan for this is that you can write strings in comment entries, then get them via str, so str(1) will refer to a comment containing str(1)=somecontenthere
}
},
*/
{
name: 'conv_ascii2str',
opname: 'conv',
comp: '{ tag: "reducer", argumentTypes: [t.ListOfNumber] }',
src: `return String.fromCharCode.apply(null, args[0])`
},
{
name: 'conv_str2ascii',
opname: 'conv',
comp: '{ tag: "reducer", minArity: 1, maxArity: 1, argumentTypes: [t.ListOfAny], returnType: t.ListOfNumber }',
src: `return args[0][0].split('').map(char => char.charCodeAt(0))`
},
// bitwise
{
name: 'bit_not',
opname: 'bit',
comp: '{ argumentTypes: [t.Number] }',
src: `return ~args[0]`
},
{
name: 'bit_and',
opname: 'bit',
comp: '{ tag: "reducer", argumentTypes: [t.ListOfNumber] }',
src: `return args[0].reduce((acc, v) => acc & v, 2**32)`
},
{
name: 'bit_or',
opname: 'bit',
comp: '{ tag: "reducer", argumentTypes: [t.ListOfNumber] }',
src: `return args[0].reduce((acc, v) => acc | v, 0)`
},
{
name: 'bit_xor',
opname: 'bit',
comp: '{ argumentTypes: [t.Number, t.Number] }',
src: `return args[0] ^ args[1]`
},
{
name: 'bit_shl',
opname: 'bit',
comp: '{ argumentTypes: [t.Number, t.Number] }',
src: `return args[0] << args[1]`
},
{
name: 'bit_shr',
opname: 'bit',
comp: '{ argumentTypes: [t.Number, t.Number] }',
src: `return args[0] >> args[1]`
},
{
name: 'bit_ashr',
opname: 'bit',
comp: '{ argumentTypes: [t.Number, t.Number] }',
src: `return args[0] >>> args[1]`
},
/*
{
name: 'test',
comp: '{ argumentTypes: [t.Number, t.Number], returnType: t.Point }',
src: args => new Proxy([args[0], args[1]], { get(target, key, _) { return (key == '0' || key == '1') ? Math.random() : target[key] } }) // this was a weird one, i call it the indecisive point, https://streamable.com/sfidi8
},
*/
{
name: 'sto',
comp: '{ argumentTypes: [t.Number, t.Number], returnType: t.Number }', // pass 0x or 0n plus your index so desmos doesnt optimize it away
src(args) {
if(globalThis._stored == undefined) globalThis._stored = {};
globalThis._stored[args[0]] = args[1];
return 1;
}
},
{
name: 'rcl',
comp: `{ argumentTypes: [t.Number], returnType: t.Number }`, // pass 0x or 0n plus your index so desmos doesnt optimize it away
src(args) {
if(globalThis._stored == undefined) globalThis._stored = {};
let v = globalThis._stored[args[0]];
return typeof v !== 'undefined' ? v : NaN;
}
}
]
unsafeWindow.Worker = new Proxy(Worker, {
construct(target, args) {
console.log(target, args);
if (args[0].startsWith("blob:")) {
const xhr = new XMLHttpRequest
xhr.open("GET", args[0], false)
xhr.send()
//console.log(xhr.responseText)
console.log('worker loaded', args)
let ret = xhr.responseText
.replace(
/Object.defineProperty\((.),"sinh"/g,
std.reduce(
(acc, def) => `${acc},$1["${def.name}"]=function(...args) { ${typeof def.src == 'function' ? def.src.toString().match(/{([\s\S]+)}/)[1] : def.src} }`, '$&'
)
)
.replace(
/sinh:(.)\("BuiltIn","sinh"\)/,
std.reduce(
(acc, def) => def.comp ? `${acc},[\`${def.name}\`]:(console.log(t), $1("BuiltIn","${def.name}",${def.comp}))` : `${acc},${def.name}:(console.log(t), $1("BuiltIn","${def.name}",{argumentTypes:[${
def.argumentTypes.reduce((acc, typ) => `${acc}t.${typ},`, '')
}]}))`,
'$&'
)
)
.replace(
/self\.onmessage=function\(e\)\{self\.loadMessageQueue\.push\(e\)\}/,
`self.onmessage = function(e) {
console.log(e);
self.postMessage('ACK');
self.loadMessageQueue.push(e);
}`)
console.log(xhr.responseText)
console.log(ret)
args[0] = URL.createObjectURL(new Blob([ret]))
}
return new target(...args)
}
})
function applyNames() {
/* see https://github.com/jared-hughes/DesThree/blob/master/src/View.js#L66 for more info, including forcing a rerender */
const fields = document.querySelectorAll('.dcg-mq-editable-field');
console.log(fields);
let optProto = Object.getPrototypeOf(fields[0]._mqMathFieldInstance.__controller.root.cursor.options)
optProto.autoCommands.gamma = true;
optProto.autoCommands.eta = true;
optProto.autoCommands.iota = true;
optProto.autoCommands.lambda = true;
optProto.autoCommands.mu = true;
optProto.autoCommands.sigma = true;
optProto.autoCommands.Delta = true;
optProto.autoCommands.Xi = true;
fields.forEach(field => {
const opt = field._mqMathFieldInstance.__controller.root.cursor.options;
std.forEach(def => (opt.autoOperatorNames[def.opname || def.name] = def.opname || def.name));
})
}
function init() {
unsafeWindow.Calc.controller.dispatcher.register(e => {
if(['tick', 'new-expression', 'new-expression-at-end'].includes(e.type) || (e.type === 'on-special-key-pressed' && e.key === 'Enter')) applyNames();
})
applyNames();
}
window.addEventListener('load', () => setTimeout(() => {
'use strict';
console.log('desmost-ready');
const Calc = unsafeWindow.Calc;
init();
const DesmosSTD = {
update(id) {
Calc.controller.dispatch({ type: 'set-expression-properties-from-api', id: id });
return DesmosSTD;
},
render() {
Calc.notifyControllerOfAPICall();
return DesmosSTD;
},
setText(id, text) {
Calc.controller.getItemModel(id).text = text;
DesmosSTD.render();
return DesmosSTD;
},
setVariableValue(id, name, value, noundo = false) {
Calc.controller.getItemModel(id).latex = name + '=' + value;
if(noundo) Calc.controller.stateStack._stackPointer -= 1;
DesmosSTD.render();
return DesmosSTD;
}
};
unsafeWindow.DesmosSTD = DesmosSTD;
GM_addStyle('.dcg-desmos-svg-logo:not(#a) { fill: #ff7fff; }');
}, 1000), false);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment