shithub: scrax

ref: 81d1e4e01d7001e6a0107792e6167136787295d6
dir: /scratch.js/

View raw version
import {toNumber, toString, Dispatcher} from "./compile.js"
import {MakeBlock, setInput} from "./model.js"
import {Stop, library as library0, NumberSlot, PositiveSlot} from "./core.js"

let operatorOptions = []

function Operator(label, fn)
{
	let make = MakeBlock({run: value => fn(toNumber(value)), name: value => [{options: operatorOptions}, "of", value], category: "operators", shape: "reporter", slots: [NumberSlot], output: "number"})
	operatorOptions.push({label, morph, type: make.type})
	return make
	function morph(block)
	{
		let block1 = make()
		setInput(block1, 0, block.inputs[0])
		return block1
	}
}

function toRadians(n)
{
	return Math.PI / 180 * n
}

function fromRadians(n)
{
	return 180 / Math.PI * n
}

export let Round = MakeBlock({run: value => Math.round(toNumber(value)), name: value => ["round", value], category: "operators", shape: "reporter", slots: [NumberSlot], output: "number"})
export let Abs = Operator("abs", Math.abs)
export let Ceiling = Operator("ceiling", Math.ceil)
export let Floor = Operator("floor", Math.floor)
export let Sqrt = Operator("sqrt", Math.sqrt)
export let Sin = Operator("sin", n => Math.sin(toRadians(n)))
export let Cos = Operator("cos", n => Math.cos(toRadians(n)))
export let Tan = Operator("tan", n => Math.tan(toRadians(n)))
export let ArcSin = Operator("asin", n => fromRadians(Math.asin(n)))
export let ArcCos = Operator("acos", n => fromRadians(Math.acos(n)))
export let ArcTan = Operator("atan", n => fromRadians(Math.atan(n)))
export let Ln = Operator("ln", Math.log)
export let Log = Operator("log", Math.log10)
export let Exp = Operator("e ^", Math.exp)
export let Exp10 = Operator("10 ^", n => 10 ** n)

export let PickRandom = MakeBlock({run: random, name: (a, b) => ["pick random", a, "to", b], category: "operators", shape: "reporter", slots: [NumberSlot, NumberSlot], defaults: [1, 10]})
export let Wait = MakeBlock({async: true, run: value => new Promise(resolve => setTimeout(resolve, toNumber(value) * 1000)), name: seconds => ["wait", seconds, "seconds"], category: "control", slots: [PositiveSlot], defaults: [1]})
export let DaysSince2000 = MakeBlock({run: () => Date.now() / 86400 / 1000 - 10957, name: () => ["days since 2000"], category: "sensing", shape: "reporter"})

function random(a, b)
{
	let an = toNumber(a)
	let bn = toNumber(b)
	if (isFractional(toString(a)) || isFractional(toString(b))) {
		return Math.random() * (bn - an) + an
	}
	if (an > bn) [an, bn] = [bn, an]
	return Math.floor(Math.random() * (bn - an + 1) + an)
}

function isFractional(v)
{
	return v.includes(".") || v.includes("e-")
}

export function Scratch(options = {})
{
	let dispatcher = options.dispatcher ?? new Dispatcher(options.tick)
	
	let date = new Date()
	let time0 = performance.now()
	let time1 = time0
	
	options.onBeforeTick(() =>
	{
		time1 = performance.now()
		date = new Date()
	})
	
	let dateOptions = []
	
	function DatePart(label, fn)
	{
		let make = MakeBlock({run: () => fn(), name: () => ["current", {options: dateOptions}], category: "sensing", shape: "reporter", output: "number"})
		dateOptions.push({label, morph: () => make(), type: make.type})
		return make
	}
	
	let Timer = MakeBlock({run: () => (time1 - time0) / 1000, name: () => ["timer"], category: "sensing", shape: "reporter", output: "number"})
	let ResetTimer = MakeBlock({run: () => time1 = time0 = performance.now(), name: () => ["reset timer"], category: "sensing"})
	let Username = MakeBlock({run: () => options.username ?? "zamfofex", name: () => ["username"], category: "sensing", shape: "reporter", output: "text"})
	
	let Year = DatePart("year", () => date.getFullYear())
	let Month = DatePart("month", () => date.getMonth() + 1)
	let Day = DatePart("date", () => date.getDate())
	let DayOfWeek = DatePart("day of week", () => date.getDay() + 1)
	let Hour = DatePart("hour", () => date.getHours())
	let Minute = DatePart("minute", () => date.getMinutes())
	let Second = DatePart("second", () => date.getSeconds())
	
	let other = options.other ?? (id => dispatcher.all().filter(id1 => id1 !== id))
	
	let StopAll = MakeBlock({run: () => dispatcher.stop(), name: (...args) => getName(StopAll.type, ...args), category: "control", cap: true})
	let StopOther = MakeBlock({run: ({id}) => dispatcher.stop(other(id)), name: (...args) => getName(StopOther.type, ...args), category: "control"})
	let getName = NameFunction({StopAll, StopOther})
	
	let blocks = {Wait, Round, PickRandom, Timer, ResetTimer, Username, DaysSince2000, Abs, Year}
	let library = [...library0, ...Object.values(blocks)]
	
	return {
		...blocks,
		Abs, Ceiling, Floor, Sqrt, Sin, Cos, Tan, ArcSin, ArcCos, ArcTan, Ln, Log, Exp, Exp10,
		Year, Month, Day, DayOfWeek, Hour, Minute, Second,
		StopAll, StopOther,
		library,
		dispatcher,
		getName,
	}
}

let NameFunction = ({StopOther, StopAll}) =>
{
	let name = ["stop", {
		options: [
			{label: "this script", morph: () => Stop(), type: Stop.type},
			{label: "other scripts in sprite", morph: () => StopOther(), type: StopOther.type},
			{label: "all", morph: () => StopAll(), type: StopAll.type},
		]
	}]
	
	function getName(type)
	{
		if (type !== Stop.type && type !== StopAll.type && type !== StopOther.type) return
		return name
	}
	
	return getName
}