Merge pull request #1008 from tidalcycles/inline-punchcard

Inline punchcard
This commit is contained in:
Felix Roos 2024-03-23 12:30:03 +01:00 committed by GitHub
commit aede6e4b26
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 47 additions and 22 deletions

View File

@ -141,7 +141,6 @@ export class StrudelMirror {
this.painters = []; this.painters = [];
this.drawTime = drawTime; this.drawTime = drawTime;
this.onDraw = onDraw; this.onDraw = onDraw;
const self = this;
this.id = id || s4(); this.id = id || s4();
this.drawer = new Drawer((haps, time) => { this.drawer = new Drawer((haps, time) => {
@ -150,13 +149,6 @@ export class StrudelMirror {
this.onDraw?.(haps, time, currentFrame, this.painters); this.onDraw?.(haps, time, currentFrame, this.painters);
}, drawTime); }, drawTime);
// this approach does not work with multiple repls on screen
// TODO: refactor onPaint usages + find fix, maybe remove painters here?
Pattern.prototype.onPaint = function (onPaint) {
self.painters.push(onPaint);
return this;
};
this.prebaked = prebake(); this.prebaked = prebake();
autodraw && this.drawFirstFrame(); autodraw && this.drawFirstFrame();
@ -182,6 +174,14 @@ export class StrudelMirror {
beforeEval: async () => { beforeEval: async () => {
cleanupDraw(); cleanupDraw();
this.painters = []; this.painters = [];
const self = this;
// this is similar to repl.mjs > injectPatternMethods
// maybe there is a solution without prototype hacking, but hey, it works
// we need to do this befor every eval to make sure it works with multiple StrudelMirror's side by side
Pattern.prototype.onPaint = function (onPaint) {
self.painters.push(onPaint);
return this;
};
await this.prebaked; await this.prebaked;
await replOptions?.beforeEval?.(); await replOptions?.beforeEval?.();
}, },

View File

@ -106,17 +106,23 @@ function getCanvasWidget(id, options = {}) {
registerWidget('_pianoroll', (id, options = {}, pat) => { registerWidget('_pianoroll', (id, options = {}, pat) => {
const ctx = getCanvasWidget(id, options).getContext('2d'); const ctx = getCanvasWidget(id, options).getContext('2d');
return pat.pianoroll({ fold: 1, ...options, ctx, id }); return pat.id(id).pianoroll({ fold: 1, ...options, ctx, id });
}); });
/* registerWidget('_spiral', (id, options = {}, pat) => { registerWidget('_punchcard', (id, options = {}, pat) => {
options = { width: 200, height: 200, size: 36, ...options };
const ctx = getCanvasWidget(id, options).getContext('2d'); const ctx = getCanvasWidget(id, options).getContext('2d');
return pat.spiral({ ...options, ctx, id }); return pat.id(id).punchcard({ fold: 1, ...options, ctx, id });
}); */ });
registerWidget('_spiral', (id, options = {}, pat) => {
let _size = options.size || 275;
options = { width: _size, height: _size, ...options, size: _size / 5 };
const ctx = getCanvasWidget(id, options).getContext('2d');
return pat.id(id).spiral({ ...options, ctx, id });
});
registerWidget('_scope', (id, options = {}, pat) => { registerWidget('_scope', (id, options = {}, pat) => {
options = { width: 500, height: 60, pos: 0.5, scale: 1, ...options }; options = { width: 500, height: 60, pos: 0.5, scale: 1, ...options };
const ctx = getCanvasWidget(id, options).getContext('2d'); const ctx = getCanvasWidget(id, options).getContext('2d');
return pat.scope({ ...options, ctx, id }); return pat.id(id).scope({ ...options, ctx, id });
}); });

View File

@ -2476,6 +2476,16 @@ export const hsl = register('hsl', (h, s, l, pat) => {
return pat.color(`hsl(${h}turn,${s * 100}%,${l * 100}%)`); return pat.color(`hsl(${h}turn,${s * 100}%,${l * 100}%)`);
}); });
/**
* Sets the id of the hap in, for filtering in the future.
* @name id
* @noAutocomplete
* @param {string} id anything unique
*/
Pattern.prototype.id = function (id) {
return this.withContext((ctx) => ({ ...ctx, id }));
};
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Control-related functions, i.e. ones that manipulate patterns of // Control-related functions, i.e. ones that manipulate patterns of
// objects // objects

View File

@ -96,9 +96,8 @@ export const cleanupDraw = (clearScreen = true) => {
} }
}; };
Pattern.prototype.onPaint = function (onPaint) { Pattern.prototype.onPaint = function () {
// this is evil! TODO: add pattern.context console.warn('[draw] onPaint was not overloaded. Some drawings might not work');
this.context = { onPaint };
return this; return this;
}; };

View File

@ -129,12 +129,17 @@ export function pianoroll({
colorizeInactive = 1, colorizeInactive = 1,
fontFamily, fontFamily,
ctx, ctx,
id,
} = {}) { } = {}) {
const w = ctx.canvas.width; const w = ctx.canvas.width;
const h = ctx.canvas.height; const h = ctx.canvas.height;
let from = -cycles * playhead; let from = -cycles * playhead;
let to = cycles * (1 - playhead); let to = cycles * (1 - playhead);
if (id) {
haps = haps.filter((hap) => hap.context.id === id);
}
if (timeframeProp) { if (timeframeProp) {
console.warn('timeframe is deprecated! use from/to instead'); console.warn('timeframe is deprecated! use from/to instead');
from = 0; from = 0;

View File

@ -19,7 +19,7 @@ function spiralSegment(options) {
cy = 100, cy = 100,
rotate = 0, rotate = 0,
thickness = margin / 2, thickness = margin / 2,
color = '#0000ff30', color = 'steelblue',
cap = 'round', cap = 'round',
stretch = 1, stretch = 1,
fromOpacity = 1, fromOpacity = 1,
@ -50,18 +50,18 @@ function spiralSegment(options) {
} }
function drawSpiral(options) { function drawSpiral(options) {
const { let {
stretch = 1, stretch = 1,
size = 80, size = 80,
thickness = size / 2, thickness = size / 2,
cap = 'butt', // round butt squar, cap = 'butt', // round butt squar,
inset = 3, // start angl, inset = 3, // start angl,
playheadColor = '#ffffff90', playheadColor = '#ffffff',
playheadLength = 0.02, playheadLength = 0.02,
playheadThickness = thickness, playheadThickness = thickness,
padding = 0, padding = 0,
steady = 1, steady = 1,
inactiveColor = '#ffffff20', inactiveColor = '#ffffff50',
colorizeInactive = 0, colorizeInactive = 0,
fade = true, fade = true,
// logSpiral = true, // logSpiral = true,
@ -69,8 +69,13 @@ function drawSpiral(options) {
time, time,
haps, haps,
drawTime, drawTime,
id,
} = options; } = options;
if (id) {
haps = haps.filter((hap) => hap.context.id === id);
}
const [w, h] = [ctx.canvas.width, ctx.canvas.height]; const [w, h] = [ctx.canvas.width, ctx.canvas.height];
ctx.clearRect(0, 0, w * 2, h * 2); ctx.clearRect(0, 0, w * 2, h * 2);
const [cx, cy] = [w / 2, h / 2]; const [cx, cy] = [w / 2, h / 2];
@ -97,7 +102,7 @@ function drawSpiral(options) {
const isActive = hap.whole.begin <= time && hap.endClipped > time; const isActive = hap.whole.begin <= time && hap.endClipped > time;
const from = hap.whole.begin - time + inset; const from = hap.whole.begin - time + inset;
const to = hap.endClipped - time + inset - padding; const to = hap.endClipped - time + inset - padding;
const { color } = hap.context; const color = hap.value?.color;
const opacity = fade ? 1 - Math.abs((hap.whole.begin - time) / min) : 1; const opacity = fade ? 1 - Math.abs((hap.whole.begin - time) / min) : 1;
spiralSegment({ spiralSegment({
ctx, ctx,