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)