Public
Edited
Jan 2
Insert cell
Insert cell
Insert cell
triangle(
levels,
)
Insert cell
triangle(
levels,
{caption: "Pascal’s triangle"},
pascal(levels)
)
Insert cell
Insert cell
triangle(
levels,
{caption: `Twofold ${levels}–${2 * levels} → pitch sequence: ${scale}`},
tetraktys(levels)
)
Insert cell
Insert cell
raw = (row, col) => `${row},${col}`
Insert cell
// 1. map raw coordinates to Pascal’s coordinates
// 2. calculate and return Pascal’s triangle value
pascal =
(levels) =>
(row, col) =>
pt(levels - row + col, levels - row)
Insert cell
// tetraktys
// https://en.wikipedia.org/wiki/Tetractys
tetraktys =
(levels) =>
(row, col) =>
ratio({row: 2 * levels - row - 1, col: 2 * levels - col})
Insert cell
triangle = _.curry(
(levels, options = {}, data = x => x) =>
tri(options)(set4(levels)(data))
)
Insert cell
aap = set4(levels)((row,col)=>Fraction(2 * levels - row - 1, 2 * levels - col)).map(k => k.value)
Insert cell
aap.map(f => f.mul(lcm(...aap.map(k => k.d))))
Insert cell
scalePlot(aap)
Insert cell
set4 = (
(levels) =>
(valuator = raw) =>
d3
.range(levels)
.map(
(row) =>
d3
.range(row + 1)
.map(
(col) => ({
row,
// start each row one more to the left
// step columns 2
// col: levels - row + 2 * col - 1,
col,
value: valuator(row, col),
})
)
)
.flat())
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
diatonicScaleSample = s .map(f => Fraction(f))
Insert cell
diatonicScaleLCM = lcm(...diatonicScaleSample.map(f => f.d))
Insert cell
diatonicNaturals = diatonicScaleSample.map(f => f.mul(diatonicScaleLCM).n)
Insert cell
diatonicScaleSample.map(f => Object.assign(
f,
{
natural: f.mul(24).n,
}
))
Insert cell
ratios = (scale) => {
const fractions = scale.map(f => Fraction(f));
const d = lcm(...fractions.map(f => f.d));
const naturals = fractions.map(f => f.mul(d).n);
const extent = d3.extent(naturals);
const data = naturals.map((x, i) => ({x, ...fractions[i]}));

return ({
scale,
fractions,
d,
naturals,
extent,
data,
})
}
Insert cell
ratios(s)
Insert cell
s = [
1/1,
9/8,
5/4,
// 4/3,
33/24,
3/2,
5/3,
15/8,
2/1,
]
Insert cell
scalePlot2(ratios(s))
Insert cell
scalePlot2 = (scale) => Plot.plot({
x: {
domain: scale.extent,
ticks: 4 * scale.naturals.length,
grid: true,
},
y: {axis: null},
height: 9*15,
width,
marks: [
// Plot.ruleY([0], {stroke: "red"}),
Plot.dot(scale.data, {
frameAnchor: "bottom",
x: "x", fill: "black", r: 6
}),
Plot.tip(scale.data, {
x: "x",
frameAnchor: "bottom",
fontFamily: "monospace",
title: jet(scale),
}),
]
})
Insert cell
jet =
(scale) =>
({n, d}, i) =>
[
`r: ${n}:${d}`,
`n: ${scale.naturals[i]}/${scale.d}`,
`f: ${(n / d).toFixed(2)}`,
`¢: ${(cent(n / d)).toFixed(0)}`
]
.join("\n")
Insert cell
jet({n:30, d: 24}, 0)
Insert cell
scale
Insert cell
ratios([3,4,5])
Insert cell
naturalized = diatonicScaleSample.map(f => f.mul(diatonicScaleLCM))
Insert cell
scalePlot(diatonicScaleSample)
Insert cell
scalePlot = (scale) => {
const d = lcm(...scale.map(f => f.d));
const naturals = scale.map(f => f.mul(d).n);
const n = naturals.map(x => ({x,y:0}));
// return naturals.map(x => ({x,y:0}));
const length = scale.length;
const e = d3.extent(naturals);
// return e;
// return scale;
// return naturals;
// return n;
// return d;
return Plot.plot({
x: {
domain: [e[0], 2 * e[0]],
ticks: 2 * length,
},
y: {axis: null,},
// width,
height: 9*6,
width,
marks: [
Plot.dot(n, {x:"x", fill:"black", r: 6}),
Plot.tip(n, Plot.pointer({
x: "x",
title: () => "hoi"
})),
// Plot.tip(naturals)
]
})}
Insert cell
diatonicNaturals.map(x => ({x,y:0}))

Insert cell
scale = scaler(levels)
Insert cell
scaler = (n) => d3.range(n, 2 * n + 1).join(":")
Insert cell
filter([7,13])(d3.range(4,22))
Insert cell
filter =
(omit) =>
(list) =>
list.filter(e => !omit.includes(e))
Insert cell
ratio = ({row,col}) => ({
cleared: Fraction(row,col)
.valueOf()
// .toFraction().replace("/", ":")
,
raw: `${row}:${col}`
})
[settings.fraction]
Insert cell
function pt(row, col) {
if (row === 1 && col === 1) {
return 1;
} else if (col < 1 || col > row) {
return 0;
} else {
return (pt (row - 1, col - 1)) +
(pt (row - 1, col));
}
}
Insert cell
Insert cell
pg =
(valuator = (row, col) => `${row},${col}`) =>
(levels) =>
d3
.range(levels)
.map(
(row) =>
d3
.range(row + 1)
.map(
(col) => ({
row,
// start each row one more to the left
// step columns 2
col: levels - row + 2 * col - 1,
col,
value: valuator(row, col),
})
)
)
.flat()
Insert cell
pg()(4) // generates 10 values
Insert cell
Insert cell
tri = Triangle(settings) // set up Tri (partial application)
Insert cell
Triangle =
({levels, wide, width, background, boxed, margin, rotate}) =>
({x = "col", y = "row", text = "value", caption = `Raw triangle coordinates for ${levels} levels`} = {}) =>
(data) =>
Plot.plot({
caption,
marks: [
Plot.cell(data, {
x,
y,
stroke: boxed ? "black" : "none",
fill: background ? "peachpuff" : "none",
}),
Plot.text(data, {
x,
y,
text,
fontSize: 27 - 2 * levels,
rotate,
}),
],
width,
height: width,
marginTop: margin,
marginLeft: margin,
marginBottom: margin,
marginRight: margin,
x: { tickSize: 0, padding: 0, tickRotate: -45, axis: null },
y: { tickSize: 0, padding: 0, tickRotate: -45, axis: null },
style: `transform: rotate(${-rotate}deg); background:transparent;`
})
Insert cell
Insert cell
table = (data) => Inputs.table(data, {width: 320})
Insert cell
Insert cell
Fraction = require("fraction.js@4.0")
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more