L-system examples

Hi! here are some examples producing the L-Systems in this picture.
To run a lsystem set it using the enum in renderer.cpp → runLSysType = LSYS::SNWFLK;

By Bernard Motheron

main.cpp

// By Bernard Motheron

#include "ofMain.h"
#include "ofApp.h"

int main( ){
	ofSetupOpenGL(3840 , 2160, OF_WINDOW);		
  ofSetBackgroundColor(ofColor::black);

	ofRunApp(new ofApp());}

ofApp.h

// By Bernard Motheron

#pragma once

#include "ofMain.h"
#include "Renderer.h"

class ofApp : public ofBaseApp{

	public:
    Renderer renderer;

		void setup();
		void update();
		void draw();
		
};

ofApp.cpp

// By Bernard Motheron

#include "ofApp.h"

void ofApp::setup(){
  renderer.setup();}

void ofApp::update(){
  renderer.update();}

void ofApp::draw(){
  renderer.draw();
}

rendere.h

// By Bernard Motheron

#pragma once
#include "ofMain.h"

class Renderer
{
  public:
    void setup();
    void update();
    void draw();

private:
  //**********************************************************************************************
  //*********************************** Commun ***************************************************
  //**********************************************************************************************

  int mActDepth;
  int mMaxDepth; 
  int mSleepTime;    // delay between drawing;

  enum class LSYS { SNWFLK, CIRCLE, GDLSPR, PLANT, TRIGLTRIGO, NBLSYS }; 
  LSYS runLSysType; // Type of Lsys to execute


  ofImage mImg;        // for Automatic savescreen  
  bool mSaveImgResult;
  void saveResult(string imgName);


  //**********************************************************************************************
  //*********************************** Snowflakes ***********************************************
  //**********************************************************************************************
  int mMainArmLenght;
  int mCptAngleSnwflk;     
  vector<float> mArmPosY; //position on main arms
  
  void drawLsysSnowflk();
  void drawnSysSnow(float lenght, float angleDeg, int depth);
  void drawnSnowExt(int posy, float lenght, float angleDeg, int depth);
  void drawArms(int lenght, int depth, bool doAngZero);


  //**********************************************************************************************
  //*********************************** Circles ************************************************** 
  //**********************************************************************************************
  int mMainCircleRadius;
  int mNbEndCircles;
  int mNbStartCircles;
  
  bool mFillCenter;
  bool mDrawHalfTransparent;

  void drawLsysCircle();
  void addCircles(float radius, int circleNbPerPerim, int depth, int transparent);


  //**********************************************************************************************
  //*********************************** Golden Spirals ******************************************* 
  //**********************************************************************************************
           
  int mStartRadIdx;     // minimum index of nbOr radius at which starting the drawing
                       // of the sequence of the main spiral
  int mCptStartRadIdx;  // actual index of nbOr 
  vector<int> mNbOr;    // Fibonacci numbers
  int maxRadIdx;

  void drawLsysNbDor();
  void drawArc(int posx, int posy, int radIdx, int maxRadId, float startAngl, int depth);
  void relocateArc(const ofPolyline& pl1, int radIdx, float agn, int minRadId, int depth,
                   int reducRad, float scal, float pct);

  //**********************************************************************************************
  //*********************************** Plants ***************************************************
  //**********************************************************************************************
  bool mIsClckwise;
  int mMainLineLengt;
  int mMainLineThckns;
  float mReductionRatio;            

  float mAngleStart;
  float mAngleIncremt;  
  float mChgDirAngle; // change rotation direction when angle reach chgDirAngle. 
                      // Nothing interesting really about the picture after 60 deg.
  
  void drawLsysPlant();
  void splitPlant(int iX, int iY, int iThickness, float iAngle);

  //**********************************************************************************************
  //*********************************** Triangle *************************************************
  //**********************************************************************************************
  int mTriRadius;
  
  void drawLsysTriTrigo();
  void drawLTriTrigo(int radius, int depth);
 };

renderer.cpp

// By Bernard Motheron

#include "Renderer.h"
#include "ofMathConstants.h"

using namespace std;

void Renderer::setup()
{
  runLSysType = LSYS::SNWFLK;
  mSaveImgResult = false;

  switch (runLSysType)
  {
    case LSYS::CIRCLE: 
      {
        mFillCenter          = true;
        mDrawHalfTransparent = true;

        mActDepth         =   0;
        mMaxDepth         =   6;
        mNbEndCircles     =   6;         
        mNbStartCircles   =   4;
        mNbEndCircles     =  10;
        mSleepTime        = 250;
        mMainCircleRadius = ofGetWindowHeight() / 8;
      }
      break;

    case LSYS::SNWFLK:
      {
        mMaxDepth = 7;
        mCptAngleSnwflk = 0;
        mMainArmLenght = ofGetWindowHeight() / 2 - 100;
        mArmPosY   = { -0.25, 0.25, 0.6, 0.8, 0.9, 1, 0 };
      }
      break;

    case LSYS::GDLSPR:
      {
        mMaxDepth       =  4;
        mStartRadIdx    = 10; // must be < nbOr.size() - 2   and > 0
        mSleepTime      = 500;
        mCptStartRadIdx = mStartRadIdx - 1;             

        mNbOr = {1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987}; 
      }
      break;

    case LSYS::PLANT:
      {
        mAngleStart    =   0;
        mMainLineThckns =  20; 
        mChgDirAngle    =  60;         
        mMainLineLengt  = ofGetWindowHeight() / 4;
        mAngleIncremt   =   0.25;
        mReductionRatio =   0.75;                      

        mIsClckwise   = false;
      }
      break; 

    case LSYS::TRIGLTRIGO:
      {
        mMaxDepth = 4;
        mTriRadius = ofGetWindowHeight() / 4 - 20;
      }
      break;
  }
}


void Renderer::update()
{ 
  switch (runLSysType)
  {
    case LSYS::CIRCLE: 
      { 
        ++mActDepth;
        if (mActDepth == mMaxDepth)
        {
          mActDepth = 0;
          if (mNbStartCircles < mNbEndCircles)
          {
            ++mNbStartCircles;
          }
        } 
      } 
      break;
    case LSYS::SNWFLK: { mCptAngleSnwflk += 1;} break;
    case LSYS::GDLSPR: 
      { 
        ++mCptStartRadIdx;
        Sleep(mSleepTime);
        if (mCptStartRadIdx > static_cast<int>(mNbOr.size() - 2)) { mCptStartRadIdx = mStartRadIdx; }
      } 
      break;
    case LSYS::PLANT:  
      {         
        if (!mIsClckwise) {mAngleStart -= mAngleIncremt;  }
        else             {mAngleStart += mAngleIncremt; }

        if (mAngleStart == -mChgDirAngle or mAngleStart == 0) { mIsClckwise = !mIsClckwise; }
      } 
      break;
    case LSYS::TRIGLTRIGO: {} break;    
  }  
}

void Renderer::draw()
{
  if (LSYS::GDLSPR == runLSysType) { ofScale(0.8, 0.8, 0.8); }  // adjust according to screen size for the largest spiral
  
  ofSetLineWidth(1);
  ofTranslate(ofGetWindowSize().x / 2, ofGetWindowSize().y / 2);

  switch (runLSysType)
  {
    case LSYS::CIRCLE:     { drawLsysCircle();   } break;
    case LSYS::SNWFLK:     { drawLsysSnowflk();  } break;
    case LSYS::GDLSPR:     { drawLsysNbDor();    } break;
    case LSYS::PLANT:      { drawLsysPlant();    } break;
    case LSYS::TRIGLTRIGO: { drawLsysTriTrigo(); } break;
  }
}     

//**********************************************************************************************
//***********************************  Circles  ************************************************
//**********************************************************************************************

void Renderer::drawLsysCircle()
{
  if (mNbStartCircles < mNbEndCircles)
  {
    mFillCenter ? ofFill() : ofNoFill();
    ofSetCircleResolution(100);
    ofSetColor(255 / (mActDepth + 1), 0, 0, 51 * mActDepth);
    ofDrawCircle(0, 0, mMainCircleRadius);

    int tp = mDrawHalfTransparent ? 128 : 0;
    
    addCircles(mMainCircleRadius, mNbStartCircles, 0, tp);

    if (mSaveImgResult)
    {
      saveResult("circleMainFill_FilledTrans_nb" + std::to_string(mNbStartCircles) + "_dpt" + std::to_string(mActDepth) + ".png");
    }      
  }
  Sleep(mSleepTime);
}

void Renderer::addCircles(float radius, int circleNbPerPerim, int depth, int transparent)
{
  if (depth <= mActDepth)
  {
    float angle = TWO_PI / circleNbPerPerim;
    ++depth;
    for (int i = 0; i < circleNbPerPerim; ++i)
    {
      ofPushMatrix();
      ofRotateRad(angle * i);
      depth % 2 == 0 ?   ofFill() : ofNoFill();
      switch (depth)
      {
        case 1: { ofSetColor(128,   0,   0, transparent); } break; 
        case 2: { ofSetColor(  0, 128,   0, transparent); } break; 
        case 3: { ofSetColor(  0,   0, 128, transparent); } break; 
        case 4: { ofSetColor(255,   0,   0, transparent); } break; 
        case 5: { ofSetColor(  0, 255,   0, transparent); } break; 
        case 6: { ofSetColor(  0,   0, 255, transparent); } break; 
        case 7: { ofSetColor(128, 128, 128, transparent); } break; 
       }
           
      ofDrawCircle(0, radius, radius * sin (angle / 2));    
      ofTranslate( 0, radius);
      addCircles(radius * sin (angle / 2), circleNbPerPerim, depth, transparent);
      ofPopMatrix();     
    }   
  }
}


//**********************************************************************************************
//***********************************   Snowflakes  ********************************************
//**********************************************************************************************
void Renderer::drawLsysSnowflk()
{          
  for (int i = 0; i < 8; ++i)
  {
    drawnSysSnow(mMainArmLenght, 45 * i, 0);
  }

  //savescreen  5 deg
  if (mSaveImgResult && mCptAngleSnwflk % 5 == 0)
  {
    saveResult("Snwflk_" + std::to_string(mCptAngleSnwflk) + ".png");
  }  
}

void Renderer::drawnSysSnow(float lenght, float angleDeg, int depth)
{
    ofPushMatrix();
    ofRotateDeg(angleDeg);
    ofDrawLine(0, 0, 0, lenght);    
    ofSetLineWidth(3);
    ofSetColor(0, 0, 102);
    drawArms(lenght, depth, true); 
    ofPopMatrix();      
}

void Renderer::drawArms(int lenght, int depth, bool doAngZero)
{
  for (int i = 1; i < static_cast<int>(mArmPosY.size()); ++i)
  {
    ++depth;
    for (int j = 0; j < 3; ++j)
    {
      if (j == 1 and !doAngZero) { continue; }
      drawnSnowExt(lenght * mArmPosY[i], (lenght * mArmPosY[i] - lenght * mArmPosY[i - 1]) / 2 , mCptAngleSnwflk * (1 - j), depth);
    }
  }
}

void Renderer::drawnSnowExt(int posy, float lenght, float angleDeg, int depth)
{
  if (depth < mMaxDepth)
  {           
    ofPushMatrix();    
    ofTranslate(0, posy);
    ofRotateDeg(angleDeg);

    ofSetLineWidth(5);
    ofSetColor(0, 0, 153);
    ofDrawLine(0, 0, 0, lenght);

    ofSetColor(153, 204, 255);
    ofSetLineWidth(3);
    ofDrawLine(0, 0, 0, lenght);   

    drawArms(lenght, depth, false);      
    ofPopMatrix();    
  }
}

//**********************************************************************************************
//***********************************   Golden spirals  ****************************************
//**********************************************************************************************

void Renderer::drawLsysNbDor()
{
  ofTranslate(-mNbOr[mCptStartRadIdx + 1], ofGetScreenHeight() / 8);  

  int depth = mNbOr.size() - 2 - mCptStartRadIdx;
  for (int i = 0; i < 4; ++i)
  {
    int mlpt = i == 3 ? 1: i;
    drawArc(mNbOr[mCptStartRadIdx + 1] * mlpt, mNbOr[mCptStartRadIdx + 1] * -sin(HALF_PI * i), mCptStartRadIdx, 2, HALF_PI * i, depth);
  }

  if (mSaveImgResult)
  {
    saveResult("GdlnSpr_wht_blckbgrnd_rad1-p5-0" + std::to_string(mCptStartRadIdx) + ".jpg");
    mSaveImgResult = !(depth == 0);
  }
}

void Renderer::drawArc(int posx, int posy, int radIdx, int minRadId, float startAngl, int depth)
{  
  posx += mNbOr[radIdx - 1] * cos(startAngl);
  posy += mNbOr[radIdx - 1] * sin(startAngl);

  float agn = startAngl + HALF_PI;  
  ofSetLineWidth(radIdx / 2);
  //ofSetColor(0, ofRandom(128, 255), 0); 
  ofPolyline pl1; 
  pl1.arc(posx,  posy, mNbOr[radIdx], mNbOr[radIdx], startAngl * RAD_TO_DEG, agn * RAD_TO_DEG, 100);
  pl1.draw();

  if (radIdx > minRadId)
  {        
    drawArc(posx, posy, --radIdx, minRadId, agn, depth);     

    if (radIdx >= minRadId and depth <= mMaxDepth)
    { 
      relocateArc(pl1, radIdx, agn, minRadId, ++depth, 1, 1.0, 1.0);  

      if (radIdx >= 4 and depth == 1)
      { 
        relocateArc(pl1, radIdx, agn, minRadId, depth, 3, 1.0, 0.5); 
        relocateArc(pl1, radIdx, agn, minRadId, depth, 2, 0.7, 0.0);
      }
    }        
  } 
}

void Renderer::relocateArc(const ofPolyline& pl1, int radIdx, float agn, int minRadId, int depth,
                           int reducRad, float scal, float pct)
{
  ofPushMatrix();
  ofDefaultVertexType dft3 = pl1.getPointAtPercent(pct);
  dft3.x += mNbOr[radIdx - reducRad + 1] * cos(agn);
  dft3.y += mNbOr[radIdx - reducRad + 1] * sin(agn);
  ofTranslate(dft3.x, dft3.y);
  ofScale(scal, scal, scal);
  drawArc(0, 0, radIdx - reducRad, minRadId, agn + PI, depth);
  ofPopMatrix();  
}

//**********************************************************************************************
//***********************************  Plants **************************************************
//**********************************************************************************************

void Renderer::drawLsysPlant()
{        
  ofClear(0);
  ofTranslate(0, ofGetWindowHeight() / 2);

  ofPushMatrix();
  ofScale(1.0f, -1.0f);
  ofFill();
  ofSetLineWidth(10);
  ofDrawLine(0, 0, 0, mMainLineLengt);             
                                                                    
  splitPlant(0, mMainLineLengt, mMainLineThckns, mAngleStart); 
  ofPopMatrix();

  if (mSaveImgResult and mAngleStart != 0 and static_cast<int>(mAngleStart * 10) % 50 == 0)
  {
    // .jpg to save with black background, .png for transparent background.
    saveResult("Plant_angle_" + std::to_string(static_cast<int>(abs(mAngleStart * 10)) / 10) + ".jpg");
  } 
}

void Renderer::splitPlant(int iX, int iY, int iThickness, float iAngle)
{ 
  if (iThickness - 1 > 0)
  {
    ofPushMatrix();
    ofTranslate(iX, iY);
    ofRotateZDeg(iAngle);
    ofSetLineWidth(iThickness - 1);
    ofDrawLine(0, 0, iX * mReductionRatio, iY * mReductionRatio);
    splitPlant(iX * mReductionRatio, iY * mReductionRatio, iThickness - 1, iAngle);
    ofPopMatrix();

    ofPushMatrix();
    ofDrawLine(0, 0, iX * mReductionRatio, iY * mReductionRatio);
    ofSetColor(65, ofRandom(255), 0);
    splitPlant(iX * mReductionRatio, iY * mReductionRatio, iThickness - 2, -iAngle);
    ofPopMatrix();
  }       
}

//**********************************************************************************************
//*************************************** Triangles ********************************************
//**********************************************************************************************
void Renderer::drawLsysTriTrigo()
{
  ofNoFill();
  drawLTriTrigo(mTriRadius, mMaxDepth);

  if (mSaveImgResult)
  {
    saveResult("Triangle.jpg");
    mSaveImgResult = false;
  } 
}

void Renderer::drawLTriTrigo(int radius, int depth)
{
  if (depth >= 0)
  {
    --depth; 
    vector<ofPoint> vPoint(3); // template triangle to draw
    for (int i = 0; i < 3; ++i)
    { 
      float ang = i * TWO_PI / 3 + PI / 6;
      vPoint[i] = ofPoint(radius * cos(ang), radius * sin(ang), 0);
    }

    // draw 3 times the triangle template
    for (int triNo = 0; triNo < 3; ++triNo)
    {
      glPushMatrix();
        ofTranslate(vPoint[triNo].x, vPoint[triNo].y);     
        ofDrawTriangle(vPoint[0].x, vPoint[0].y, vPoint[1].x, vPoint[1].y, vPoint[2].x, vPoint[2].y);
        drawLTriTrigo(radius / 2, depth);
      glPopMatrix();
    }           
  }
}


//**********************************************************************************************
//***********************************  Automatic savescreen ************************************
//**********************************************************************************************

// save images at [projectName]\bin\data
void Renderer::saveResult(string imgName)
{
  mImg.grabScreen(0, 0, ofGetWidth(), ofGetHeight());
  mImg.save(imgName);
}

main.cpp (275 Bytes)
ofApp.cpp (396 Bytes)
ofApp.h (220 Bytes)
Renderer.cpp (12.2 KB)
Renderer.h (3.8 KB)

5 Likes