I made a fern using the algorithm described in this wikipedia article

Here is the code

Code:
AxesOff:ClrDraw
DelVar XDelVar YDelVar A0
While 1
Pxl-On(int(26A+75),int(26Ans
Ans→Y
A→X
rand
If Ans<.01
Then
DelVar A.16Y
Else
If Ans<.86
Then
.85X+.04Y→A
­.04X+.85Y+1.6
Else
If Ans<.93
Then
.2X-.26Y→A
.23X+.22Y+1.6
Else
­.15X+.28Y→A
.26X+.24Y+.44
End:End:End:End

Here is the output after a while

Excellent mr womp womp !!!
That looks AWESOME!
Would you mind explaining how it works?
jcgter777 wrote:
That looks AWESOME!
Would you mind explaining how it works?


The Wikipedia article he linked gives a pretty good explanation. Essentially, you choose at random of one four functions to apply to a point, then repeat. The choice of functions and how often they occur is such that you end with a fractal structure that looks like, in this case, a fern. Tweaking the decimals you see in the equations for the four cases will make the fern look different, and there are examples of other constants on the Wikipedia page you can use to get ferns that resemble other species.
That's about it, the actual implementation really isn't hard.
mr womp womp did a great job of implementing Barnsley's Fern, but I thought I try optimizing the code a bit and making it more versatile. Here's what I've got:


Code:
PRGM:BARNSLEY
:AxesOff:ClrDraw
:DelvarA:75→C:0→D:0
:While 1
:Pxl-On(int(26A+C),int(26Ans+D),GREEN
:Ans→Y:A→X:rand→R
:For(L,1,dim(L₁
:If R<L₁(L:Then
:Matr►list([A],L,L₂:99→L
:XL₂(1)+YL₂(2)+L₂(5→A
:XL₂(3)+YL₂(4)+L₂(6
:End:End:End


The matrix [A] is 6x4 and contains the coefficients of the functions generating the fern. You essentially punch in the constants as they appear in the tables on the Wikipedia page, except vertically rather than horizontally (if you want it horizontal rather than vertical, just replace [A] with [A]^T in the Matr►list( command).
Meanwhile, L₁ contains the probabilities of the functions occurring as cumulative sums (effectively the "p" column of the Wikipedia table except cumSum('ed with a 1 as the final entry). C and D are offsets for drawing the fern so it stays within the graphable range.

Here's the original fern (but in GREEN), where C=75 and D=0:



And a variant (also in GREEN), where C=75 and D=30 (I was pressed for time, so its not fully generated):



Sorry if my descriptions of the function matrix and what not aren't sufficient; let me know if you have questions!
I like the way you generalized the code.
I actually did something like this at first to more closely resemble the underlying linear algebra, but I optimized it for speed because I think that's quite important in this case.
Just for fun, I decided to measure and compare the speeds of your program and mine. In a 2 minute time-span, yours drew 892 points while mine drew 1623 Wink

You also have a small mistake in your code:

Code:
:Pxl-On(int(26A+C),int(26A+D),GREEN

should be

Code:
:Pxl-On(int(26A+C),int(26Ans+D),GREEN
mr womp womp wrote:
I like the way you generalized the code.
I actually did something like this at first to more closely resemble the underlying linear algebra, but I optimized it for speed because I think that's quite important in this case.
Just for fun, I decided to measure and compare the speeds of your program and mine. In a 2 minute time-span, yours drew 892 points while mine drew 1623 Wink

You also have a small mistake in your code:

Code:
:Pxl-On(int(26A+C),int(26A+D),GREEN

should be

Code:
:Pxl-On(int(26A+C),int(26Ans+D),GREEN


Oh yeah; typo. I'll edit it. But yeah the Matr►list( does slow stuff down a bit.
Did someone say optimizing for speed?

* Using finance variables and changing for example .85X+.04Y to X.85+Y.04 could save a few ms per cycle.
* The multiplications by 26 can be eliminated entirely by appropriate scaling.
* The conditional that occurs 85% of the time should come first.
* rand is expensive and generates ~10 decimal places of randomness each time, and you're only using 2 each loop iteration. Therefore uses of rand can be cut down by a factor of 5.
These are all very good suggestions! I never even thought about having the conditional that occurs most often first. I don't think the multiplication by 26 could be removed though because I don't want it to affect A and Ans. I actually thought about using [recursiven] instead of A, but I figured the time difference was negligible. I guess if I am going to be advocating for speed optimization, I should go all the way Razz
I'll make the changes and measure the speed again tonight to see what kind of a difference it really makes.
These look amazing Love the Art, and I will love to see more of this.
I found the Barnsley's Fern post interesting. I have several programming calculators, but I kind of gotten away from programming on them. I currently enjoy coding on an iPad using Lua. I thought I’d convert the code here and run it on my iPad. Just to give you an idea of the speed difference, mr womp womp said his code ran 1623 plots in 2 minutes. On my iPad, I did 100,000 plots in less than 3 seconds. Here's a screen shot of the results I got.
The p1 thru p4 sliders allow me to vary the settings in the tables to get different plots when I press Redraw. This is 100,000 plots.
I put womp's code onto my CE and left it to run (with the wait states turned down) for about 20 minutes. It complete about 20500 iterations and made a pretty good fern!


I tried to make a version in ICE but I've spent 2 hours on this and it's refusing to work. Perhaps someone could see what I'm doing wrong?
I'm multiplying everything in womp's code by 100 (to account for the lack of decimals in ICE) then dividing by 100 at the point where it displays the points. I've also adjusted the probability numbers to account for ICE's weird rand function, and I stored the coordinates into S (x) and T (y) for debugging purposes.

Code:
FERNY
det(0
0→A
0→B
0→R

While getKey≠15
dbd(0
(2600*A+7500)/100→S
(2600*B)/100→T
det(6,S,T
B→Y
A→X
(rand/100000)→R
If R<2
0→A
16*Y→B
Else
If R<142
85*X+4*Y→A
­4*X+85*Y+160→B
Else
If R<155
20*X-26*Y→A
23*X+22*Y+160→B
Else
­15*X+28*Y→A
26*X+24*Y+44→B
End
End
End
End
det(1
I swapped the variables and constants around and used finance vars. I also swapped the order of the checks to make the .85 first, then the two .07 and then the .01. I can see how generating 14 digits of randomness and using only 2 is bad, but I can't really think of any way to improve the speed because adding pretty much any code at all makes it immediately slower, and randInt() is also slower.
These changes made it go from 1623 to 2060 (points drawn per 2 minutes).
Here is the updated code:


Code:
AxesOff:ClrDraw
0→I%:0→PV:0→Ś:0
While 1
Pxl-On(int(26PV+75),int(26Ans
Ans→Ś
PV→I%
rand
If Ans<.85
Then
I%.85+Ś.04→PV
I%­.04+Ś.85+1.6
Else
If Ans<.92
Then
I%.2-Ś.26→PV
I%.23+Ś.22+1.6
Else
If Ans<.99
Then
I%­.15+Ś.28→PV
I%.26+Ś.24+.44
Else
0→PV:.16→Ś
End:End:End:End


Just for fun, I reduced the wait states setting down to 6 to see how much I could push this. This actually got it up to 3003 points per 2 minutes. I think there isn't much left to do in pure basic. Any further improvements will probably have a minuscule impact on the overall speed.

The following code that store directly to the finance var instead of using Ans seems to be a bit faster, reaching 3111 with wait states set to 6.

Code:
AxesOff:ClrDraw
0→I%:0→PV:0→Ś:0
While 1
Pxl-On(int(26PV+75),int(26Ś
PV→I%
rand
If Ans<.85
Then
I%.85+Ś.04→PV
I%­.04+Ś.85+1.6→Ś
Else
If Ans<.92
Then
I%.2-Ś.26→PV
I%.23+Ś.22+1.6→Ś
Else
If Ans<.99
Then
I%­.15+Ś.28→PV
I%.26+Ś.24+.44→Ś
Else
0→PV:.16→Ś
End:End:End:End


Interesting finding though is that having the calculator plugged in makes it slower. running the same test with the calculator unplugged yields 3656.

I also let it run for way longer than I probably should have
I had an issue where I was getting absurdly low scores (around 2510) but here's what I did to fix it:
  • Unplug the charger: I guess the calculator keeps trying to communicate with my laptop while running the program which affected its performance. I got around 65 points back from this.
  • garbageCollect: this had the biggest impact, I hadn't garbageCollected in at least 3 months so cleaning all that up definitely helped, I got about 585 points back from this.
  • disable hooks: I broke the hooks that Cesium and ICE used by activating the 'test mode' prompt. Although I can't say it's an impressive score increase, I still got 16 points back.

My final score after all this was 3166 which is still slower than womp's, but much better than last time!

EDIT: mateo ninja'd me, but here's what I got after probably a million iterations, I had CEmu unthrottled and left it for about an hour.
When I run the code, it says ERROR: DOMAIN. Am I in the wrong window?


Code:

AxesOff:ClrDraw
DelVar XDelVar YDelVar A0
While 1
   Pxl-On(int(26A+75),int(26Ans
   Ans->Y
   A->X
   rand
   If Ans<.01
   Then
      DelVar A.16Y
   Else
      If Ans<.86
      Then
         .85X+.04Y->A
         .04X+.85Y+1.6
      Else
         If Ans<.93
         Then
            .2X-.26Y->A
            .23X+.22Y+1.6
         Else
            .15X+.28Y->A
            .26X+.24Y+.44
End:End:End:End
Barnsley's Fern in C on the TI-84+CE with 2,000,000 iterations:

I put together a quick attempt (Z80 assembly), it looks like I messed up somewhere Razz
Floating point pb ?
mr womp womp wrote:
I think there isn't much left to do in pure basic. Any further improvements will probably have a minuscule impact on the overall speed.


The below pure BASIC code about doubles the speed again and has adjustable window dimensions.

EDIT: now works on either monochrome or color (use Pt-On(,,4 for color)

EDIT: Improved speed another 20%


Code:
startTmr->T
Repeat checkTmr(T:End
AxesOff:ClrDraw
~log(.85->PMT
~log(.93->A
~log(.99->B
0->Xmin
12->Xmax
~3->Ymin
3.3->Ymax
0
For(I,1,4600*.15  //expected to draw 4600 points
  For([recursiven],~log(rand),PMT,~PMT
    Pt-On(real(Ans),imag(Ans),4
    Ans(.85+.04[i])+1.6
  End
  Pt-On(real(Ans),imag(Ans),4
  If [recursiven]>A
  Then
    real(Ans)(.24+.28[i])+imag(Ans)(.26-.15[i])+.44
  Else
    If [recursiven]>B
    Then
      real(Ans)(.22-.26[i])+imag(Ans)(.23+.2[i])+.44
    Else
      real(Ans).16
    End
  End
End
checkTmr(T)-1
  
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
Page 1 of 2
» All times are UTC - 5 Hours
 
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

 

Advertisement