12constexpr float kPi = 3.14159265358979323846f;
16float clampUnit(
float value) {
return std::max(0.f, std::min(1.f,
value)); }
21 std::fill(std::begin(leafChars_), std::end(leafChars_),
false);
27 rules_[
static_cast<unsigned char>(symbol)] = {Rule{production, 1.f}};
31 const std::vector<float>&
weights) {
32 auto&
target = rules_[
static_cast<unsigned char>(symbol)];
33 const size_t count = std::min(productions.size(),
weights.size());
35 for (
size_t i = 0; i <
count; ++i) {
37 target.emplace_back(productions[i],
w);
42 for (
auto& slot : rules_) slot.clear();
51 const float n = std::sqrt(
x *
x +
y *
y +
z *
z);
52 if (
n <= 1e-9f)
return;
53 heading0_ = {
x /
n,
y /
n,
z /
n};
61 std::fill(std::begin(leafChars_), std::end(leafChars_),
false);
62 for (
char c : symbols) leafChars_[
static_cast<unsigned char>(
c)] =
true;
70LSystem::Vec3 LSystem::rotate(
const Vec3&
v,
const Vec3& axis,
float angle)
const {
71 const float c = std::cos(
angle);
72 const float s = std::sin(
angle);
73 const float dot =
v.x * axis.x +
v.y * axis.y +
v.z * axis.z;
74 Vec3 cross{axis.y *
v.z - axis.z *
v.y, axis.z *
v.x - axis.x *
v.z,
75 axis.x *
v.y - axis.y *
v.x};
76 return {
v.x *
c + cross.x *
s + axis.x * dot * (1.f -
c),
77 v.y *
c + cross.y *
s + axis.y * dot * (1.f -
c),
78 v.z *
c + cross.z *
s + axis.z * dot * (1.f -
c)};
81void LSystem::orthonormalize(Vec3&
heading, Vec3&
up)
const {
86 if (
n <= 1e-9f)
return;
92 n = std::sqrt(nextUp.x * nextUp.x + nextUp.y * nextUp.y + nextUp.z * nextUp.z);
93 if (
n > 1e-9f)
up = {nextUp.x /
n, nextUp.y /
n, nextUp.z /
n};
97 std::string
word = axiom_;
98 std::mt19937 rng(seed_);
99 std::uniform_real_distribution<float>
unit(0.f, 1.f);
100 for (
int iter = 0; iter < iterations_; ++iter) {
102 next.reserve(
word.size() * 2 + 16);
103 for (
char ch :
word) {
104 const std::vector<Rule>& ruleSet = rules_[
static_cast<unsigned char>(ch)];
105 if (ruleSet.empty()) {
109 if (ruleSet.size() == 1) {
110 next += ruleSet[0].first;
114 for (
const Rule&
r : ruleSet) total +=
r.second;
116 next += ruleSet[0].first;
119 const float pick = total *
unit(rng);
121 const std::string* chosen = &ruleSet[0].first;
122 for (
const Rule&
r : ruleSet) {
137 const float angle = radians(angleDeg_);
139 t.pos = {0.f, 0.f, 0.f};
140 t.heading = heading0_;
141 t.up = {0.f, 0.f, 1.f};
143 orthonormalize(
t.heading,
t.up);
145 std::vector<TurtleState> stack;
147 for (
char ch :
word) {
151 if (tropism_.x != 0.f || tropism_.y != 0.f || tropism_.z != 0.f) {
152 t.heading = {
t.heading.x + tropism_.x * 0.02f,
153 t.heading.y + tropism_.y * 0.02f,
154 t.heading.z + tropism_.z * 0.02f};
155 const float n = std::sqrt(
t.heading.x *
t.heading.x +
156 t.heading.y *
t.heading.y +
t.heading.z *
t.heading.z);
157 if (
n > 1e-9f)
t.heading = {
t.heading.x /
n,
t.heading.y /
n,
t.heading.z /
n};
158 orthonormalize(
t.heading,
t.up);
161 const float scale = std::pow(radiusFalloff_,
float(
t.depth));
162 const float r0 = branchRadius_ *
scale;
163 t.pos = {
t.pos.x +
t.heading.x * step_,
t.pos.y +
t.heading.y * step_,
164 t.pos.z +
t.heading.z * step_};
165 const float r1 = branchRadius_ * std::pow(radiusFalloff_,
float(
t.depth + 1));
168 seg.ex =
t.pos.x; seg.ey =
t.pos.y; seg.ez =
t.pos.z;
169 seg.r0 = r0; seg.r1 = r1; seg.depth =
t.depth;
174 t.pos = {
t.pos.x +
t.heading.x * step_,
t.pos.y +
t.heading.y * step_,
175 t.pos.z +
t.heading.z * step_};
177 case '+':
t.heading = rotate(
t.heading,
t.up,
angle); orthonormalize(
t.heading,
t.up);
break;
178 case '-':
t.heading = rotate(
t.heading,
t.up, -
angle); orthonormalize(
t.heading,
t.up);
break;
181 orthonormalize(
t.heading,
t.up);
182 right = {
t.up.y *
t.heading.z -
t.up.z *
t.heading.y,
183 t.up.z *
t.heading.x -
t.up.x *
t.heading.z,
184 t.up.x *
t.heading.y -
t.up.y *
t.heading.x};
188 orthonormalize(
t.heading,
t.up);
193 orthonormalize(
t.heading,
t.up);
194 right = {
t.up.y *
t.heading.z -
t.up.z *
t.heading.y,
195 t.up.z *
t.heading.x -
t.up.x *
t.heading.z,
196 t.up.x *
t.heading.y -
t.up.y *
t.heading.x};
200 orthonormalize(
t.heading,
t.up);
203 case '\\':
t.up = rotate(
t.up,
t.heading,
angle); orthonormalize(
t.heading,
t.up);
break;
204 case '/':
t.up = rotate(
t.up,
t.heading, -
angle); orthonormalize(
t.heading,
t.up);
break;
210 if (!stack.empty()) {
216 if (leafChars_[
static_cast<unsigned char>(ch)]) {
218 seg.sx =
t.pos.x; seg.sy =
t.pos.y; seg.sz =
t.pos.z;
219 seg.ex =
t.pos.x; seg.ey =
t.pos.y; seg.ez =
t.pos.z;
220 seg.r0 = 0.f; seg.r1 = 0.f; seg.depth =
t.depth;
221 seg.leaf =
true; seg.leafSize = leafSize_;
222 seg.dx =
t.heading.x; seg.dy =
t.heading.y; seg.dz =
t.heading.z;
242 out.
add(seg.sx, seg.sy, seg.sz);
243 out.
add(seg.ex, seg.ey, seg.ez);
std::array< float, 3 > scale
void setAxiom(const std::string &axiom)
Set the starting string.
void toPointSet(PointSet &out) const
Generate and pack segment endpoints into a PointSet.
void setIterations(int iterations)
Set the grammar expansion count.
void setBranchRadiusFalloff(float factor)
Set the per-depth radius multiplier.
void generate(LSystemResult &out) const
Expand then interpret with the turtle.
void setStep(float step)
Set the forward distance per F.
std::string derive() const
Expand the grammar iterations times.
void setLeafSymbols(const std::string &symbols)
Set the set of symbols that emit a leaf marker.
void setInitialHeading(float x, float y, float z)
Set the initial heading (normalized).
int getIterations() const
Current iteration count.
uint32_t getSeed() const
Current seed.
void setTropism(float x, float y, float z)
Bias growth toward a direction (phototropism).
void setLeafSize(float size)
Set the foliage card size.
void addRules(char symbol, const std::vector< std::string > &productions, const std::vector< float > &weights)
Add weighted (stochastic) productions for one symbol.
void clearRules()
Remove all production rules (the axiom and alphabet remain).
void setBranchRadius(float radius)
Set the trunk radius at depth 0.
void setAngle(float degrees)
Set the turtle turn angle.
void addRule(char symbol, const std::string &production)
Add a deterministic production for one symbol.
void setSeed(uint32_t seed)
Set the deterministic seed.
Script-friendly collection of attributed 3D samples.
int add(float x, float y, float z)
Full result of expanding an L-system grammar and tracing the turtle.
std::vector< LSystemSegment > segments
One drawn segment produced by an L-system turtle.