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.drawTime = drawTime;
this.onDraw = onDraw;
const self = this;
this.id = id || s4();
this.drawer = new Drawer((haps, time) => {
@ -150,13 +149,6 @@ export class StrudelMirror {
this.onDraw?.(haps, time, currentFrame, this.painters);
}, 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();
autodraw && this.drawFirstFrame();
@ -182,6 +174,14 @@ export class StrudelMirror {
beforeEval: async () => {
cleanupDraw();
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 replOptions?.beforeEval?.();
},

View File

@ -106,17 +106,23 @@ function getCanvasWidget(id, options = {}) {
registerWidget('_pianoroll', (id, options = {}, pat) => {
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) => {
options = { width: 200, height: 200, size: 36, ...options };
registerWidget('_punchcard', (id, options = {}, pat) => {
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) => {
options = { width: 500, height: 60, pos: 0.5, scale: 1, ...options };
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}%)`);
});
/**
* 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
// objects

View File

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

View File

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

View File

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