Skip to content

Instantly share code, notes, and snippets.

@jcmckeown
Last active January 28, 2017 05:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jcmckeown/9e97927361cb94232e5546d511eea3f6 to your computer and use it in GitHub Desktop.
Save jcmckeown/9e97927361cb94232e5546d511eea3f6 to your computer and use it in GitHub Desktop.
a sketch for processing 3.2.1 (java mode) ; draws approximate Thurston models of the Mandelbrot Set
/**
PRELUDE: the names given in this initial comment are (in my own thinking) mostly fanciful, in that I don't mean that
any of the particular people named particularly considered those particular things named for them in this note except
perhaps Thurston; but they *are* meant to lend recognition to some names genuinely connected with the subject of this
sketch. Of course, the name that really makes any of this connect to that thing is Yoccoz, and none of these things
is named for him...
NOTATION: until further notice, capitals [W] indicate words and miniscules [x] indicate single letters.
length of a word is notated #[W].
DEFINITION: given a word A of the form [WxY], where [x] is the n'th letter; the n'th SPLIT of A is the word
[WxxYWxxY]
NOTE that #[WxxYWxxY] = 2#[WxY] + 2;
NOTATION: now r,l,R,L are litteral litterals in words. other capitals still indicate words;
the lower-casing of a word W is denoted W'
denote %[W] the count of subwords [lr] [lR] [Lr] [LR] in [W]. (we don't, so far, actually do anything with the l/r
distinction, but they are relevant to motivating the analysis...)
DEFINITION:
[rL] is a Julia Word.
lower-casing a Julia Word and splitting at an index that *was* capitalized gives a derived-Julia word;
(the first derived-Julia Word is [rllrll]
note that #[rllrll] = %[rllrll] + #[rL] + 1
)
To INDUCE all Julia Words start r, and end L or l.
NOTE that in the split:
[W'rrY'W'rrY'] or [W'llY'Y'llY']
there are: the same number of subwords lr/rl in W'rrY' as in WRY; and one more subword lr *specifically* at the joint Y'W':
%[W'rrY'W'rrY'] = 2 %[WRY] + 1;
INDUCE: #[W'rrY'W'rrY'] = 2%[WRY]
DEDUCE: there is a monotone mapping from letter indexes of [W'rrY'W'rrY'] to those of [WRYr] (note the extra letter!)
which increments at indexes of repetitions:
which is called
DEFINITION: The SYNC(hronisation) of [W'rrY'W'rrY'].
The INDUCED Julia Word (a Julia Word) is Capitalized At those letters mapping to the splitting capital under the SYNC map.
ASIDE:
the induction by which the synchronisation is defined works for any chain of splittings, not merely the Julia-admissible
splittings;
DEFINITION:
the Brooks Words are the level words of the Tree of Julia Words; the first few layers are
l
l r l
l r l l r l l r l
lrl lrl l rl lrl lr l lrl lrl
DEFINITION:
ASIDE: this is related to splittings, and indeed plays rather well with them...
bracket words are either empty or of the form [(B)C] for bracket words B,C.
Given a bracket word [XY] (not assuming X or Y separately bracket)
Note that all the ")" in X are paired, all the "(" in Y are paired, and all the
"(" unpaired-in-X are in bijection with ")" unpaired-in-Y.
Define X^ to be the word with unpaired "(" replaced with ")" and Y^ oppositely.
The SPLICE of the pair {X,Y} is the word
[X(Y^X^)Y]
which is again a bracket word--- note that the literal "(" and ")" are paired brackets
More elaborately,
DEFINITION:
[r(L)] is a Fatou word.
for a Fatou word [XLY] or [XRY] the split/splice of the lower-casing
[X'l(lY'^X'^l)lY] or [X'r(rY'^X'^r)rY]
are derived-Fatou words, and capitalizing those l/r that SYNC with the particular R or L in [X?Y]
are new Fatou Words.
ASIDE Fatou words refine Julia words with Canonical Bracketings.
Matelski:Fatou::Brooks:Julia
l
l ( r ) l
l ( r ) l ( l ( r ) l ) l ( r ) l
l ( r )l( l ( r )l) l ( r (l (l ( r ) l) l) r ) l (l( r ) l )l( r ) l
l(r)l(l(r)l)l(l(r)l(l(r)l)l)l(r)l(l(r)l(l(r(r(l(l(r)l)l)r)r)l)l(r)l)l(r)l(l(l(r)l)l(r)l)l(l(r)l)l(r)l
(Matelski words in layers of the tree of Fatou words.)
DEFINITION:
A THURSTON word is the underlying bracket of a Matelski word.
*/
import java.util.Arrays;
int[] pairingSequence(char[] aString){
int lll = aString.length;
int[] ans = new int[lll];
int[] theStack = new int[lll];
int stringIndex;
int returnIndex;
int stackIndex = 0;
for (stringIndex = 0; stringIndex < lll ; stringIndex++){
switch( aString[stringIndex] ){
case '(': {
ans[stringIndex] = -1 ; // we don't know yet that this open-bracket is ever closed.
theStack[stackIndex] = stringIndex;
stackIndex++ ; // Note that stackIndex now points *above* the top of the stack (if anywhere)
}
break;
case ')': {
if ( stackIndex > 0 ){
stackIndex-- ; // now point to the top of the stack; note, we don't need to change anything until we push down next (if ever). But don't be doing cryptography
returnIndex = theStack[stackIndex];
ans[returnIndex] = stringIndex ; // *now* we know where that closes
ans[stringIndex] = theStack[stackIndex]; // and that is where this was opened.
}
else {
ans[stringIndex] = -1 ;
}
} break;
default: ans[stringIndex] = -1; break; // otherwise point to nowhere and do nothing
}
}
return ans;
}
void flipUnpaireds(char[] aString){ // this implements the "^" operation described in SPLICE
int[] thePairs = pairingSequence(aString);
int l = aString.length;
int j;
for (j = 0; j< l; j++){
if (thePairs[j] == -1) {
switch (aString[j]){
case '(' : aString[j] = ')'; break;
case ')' : aString[j] = '('; break;
default: break;
}
}
}
}
int[] letterCount(char[] lrsequence){
int ll = lrsequence.length;
int[] ans = new int[ll];
int j;
int count = 0;
for(j = 0; j<ll; j++){
switch (lrsequence[j]){
case 'l':
case 'r': count++;
default: break;
}
ans[j] = count;
}
return ans;
}
int[] stayCount(char[] lrsequence){ // when invoked, lrsequence may contain l's, r's, and brackets. this is why lastlr is a separate register.
// this implements SYNC as described above;
int ll = lrsequence.length;
int[] ans = new int[ll];
int j;
int count = 0;
char lastlr = 'r';
for(j = 0; j < ll ; j++){
if( lrsequence[j] == lastlr ){
count++;
}
ans[j] = count;
switch (lrsequence[j]){
case 'l': lastlr = 'l'; break;
case 'r': lastlr = 'r'; break;
default: break;
}
}
return ans;
}
char[] splitsplice(char[] lrsequence, int splitpoint){
int realSplit = splitpoint; // still not sure why i'm worying about splitpoint being a wrong index...
int ll = lrsequence.length;
int newll = 2 * ll + 4 ; // two copies of all the letters, two further subdivided, one new pair of brackets.
int j,k;
char[] middle = new char[ll]; // middle of the new string... we want to flip its brackets before inserting, so as not to pick up any rogue pairings.
char[] altogether = new char[newll];
// lrsequence[0..realsplit-1,realsplit] is Wx; lrsequence[realsplit+1..ll-1] is Y;
// altogether[0..newll-1] is Wx(xY^W^x)xY ;
// altogether[0..realsplit-1] is W is lrsequence[0..realsplit-1]
// altogether[realsplit] is lrsequence[realsplit]
// altogether[realsplit+1] is '('
// altogether[realsplit+2..realsplit+1+ll] is xY^W^ is middle.flipUnpaireds()[0..ll-1] ;
// altogether[ll+realsplit+2] is x again;
// altogether[ll+realsplit+3] is ')';
// altogether[ll+realsplit+4..newll-1] is lrseqeunce[realsplit..ll-1];
// verify: ll+(ll-1) + 4 = 2 * ll + 3 = newll-1;
/* { boolean keepGoing = true; // no particular reason to move backwards instead of forwards... i don't think we even really need to... it's just... have to track carefully.
char bracket = lrsequence[realSplit];
while(keepGoing && realSplit > 0 && realSplit < ll){
switch (lrsequence[realSplit]){
case 'l':
case 'r': keepGoing = false; break;
case '(': if (bracket =='(')realSplit++; else keepGoing = false; break; // neither "else" should ever be invoked...
case ')': if (bracket == ')')realSplit--; else keepGoing = false; break; // on the other hand, this might hang, otherwise.
default: break;
}
}
} */
for (j = 0; j <= realSplit; j++){
altogether[j] = lrsequence[j];
}
altogether[ j ] = '(' ; // j = realSplit+1; ...
k = realSplit; //
for (j = 0 ; k < ll ; j++){
middle[j] = lrsequence[k];
k++; // keep them together...
}
for (k = 0; j < ll ; j++){ // overflow!
middle[j] = lrsequence[k];
k++ ; // again
}
flipUnpaireds(middle);
k = realSplit+2;
for(j = 0; j < ll; j++){
altogether[k] = middle[j];
k++; // one last time...
} // now j = ll; k = ll + realsplit + 2 hmmm...
altogether[k++] = lrsequence[realSplit];
altogether[k++] = ')'; // k = ll + realsplit + 4 ;
for( j = realSplit; k < newll ; j++){
altogether[k] = lrsequence[j];
k++;
} // k = newll = 2 * ll + 4 ; j = ll + 1 ???
return altogether;
}
class FatouWord {
private
int[] syncMap;
int[] pairings;
public
char[] theWord;
FatouWord parent;
int[] splitables; // we're pretty sure there are at most three...
/* int mylength(){
return theWord.length;
}
char charAt(int k){
return theWord[k];
} */
FatouWord(){ // the root word
theWord = new char[] { 'r','(','l',')'} ;
parent = null;
splitables = new int[] { 2 };
syncMap = stayCount(theWord);
pairings = pairingSequence(theWord);
}
FatouWord(FatouWord pp,int whereToSplit){
parent = pp;
int theIndex = pp.splitables[whereToSplit];
theWord = splitsplice(pp.theWord,pp.splitables[whereToSplit]);
syncMap = stayCount(theWord);
int goal = letterCount(pp.theWord)[theIndex];
int start=0;
int stop=0;
while( start < syncMap.length ? syncMap[start] < goal : false){
start++;
}
stop = start;
while(stop < syncMap.length ? syncMap[stop] == goal : false){
stop++;
}
int[] newhelp = letterCount(theWord);
int J = newhelp[stop] - newhelp[start];
splitables = new int[J];
int k = 0;
for(int j = start; j < stop ; j++){
switch( theWord[j] ){
case 'l':
case 'r':
splitables[k] = j;
k++;
default : break;
}
}
pairings = pairingSequence(theWord);
}
void printData(){
println(new String(theWord));
println(Arrays.toString(splitables));
}
char[] printFocus(){
int start = splitables[0];
int stop = splitables[splitables.length-1];
int ll = 1 + stop - start;
char[] theFocus = new char[ll];
int k = start;
for (int i = 0; i < ll ; i++){
theFocus[i] = theWord[k];
k++;
}
return theFocus;
}
String explore(int depth){
String theAns = "";
int splitdex = 0;
if(depth <= 0){
theAns = new String(printFocus());
}else{
for(int i = splitables[0]; i <= splitables[splitables.length-1] ; i++){
switch (theWord[i]){
case '(' : theAns = theAns + "("; break;
case ')' : theAns = theAns + ")"; break;
default: theAns = theAns + (new FatouWord(this,splitdex)).explore(depth-1); splitdex++ ;
}
}
}
return theAns;
}
}
int depth = 10;
int offset = 0;
float rscale = 2.0;
void setup(){
fullScreen(); // or what you will...
smooth(8); // not sure this does aught without specifying P2D ...
stroke(0);
noFill();
noLoop();
}
void draw(){
background(255);
FatouWord ff = new FatouWord( );
String theString = ff.explore(depth);
char[] aMatelskiWord = theString.toCharArray();
int[] theCoords = pairingSequence(aMatelskiWord);
int ll = aMatelskiWord.length;
float scale = rscale * width / ll;
println("For Your Information, the current wordlength is " , ll);
float x,r;
for(int i = 0; i < ll/2 ; i++) {
if (theCoords[i] > i){
x = offset + scale * ( i + theCoords[i] ) / 2.0;
r = scale * ( theCoords[i] - i );
ellipse(x , 360 , r, r);
}
}
}
void mousePressed(){
switch
(mouseButton){
case LEFT: depth++ ; rscale *= 2;
offset = 2 * offset - mouseX; break;
case RIGHT: depth-- ; rscale /=2 ;
offset = (offset + mouseX)/2; break;
}
redraw();
}
@jcmckeown
Copy link
Author

Almost Certainly there are smarter ways to do much of this.

@jcmckeown
Copy link
Author

this revision is now click-to-explore. Horribly inefficient; but trying to make it smarter hurt my head.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment