# Function for fractals from initial rhythm pattern

hey,

im using this function for creating fractals from an initial rhythm pattern:

``````(
var low_dens = { |array|
var ioi, min_index, neighbor_index, result = [];
ioi = array.copy();

while {ioi.size() > 1} {
// in every step, we will reduce ioi until it has size 1
var intermediate_result = [];
min_index = ioi.minIndex;

// if first element is minimum element, the neighor can only be to the right
if (min_index == 0) {
neighbor_index = 1;
} {
// else if last element is minimum element, the neighbor can only be to the left
if (min_index == (ioi.size - 1)) {
neighbor_index = (ioi.size - 2);
} {
// else we're in the middle, so use the neighbor which is smallest (arbitrary choice)
var dir = 1;
if (ioi[min_index - 1] < ioi[min_index + 1]) {
dir = -1;
};
neighbor_index = min_index + dir;
};
};
// swap neighbor and min_index so that neighbor is always bigger than min_index
// - this makes copying easier in the next step
if (neighbor_index < min_index) {
var tmp = min_index;
min_index = neighbor_index;
neighbor_index = tmp;
};

// make a new list consisting of everything before min_index, sum of min el and its neighbor, everything after neighbor
intermediate_result = ioi.copyRange(0, min_index-1) ++ (ioi[min_index] + ioi[neighbor_index]) ++ ioi.copyRange(neighbor_index+1, (ioi.size-1));

if (intermediate_result.size >= ioi.size) {
"ERROR!".postln;
};

// add it to the list of results

// and update our starting point with the reduced list
ioi = intermediate_result.copy();
};

// return the complete list of reductions
result;
};

var high_dens = { |array|
var ioi, ioi_sort, pot_ioi, index_max, result = [];

ioi = array.copy();

ioi_sort = ioi.as(Set).as(Array).sort;
pot_ioi = ioi_sort.minItem;
//pot_ioi = 1;

while { ioi.maxItem > pot_ioi } {
var intermediate_result = [];

index_max = ioi.findAll([ioi.maxItem]).choose;

intermediate_result = ioi;

intermediate_result = intermediate_result.insert(index_max + 1, [ioi[index_max] - pot_ioi, pot_ioi]).flat;

intermediate_result.removeAt(index_max);

// add it to the list of results

// and update our starting point with the reduced list
ioi = intermediate_result.copy();

};

// return the complete list of increasements
result;
};

var getAbstractions = { |durations|
var low_abs, high_abs, abstractions = [];

durations.debug("rhythm");

//add abstractions with lower density to the array of abstractions
low_abs = low_dens.(durations);
low_abs.do{|el, elindex|
};

//add the initial rhythm pattern to the array of abstractions

//add abstractions with higher density to the array of abstractions
high_abs = high_dens.(durations);
high_abs.do{|el, elindex|
};

//order array of abstractions by size: low -> high
abstractions = abstractions.sort({|a, b| a.size < b.size});

abstractions.indexOf(durations).debug("rhythm index");
abstractions.debug("abstractions");
};

var convertToBinary = { |arrays|
arrays.collect { |row|
var max;
row = row.integrate;
max = row.maxItem;
Array.fill(max, 0).putEach(row % max, 1)
};
};

~getAbs = { |durations|

var arrays = getAbstractions.(durations);
var binary = convertToBinary.(arrays);

var patterns = arrays.collect { |array|
Pseq(array, 1);
};

(arrays: arrays, binary: binary, patterns: patterns);
};
)
``````

There is one issue i would like to fix and it has something to do with the `high_dens` function:

when you pass in an array which includes 1 as the smallest integer value it works nicely:

`~getAbs.([3, 1, 2, 2])[\arrays];`

but when you pass in an array which does not include 1 as the smallest value you dont get the full set of abstractions:

``````~getAbs.([3, 3, 2])[\arrays];
``````

is it enough to just exchange `pot_ioi = ioi_sort.minItem;` with `pot_ioi = 1;` in the `high_dens` function? and do you have any other suggestions in terms of coding style for this function? thanks.

1 Like

This is really a matter of your design of the algorithm spec… for you to decide. So far the one thing that’s known is, you’re not satisfied with the result of using minItem.

• [1, 2, 3] you want 1
• [2, 3, 3] you said above that you didn’t want 2
• What about [2, 4, 4] then?
• Could be just 1?
• Or maybe you want the GCD? In that case pot_ioi = 2 in this case, where [2, 3] would give you 1.

In any case, nobody here can tell you whether it’s “enough” to use a constant 1 – because it’s your algorithm and your requirement.

hjh

i think the current implementation is nice for an initial array which contains 1 as the lowest value.
then you get an array with just one item == array.size at index 0 and another array filled with just 1s for the last index.
so minimum rhythmic density at index 0 vs. maximum rhythmic density at the last index and you could scale them via multiplication / division and go through them via a specific sequence for the index.

unfortunately the [3, 3, 2] example doesnt create an array filled with 1s at the last index with my implementation and im not sure right now if the function works correctly if i just hardcode 1 for pot_ioi.

never thought of GCD, maybe that would work.

i was also thinking what happens if you are not using integer values for the initial rhyhtm pattern.
i like this blending idea between eucledian and equal divisions:

``````// morphing between eucledian and equal divisions
(
~blend = { |n, k, blend|
Bjorklund2(n, k).blend(Psubdivide(n, k).asStream.nextN(n), blend);
};
)

// eucledian divisions
~blend.(3, 8, 0);

// 50% blend between eucledian and equal divisions
~blend.(3, 8, 0.5);
``````
1 Like