General
Follow


MATLAB's Fractal Art

Teodo on 4 Nov 2024 (Edited on 7 Nov 2024 at 14:47)
Latest activity Edit by Teodo on 7 Nov 2024 at 14:47

At the onset of each week, I release a post that analyzes code with the intent of making it accessible for beginners, while also providing insights that can benefit more experienced users seeking to learn new techniques or approaches.
This week, my inspiration comes from the fractal art produced in MATLAB, as presented in my entry Whispers of the Ocean's Breeze:
MATLAB Shorts Mini Hack entry: Whispers of the Ocean's Breeze
Below, I offer a pretty detailed walkthrough of the code break-down, with the goal of creating both an educational and stimulating experience for those eager to learn or find some inspiration. Taking into account that this post is somewhat lengthy, it provides a breakdown and summary of various techniques. It is my hope that it will assist someone and allow readers to focus on the sections that are of most interest to them.
While the code contains comments, this post offers additional explanations and details.
1. Function Definition and Metadata
function drawframe(f)
Line 1: Defines the main function drawframe, which takes a single parameter f. This parameter controls various aspects of the animation, such as movement or speed.
% Audio source: Klapa Šibenik (comp. Arsen Dedić) -
% - Zaludu me svitovala mati
% (Hrvatska 🇭🇷, (Dalmacija))
% Enhanced aesthetics and added dynamic movement,
% offering a creative Remix of my earlier concept.
% (This version brings richer visual appeal,smoother transitions,
% and a more engaging animation flow)
Lines 2-4: Commented-out lines providing metadata or notes about the code. These comments describe the aesthetic goals and improvements in this version, highlighting that it's a remix of one of my earlier entry's, with added dynamic movement and smoother visuals. (These notes are not executed by MATLAB.)
A general tip for using comments: include comments in your code as frequently as needed. They serve as helpful reminders of what each part of the code does, especially when you revisit it after some time, and make it easier for others who may read or use your code.
2. Function Call to seaweed
seaweed(4) % The value in brackets can be adjusted for significantly
% enhanced visualization,
% but it exceeds the 235-second limit in the contest script.
% Feel free to experiment at Desktop workstation - with higher
% values in the loop,
% for more complex and beautiful results.
Line 5: Calls the seaweed function with an argument of 4. The number 4 controls the recursion depth, affecting how detailed or complex the 'seaweed' pattern will be.
Lines 6-9: Comment explaining that increasing the value in seaweed(4) enhances visualization but may exceed time limits in contest environment(s). This suggests adjusting this parameter on a desktop to explore more intricate patterns...
3. Definition of seaweed Function
function seaweed(k)
% Set up the figure window with a specific position and background color
figure('Position', [60+2*f, 60-2*f, 600, 600], 'Color', [0.15, 0.15, 0.5]);
Line 10: Defines the seaweed function, which takes a parameter k (depth of recursion). This function initializes the graphical figure window.
Line 12: Sets up the figure window with a position influenced by f, making the figure’s position change dynamically with f. The background color [0.15, 0.15, 0.5] creates a dark blue background, enhancing the underwater aesthetic.
4. Recursive Drawing with crta Function
crta([0, 0], 90, k, k);
% Make the axes equal and turn them off for a clean figure
axis equal
axis off
Line 14: Calls the recursive function crta, starting the drawing process at [0, 0](origin point) with a 90-degree angle. Both k values set the initial recursion depth and maximum recursion level.
Lines 16-17: Sets the axis scale to equal, ensuring no distortion, and turns off the axes for a cleaner display.
5. Definition of crtaFunction and Initialization of Parameters
function crta(tck, ugao, prstiter, r)
% Define thickness of line segment proportional to current depth
sir = 5 * (prstiter / r);
% Define length of the line segment
duz1 = 5 * prstiter;
Line 18: Defines crta, a nested function within seaweed, taking parameters tck (current coordinates), ugao (angle), prstiter (current recursion depth), and r (maximum recursion depth).
Lines 20-21: Defines sir (line thickness) to be proportional to the current recursion depth, prstiter, creating thinner branches as depth increases. While, duz1 defines the line segment length, which shortens with each recursion, creating perspective.
6. Angle Calculations for Branching
% Define four branching angles with slight variations
ug1 = ugao + 15 + (f / 15);
ug2 = ugao + 7 - f / 15;
ug3 = ugao - 7 + f / 15;
ug4 = ugao - 15 - f / 15;
Lines 23-27: Sets branching angles ug1, ug2, ug3, and ug4 relative to the initial angle ugao, adding and subtracting small amounts. These angles, influenced by f, introduce subtle variations, enhancing the natural appearance.
7. Calculations for Branch Endpoints
% Calculate endpoints of each line segment for the four angles
a1 = duz1 * sind(ug1) + tck(2);
b1 = duz1 * cosd(ug1) + tck(1);
c2 = duz1 * sind(ug2) + tck(2);
a2 = duz1 * cosd(ug2) + tck(1);
b3 = duz1 * sind(ug3) + tck(2);
c3 = duz1 * cosd(ug3) + tck(1);
d4 = duz1 * sind(ug4) + tck(2);
e4 = duz1 * cosd(ug4) + tck(1);
Lines 29-36: Calculates x and y endpoints for each of the four branches using sind and cosd functions, which convert angles into coordinates. Each branch starts at tck(current In this section, the code calculates the endpoints of four line segments, each corresponding to a distinct angle (ug1, ug2,ug3, and ug4). These endpoints are computed based on the length of the line segment duz1, which scales with recursion depth to make each segment shorter as the recursive function progresses. The trigonometric functions sind and cosd are used here to calculate the horizontal and vertical displacements of each segment relative to the current position, tck. While, sind and cosd functions compute the sine and cosine of each angle in degrees, returning the y and x displacements, respectively. For each angle, multiplying by duz1scales these displacements to achieve the intended length for each line segment. Each endpoint coordinate is calculated by adding these displacements to the initial position, tck, to determine the final position for each branch segment:
  • a1 and b1 represent the y and x endpoints for the segment at angle ug1
  • c2 and a2 represent the y and x endpoints for the segment at angle ug2
  • b3 and c3 represent the y and x endpoints for the segment at angle ug3
  • d4 and e4 represent the y and x endpoints for the segment at angle ug4
These coordinates form the four main branches radiating out from the current position in different directions. By varying the angle slightly for each branch and scaling the length proportionally, the function generates a visually rich, organic branching structure that resembles seaweed or other natural patterns.
8. Midpoint Calculations for Additional Complexity
% Calculate midpoints for additional "leaves" to
% simulate complexity
uga1 = ug2 - 5 + f / 5;
ugb2 = ug3 + 5 - f / 5;
uga2 = duz1 / 2 * sind(uga1) + c2 + f / 20;
ugb3 = duz1 / 2 * cosd(uga1) + a2 - f / 20;
ugc2 = duz1 / 2 * sind(ugb2) + b3 + f / 20;
ugda1 = duz1 / 2 * cosd(ugb2) + c3 - f / 20;
Lines 38-44: Calculates midpoint angles and positions for extra leaf” structures. This further enhances the fractal appearance by adding more detail, as these points fall between main branches!
Additional midpoints are calculated to add further detail and complexity to the fractal pattern. These midpoints represent extra branches or leaves that emerge from within the main branch segments, enhancing the natural, organic appearance of the fractal structure. Consequently, uga1 and ugb2 are new angles derived by slightly modifying the main branch angles ug2and ug3. The adjustments are made by adding and subtracting small values, including a component based on f. These subtle variations create slight deviations in the angles of the additional branches, making them appear more random and organic, like leaves growing off main stems in varied directions. Once the new angles uga1 and ugb2 are defined, they are used to calculate intermediate coordinates along the main branch lines. These midpoints are positioned halfway along each branch segment, representing the location from which the extra “leaf” branches will emerge.
To find these midpoints:
  • uga2 and ugb3 use sind(uga1) and cosd(uga1)to calculate the y and x coordinates halfway along the segment for angle ug2.
  • ugc2 and ugda1 similarly use sind(ugb2) and cosd(ugb2) to get the coordinates for angle ug3.
Each midpoint calculation also includes a slight additional offset based on f (like f / 20), adding variation in their positions and contributing to the irregular, natural look of the structure.
By adding these secondary branches, the fractal pattern gains more intricacy. These “leaves” give a more complex and dense appearance, resembling the growth patterns of plants or seaweed where smaller branches diverge from main stems. The addition of midpoints also contributes to the overall depth and richness of the fractal design, ensuring that each recursive call doesn’t simply repeat but also grows in visual detail, making the resulting fractal more visually appealing and realistic. The midpoint calculations thus play a crucial role in enhancing the visual complexity of the fractal by introducing smaller, secondary branches that break up the regularity of the main branches, making the structure more detailed and lifelike.
9. Color Definition Based on Depth
% Define color based on depth, simulating a gradient effect
% as recursion deepens
boja = [1 - (prstiter / r), 1 - 0.5 * (prstiter / r), 0];
Line 46: Defines the color boja as a gradient that shifts from yellow to dark orange based on recursion depth. This gradient effect enhances the visual depth of the pattern.
This code sets up a color gradient for each branch segment based on its recursion depth. This approach not only adds aesthetic appeal but also visually separates different levels of recursion, making it easier to perceive depth within the fractal. The variable boja is an RGB color array, where each element represents the intensity of red, green, and blue respectively, on a scale from 0 (no intensity) to 1(full intensity).
The first element, 1 - (prstiter / r), controls the red component. The second element, 1 - 0.5 * (prstiter / r), controls the green component. The third element is set to 0, meaning there is no blue in the color, resulting in a gradient that shifts from yellow (where both red and green are high) to darker orange and then brownish tones as recursion deepens. The color gradually shifts from a bright yellowish tone at shallow recursion levels to a darker, warmer orange as recursion depth increases. This is achieved by gradually decreasing the red and green components of the color as prstiter (current recursion depth) approaches r (maximum recursion depth). At the top levels of recursion (where prstiter is closer to r), the color becomes darker and more subdued, giving the branches a gradient that makes the structure look natural and complex. This effect is reminiscent of how colors in nature tend to fade or darken with distance or depth, such as in underwater scenes where light penetration decreases with depth.
The gradient serves as a visual cue that helps distinguish between different recursion levels. Since each level is progressively darker, viewers can intuitively sense the depth of each branch, which adds to the three-dimensional effect of the fractal. The use of warm colors (yellow to orange) for each branch segment helps the fractal pattern stand out vividly against the cool blue background set in the seaweed function. This color contrast enhances the underwater, organic look of the structure, making it appear as though the "seaweed" is reaching out toward a light source above. This coloring strategy also contributes to the fractal’s aesthetic complexity. By associating color depth with recursion depth, the fractal appears to have layers, creating a visually satisfying and realistic effect.
10. Plotting Branch Segments
% Plot main branches from the starting point (tck) to the calculated
% endpoints with color and transparency
p1 = plot([tck(1), b1], [tck(2), a1], 'LineWidth', sir, 'Color', boja);
hold on
s2 = plot([tck(1), a2], [tck(2), c2], 'LineWidth', sir, 'Color', boja);
s3 = plot([tck(1), c3], [tck(2), b3], 'LineWidth', sir, 'Color', boja);
s4 = plot([tck(1), e4], [tck(2), d4], 'LineWidth', sir, 'Color', boja);
% Plot secondary branches connecting midpoints for added detail
s5 = plot([a2, ugb3], [c2, uga2], 'LineWidth', sir, 'Color', boja);
s6 = plot([c3, ugda1], [b3, ugc2], 'LineWidth', sir, 'Color', boja);
Lines 48-56: Plots the main branches and secondary branches for added detail. Each plot command connects points with a specified thickness and color, creating the branching effect.
Here presented code, plots the main branches and additional leaf segments, giving form to the fractal pattern. Each plot command specifies a line segment by connecting two points, with attributes like line width (sir) and color (boja) enhancing the realism and aesthetic detail. Lines from p1to s4 represent a branch extending outward from the current point tck to its calculated endpoint. The branch segments p1, s2, s3, and s4 form the primary structure of the fractal by branching off at angles ug1, ug2, ug3, and ug4 respectively, calculated in earlier presented and explained steps. The plot command takes a pair of [x, y] coordinates that define the line’s start and end points. For instance, p1 = plot([tck(1), b1], [tck(2), a1], 'LineWidth', sir, 'Color', boja); draws a line from tck(the current position) to (b1, a1), one of the endpoints.
The arguments 'LineWidth', sir and 'Color', boja ensure that each line segment has a thickness and color appropriate to its recursion level, making higher-level branches thicker and more prominent while creating a natural gradient. The command hold on is crucial here, it allows MATLAB to draw multiple line segments within the same figure window without erasing the previous segments. This is necessary for the recursive nature of the fractal, as each call to crtaadds branches to the existing structure, layering them to form a complex, interconnected pattern. Lines s5 and s6 represent additional leafsegments, plotted between midpoints calculated in Section 8. These smaller branches diverge from the main branches, adding further intricacy and detail to the fractal. By connecting midpoints (such as a2 to ugb3 and c3 to ugda1), the code generates extra leafy offshoots that break up the regularity of the main branches.
These segments make the fractal look more organic, akin to the smaller branches and leaves one might see on real plants or seaweed. Similar to the main branches, the secondary branches use sir and boja for line width and color, ensuring consistent visual depth and blending them seamlessly into the overall pattern. This layering allows the fractal to resemble natural structures like foliage or underwater vegetation. The combination of primary and secondary branches contributes to both symmetry and asymmetry in the fractal. While the primary branches provide a balanced, four-way split, the secondary branches introduce slight irregularities, which lend an organic feel to the pattern. Finally, by plotting each segment separately, the code achieves a highly customizable structure. Line thickness, color, and endpoint coordinates can be easily adjusted for each recursion level, allowing flexibility in the appearance and feel of the fractal.
11. Setting Transparency and Recursion
% Set transparency for each plot segment
s1.Color(4) = 0.95;
s2.Color(4) = 0.95;
s3.Color(4) = 0.95;
s4.Color(4) = 0.95;
s5.Color(4) = 0.95;
s6.Color(4) = 0.95;
% Continue recursive drawing if there are levels left ( prstiter > 0)
if prstiter - 1 > 0
% Recursive calls for each of the main branches with
% updated angles and decreased recursion depth
crta([b1, a1], ug1, prstiter - 1, r);
crta([ugb3, uga2], uga1, prstiter - 1, r);
crta([ugda1, ugc2], ugb2, prstiter - 1, r);
crta([e4, d4], ug4, prstiter - 1, r);
end
end
end
Lines 58-63: Sets the transparency of each branch segment to 0.95, creating a slightly translucent effect.
Lines 65-71: Checks if recursion should continue (i.e., if prstiter > 0). If so, the crta function recursively calls itself with updated angles and positions, generating the next level of branching until prstiter reaches the value of 0.
This code applies transparency to each branch segment to enhance the visual layering effect and initiates further recursion for drawing deeper levels of the fractal. Lines s1.Color(4) = 0.95; through s6.Color(4) = 0.95; apply transparency to each of the plot segments, allowing branches to be slightly see-through. In MATLAB, the fourth element of the Color property, Color(4), represents the alpha (transparency) value. That is, setting it to 0.95 makes each branch segment 95% opaque, meaning it is just translucent enough to create a layered effect where overlapping branches blend slightly. This subtle transparency creates depth, giving the impression that some branches are behind others, which enhances the natural, three-dimensional appearance of the fractal structure. The transparency effect also softens the overall image, making the fractal appear less rigid and more fluid in water.
Line: if prstiter - 1 > 0, checks if further recursion should occur by verifying that prstiter (the current recursion depth) is greater than 1. If prstiter is greater than 1, the function proceeds to recursively call crta, reducing prstiter by 1 with each call. This gradual reduction in prstiter ensures that recursion continues until the maximum depth, defined by r, is reached. As the recursion depth decreases with each call, the branch segments become progressively shorter and thinner, creating a tapered effect that adds to the realistic, fractal-like branching.
Recursive Calls of the function crta, calls itself four times, once for each main branch direction (ug1, ug2, ug3, ug4), using updated coordinates and angles:
  • crta([b1, a1], ug1, prstiter - 1, r); initiates a recursive call for the branch at angle ug1.
  • crta([ugb3, uga2], uga1, prstiter - 1, r); starts recursion from the midpoint branch at angle uga1.
  • crta([ugda1, ugc2], ugb2, prstiter - 1, r); continues recursion from the midpoint branch at angle ugb2.
  • crta([e4, d4], ug4, prstiter - 1, r); initiates recursion from the branch at angle ug4.
Each recursive call passes a new starting point (calculated in previous steps) and an adjusted angle. These recursive calls add the next level of branching, gradually building out the entire fractal structure. The recursive calls are fundamental to constructing the fractal pattern. By creating multiple levels of branching, each progressively smaller and more complex, the fractal develops a rich, layered structure that mimics natural growth patterns. The recursive structure also allows for variations in each level, as each branch is influenced by slightly different angles and positions, resulting in an organic, non-uniform look. This natural irregularity is key to creating a visually appealing fractal. Additionally, since each recursive call has transparency applied to its branches, the resulting fractal has a soft, blended appearance. Overlapping branches appear to merge gently, creating a cohesive, three-dimensional visual effect.
End of Code
end
This line closes the entire drawframe function, completing the recursive fractal drawing of the seaweed structure.
Sometimes, neglecting to include the necessary closure for a function can lead to unexpected surprises in the code. Always be vigilant about ensuring that functions, loops, and other structures are properly closed.
Final result: Whispers of the Ocean's Breeze
Summary: This whole code uses recursion and geometry to create natural-looking, fractal-inspired patterns that mimic the movement and appearance of seaweed, achieving complexity and organic flow through simple recursive structure and dynamic angle variations.
Adam Danz
Adam Danz on 4 Nov 2024 at 19:56
Thank you for writing this article @Teodo!
> include comments in your code as frequently as needed.
👏🏻👏🏻👏🏻 I find great value when contributors add code comments. It saves the reader from having to reverse-engineer the code to understand the purpose and motivation. I wish I had written more code comments like yours in my previous contest entries.
I recognize some of your variable names (ugao, crta) 😊
Teodo
Teodo on 4 Nov 2024 at 23:15
Thank You! In fact, it was Tim's comments embedded in the code that initially motivated me. I share the same sentiment.
I believe all of us participants have grown as coders by engaging with and learning from each other in this fantastic MathWorks/MATLAB contest (MATLAB Shorts Mini Hack).
I really appreciate your recognition,❤️,😊, sometimes using variable names in our native language makes the code easier to follow...
Chen Lin
Chen Lin on 5 Nov 2024 at 14:05
'I believe all of us participants have grown as coders by engaging with and learning from each other in this fantastic MathWorks/MATLAB contest (MATLAB Shorts Mini Hack).' This is exactly why we are hosting the contest. Thank you again for publishing 3 high-quality articles. Let the learning journey go beyond the contests.
Teodo
Teodo on 5 Nov 2024 at 14:39
Thank you for the inspiration, your influence sparked an idea, and I just went ahead and completed the articles.
I’m hopeful, that this journey, will carry on well past the competition(s)...