(
// ~distance is a function that expects two triads as arguments and calculates
// the euclidian distance between them. In other words, it considers both
// triads as points in a three-dimensional space.
~distance = {
arg tr1, tr2;
// Given two triads tr1=[a, b, c] and tr2=[x, y, z], the euclidian distance is
// defined as
// dist(tr1, tr2) = sqrt((a-x)² + (b-y)² + (c-z)²)
// In sclang, we can use
(tr2-tr1).sum({|i| i**2}).sqrt();
};
// ~findBestInversion expects two triads as argument and returns an inversion
// of the second triad that has the minimal distance to the first in terms of
// steps in the voices.
~findBestInversion = {
arg tr1, tr2; // triads given.
// Let's generate an array of all inversions of the second triad. We
// will later find and return the one with the minimal distance.
var inversions = [
tr2, // root position
(tr2 + [0 , -12, -12]).sort, // first inversion, down an octave
(tr2 + [0 , 0, -12]).sort, // second inversion, down an octave
tr2 - 12, // root position, down an octave
];
// For each chord in the array `inversions` we calculate its ~distance
// to the first triad and collect the results into an array.
var distances = inversions.collect({|chord| ~distance.value(tr1, chord)});
// Find the index that contains the minimal distance.
var minIdx = distances.minIndex;
// Return the inversion at that index as our choice for the progression.
inversions[minIdx];
};
// Test it on C-major → e-minor. Should return the first inversion, i.e. [-1, 4, 7].
~findBestInversion.value([0,4,7], [4,7,11])
)