The Mandelbrot set rendered with the minilibX

FRACT’OL

This article is just a corollary to the actual video tutorial, to clarify some “black pixels”

oceanO
11 min readAug 21, 2023

--

MiniLibX

Here you can find all you need to know (imo) about the minilibX, last section for the pixel put stuff that we are gonna briefly discuss ;).

This is the OG mlx docu i found (here’s revealed the my_pixel_put riddle!), but also this one is very good.

Pushing Pixels: The Efficient Way

Optimizing graphics performance is vital for a snappy, responsive interface. When creating a GUI, every millisecond counts. Here’s a streamlined explanation of pushing pixels efficiently to an image using MiniLibX:

The Slow Pixel Put

  • mlx_pixel_put: Although simple, it’s slow because it pushes pixels instantly without waiting for frame rendering.
  • Solution: Buffer pixels to an image before pushing to a window.

Setting Up Image in mlx

  • Understand the image type and how mlx functions handle them.
  • Initialize image pointers with relevant variables:

Image Initialization

Create an image of size 1920x1080:

#include <mlx.h>

int main(void)
{
void *img;
void *mlx;
mlx = mlx_init();
img = mlx_new_image(mlx, 1920, 1080);
}

Retrieving the Memory Address

After image initialization, get the memory address to write pixels:

#include <mlx.h>

typedef struct s_img
{
void *img_ptr;
char *img_pixels;
int bpp;
int size_line;
int endian;
} t_img;

int main(void)
{
void *mlx;
t_img img;
mlx = mlx_init();
img.img = mlx_new_image(mlx, 1920, 1080);

// pixels are the 👑 piece of data
img.img_pixels = mlx_get_data_addr(fractal->img.img_ptr,
&fractal->img.bpp,
&fractal->img.size_line,
&fractal->img.endian);
}

Calculating Memory Offset

Bytes aren’t aligned. Always use line length to calculate the memory offset:

offset = (y * fractal->img.size_line) + ((fractal->img.bpp / 8) * x);

…wait…aligned?

behind every top developer, there is youtube ♥️

Writing Pixels Efficiently

Mimic mlx_pixel_put’s function, but much faster:

// This is the actual code in the video, manipulating the fractal
void my_pixel_put(int x, int y, t_fractal *fractal, int color)
{
int offset;

offset = (y * fractal->img.size_line) + ((fractal->img.bpp / 8) * x);
*(unsigned int *)(fractal->img.img_pixels + offset) = color;
}

Displaying the Image

After writing pixels, push the image to the window with:

  • mlx_put_image_to_window(…);

In graphics, efficiency is key. Buffering and pushing pixels the right way ensures smooth transitions and seamless visuals. This method, though slightly more involved than directly writing to the window, provides a smoother experience for the user. You can see a clear example in my mlx tutorial video.

Mini Intro to the X server

Navigating through the matrix of the command-line interface in UNIX-like operating systems can seem like a task reserved for wizards. But, just as wizards wield wands to conjure magic, modern users yearn for the alchemy of a mouse click. Welcome to the world of graphical user interfaces (GUIs) on UNIX-like systems.

Understanding the Importance of GUI

  • The Great Divide: System administrators might be unfazed by lengthy shell commands. Still, average end-users often feel a sense of trepidation when confronted with the command line.
  • Mouse over Keyboard: Imagine trying to explain to your technologically-averse uncle that he needs to type commands to open a file. With GUI, the same uncle can comfortably double-click an icon.

Enter the X Window System

  • The X Window System as the foundation for the GUI on UNIX-like systems. Just as buildings vary in architecture while using similar foundational elements, different GUIs can be constructed on this system.
  • Like Windows but Different: The X Window System allows a user-experience reminiscent of Microsoft Windows, offering familiar components such as search fields, desktop icons, and adjustment options.

Understanding the GUI Components

  • Modularity is Key: Just as Lego blocks allow for countless constructions, the UNIX-like GUI is modular, letting you mix and match components to customize appearance and functionality.

X Server: The beating heart.

  • Draws windows graphically.
  • Manages mouse and keyboard inputs.
  • Can display output even on a remote system.

Mental Hook: X Server as the Building Constructor.

The X Server is akin to the skilled constructors and builders responsible for erecting the basic structure of each building. They lay down the foundational bricks, ensuring the building is robust, stands tall, and serves its primary purpose. In the same way, the X Server ensures the foundational structure of the Graphical User Interface (GUI) — it draws the windows, manages user inputs. Imagine a grand skyscraper in New York. Its magnificent structure is the result of meticulous planning and construction, by the X Server.

Window Manager: The stylist.

  • Determines how windows look and behave.
  • Choices range from elegant and complex managers, like Enlightenment and KWin, to minimalist ones, like fvwm.

If the X server is the skeleton of a building, the window manager is the interior designer determining aesthetics and usability.

Mental hook: Every building, regardless of its function or purpose, needs a foundational structure — the beams, columns, and walls that ensure it stands tall and remains robust. This skeleton is indispensable but, on its own, is stark and without character. Now, picture an interior designer stepping in, deciding on the color of the walls, the style of the furniture, the placement of art, and the flow between rooms. This designer brings life, functionality, and a personal touch to the previously bare structure.

In the world of UNIX-like operating systems, the X server acts as that foundational structure — the skeleton, as previously mentioned. It ensures the graphical user interface (GUI) stands and functions. Yet, it’s the Window Manager that breathes life into this interface, much like our interior designer.

1. **Crafting Aesthetics (Color & Style)**:
— The Window Manager is responsible for the ‘look’ of the interface. Think of it as choosing the wall color, the curtain style, or the kind of light fixtures in a room. In digital terms, it decides the appearance of the title bars, the shape and style of window borders, the animation when you minimize or maximize a window, and even the drop shadows.

2. **Determining Behavior (Functionality & Flow)**:
— Beyond just looks, the interior designer also considers how spaces are used — where should the sofa be, so it’s both cozy and practical, or how should the kitchen be arranged for optimal workflow. Similarly, the Window Manager determines how windows behave — how they can be resized, what happens when they’re dragged to the screen’s edge, or how they’re tiled together.

3. **Providing Choices (Versatility)**:
— Just as different people have varied interior design tastes, from modern minimalistic to classic Victorian, the Window Manager offers a range of ‘styles’. Some, like Enlightenment and KWin, offer a richer, more detailed environment, with glossy effects and animations. Others, like fvwm, opt for simplicity, focusing on speed and function over flash.

TL;DR: while the X server ensures your digital ‘building’ stands firm, the Window Manager ensures it’s a place you’d want to live in. It tailors the GUI to cater to individual preferences, balancing aesthetics with usability. Whether you prefer the luxury of a penthouse or the minimalism of a loft, there’s a Window Manager out there that’s the perfect interior designer for your digital space.

Desktop Environment: The utility belt.

  • Offers tools and utilities to make the GUI useful.
  • Ties all GUI components into a cohesive environment.
  • Popular choices include KDE and Gnome.

…feels unclear…let’s reelaborate with a real life parallel!

Mental Hook: Let’s change the builder and designer perspective..let’s talk music now!

Imagine attending a grand orchestra. Each musician with their unique instrument contributes to the rich tapestry of sound, but it’s the conductor who directs their rhythm, harmony, and tempo to weave together individual notes into a cohesive symphony. This harmony is the ultimate goal of the desktop environment in the digital realm.

A desktop environment plays a similar role to the conductor. In the digital world, each application, tool, or utility is like an instrument. On their own, they’re capable and functional, but without direction, they might not work harmoniously together. Here’s where the desktop environment steps in.

1. **Offering Tools and Utilities (Instruments)**:
Just as a violin or a trumpet has a unique sound and role in an orchestra, tools and utilities in a desktop environment serve specific functions. This could be as simple as a calendar app, a file explorer, or more intricate tools for system monitoring.

2. **Unifying GUI Components (Orchestration)**:
— In an orchestra, the conductor ensures the flute doesn’t overpower the cello, or the drums don’t drown out the clarinet. Similarly, the desktop environment ensures that every tool and utility fits seamlessly into a unified look and feel, providing consistency in aesthetics and functionality. This ensures that when you open a new app or tool, it doesn’t feel jarringly out of place but instead blends smoothly into the digital symphony you’re accustomed to.

3. **Holistic System (Symphony)**:
— Just as a conductor guides the orchestra to crescendos and pauses, the desktop environment streamlines tasks, guiding users through workflows with ease. Whether it’s the strategic placement of shortcuts, the consistency in icon design, or the intuitive arrangement of menu options, all these details culminate in a holistic user experience.

TL;DR: a desktop environment doesn’t just slap together a bunch of tools and apps. It curates them, fine-tunes their interactions, and presents them in a manner that feels intuitive and fluid to the user. Like a conductor ensuring each note is hit at the right moment, the desktop environment ensures every digital interaction feels just right.

In Conclusion

Flexibility is the defining hallmark of the UNIX-like GUI and users can shuffle between different X servers, window managers, and desktop environments, tailoring the system to personal preferences.

Understanding the GUI on UNIX-like systems requires a dive into its layered architecture. From the foundational X Server to the stylistic window manager and the utilitarian desktop environment, users are offered a buffet of choices to tailor their experience. As technology continues to evolve, so will the tapestry of the graphical user interface, further bridging the gap between command-line wizards and the everyday user.

Watch this beautiful playlist to know more!

Keyboard Encoding: KeyCodes vs KeySyms in Xlib

In the realm of Xlib, a library aiding the creation of GUIs for UNIX-like systems, keyboard inputs play a crucial role. Let’s explore the intricate dance between KeyCodes and KeySyms, two concepts that give meaning to our key presses.

KeyCodes: The Physical Realm

  • A KeyCode symbolizes a physical or logical key.
  • It’s limited to the range [8,255], offering ample space for various keys.
  • Think of KeyCode as a messenger; it carries no inherent message but could contain encoded information depending on the server implementation.
  • An important limitation: You can’t alter the relationship between keys and KeyCodes.

KeySyms: Symbols and Actions

  • KeySym is the actual symbol on your key, be it a letter, number, or function.
  • Their official list resides in X11/keysymdef.h. Developers typically include X11/keysym.h in applications to get symbols from Latin 1-4, Greek, and other general sets.
  • Each KeyCode is associated with a list of KeySyms, which conveys potential symbols for a particular key.

Portability Implications: KeyCodes vs. KeySyms

One might wonder why differentiating between KeyCodes and KeySyms is essential. The answer lies in portability:

  • KeyCodes are server-dependent, potentially changing between different devices or environments.
  • KeySyms offer a consistent representation of a key’s action or symbol across platforms.

By focusing on KeySyms, developers ensure consistent behavior regardless of the underlying hardware or X server configuration.

Wrapping Up

Behind every key press, there’s an elaborate system ensuring our computers understand our intentions. The division between KeyCodes and KeySyms might seem like a minor detail, but it’s these intricacies that allow us to seamlessly interact with our digital realms. As you program, remember the symbiotic relationship between hardware and software, and the intricate bridges, like Xlib, that connect them.

Exponential Zooming: A Deeper Dive

The mechanism of zooming… Two ideas came to my mind:

  • multiplicative (or exponential) zooming
  • additive (or linear) zooming.

Let’s explore the intricacies of these mechanisms and understand why exponential won my doubts.

The Magic of Exponential Zooming

At its core, exponential zooming involves changing the zoom level by multiplying the current zoom value by a consistent factor. Imagine a scenario where every scroll on a mouse or gesture on a touchscreen increases or decreases the zoom level by a consistent percentage, say 5%. For instance, if you start with a zoom level of 1, an initial scroll could reduce this to 0.95. The next scroll doesn’t simply shave off another 0.05 but takes 5% of the 0.95, giving 0.9025, and so on. This method offers a fluid and predictable zooming experience, mimicking the natural way humans focus on details either close up or from afar.

However, there’s a subtle distinction to note. When you zoom in, reducing the view to 95% might seem direct. But when you zoom out, increasing it by 5% doesn’t simply reverse the process. The result isn’t a mirror image, as increasing a zoomed-in view by 5% makes the process seem faster. This might sound counterintuitive, but when dissected, it’s evident that zooming out from a high magnification has a more pronounced effect than zooming in from a baseline.

TL;DR-> Tailor the dezoom accordingly. I didn’t spend, i just used 1.05 for the dezoom to “keep the same approach” for the 0.95 zoom.

Linear Zooming: A Steady Hand

On the other hand, additive zooming takes a more simplistic approach. It changes the zoom level by a fixed amount, regardless of the current level. Consider adjusting the zoom level of an image by a set value of 0.05, no matter its current magnification. This approach has its pitfalls. For instance, a change of 0.05 on a zoom level of 100 is barely discernible, but the same change on a level of 0.1 is transformative. Additionally, if not capped, this method can lead to negative zoom values, causing visual disruptions, i.e. you will see at a certain point the set filps.

A Relatable Analogy

To visualize these mechanisms, think of binoculars. The seamless zooming in and out feels so intuitive, allowing you to focus effortlessly. If binoculars operated on a linear zooming mechanism, adjusting the focus at different distances would be jarring.

Moreover, for a financial analogy, exponential zooming operates similarly to compound interest. Just as the principal sum in a bank grows faster due to accumulating interest on both the initial amount and the accumulated interest, the zoom level using exponential zooming also becomes more dramatic over time.

TL;DR

Exponential zooming, with its fluidity and adaptability, finds favor, especially in applications where detailed exploration is paramount, such as fractals. It ensures a consistent experience across all zoom levels, maintaining positivity in values and avoiding visual glitches. On the other hand, while linear zooming offers simplicity, it may lack the finesse required for a seamless user experience in specific applications. The choice between them, thus, hinges on the user’s needs and the application’s demands.

Scaling Down Numbers: Making Sense of Rescaling Values in the map function

Rescaling is a fundamental mathematical operation used in numerous real-world applications, from graphics rendering to data normalization in machine learning. Understanding its core concept and knowing how to apply it efficiently can be invaluable.

The Fundamental Question:

Imagine having a range of numbers, for example, between 0 and 10. But for a specific application, you want to fit this range between 0 and 30. How do you effectively achieve this without distorting the relationships between individual numbers?

#include <stdio.h>

double map(double value, double old_value_min, double old_value_max, double new_value_min, double new_value_max)
{
return ((value - old_value_min) * (new_value_max - new_value_min) / (old_value_max - old_value_min)) + new_value_min;
}
int main()
{
double value = 9; // example value
// university vs shool in italy grades
printf("Rescaled Value: %.0lf\\n", rescale(value, 0, 10, 0, 30));
return 0;
}

Running this C code will provide a rescaled value for 9, within the new range, namely 27, which is actually the “old school 9” for universities in Italy.

Why is Rescaling Important?:

  • Graphics: In computer graphics, shapes might need to be resized according to screen dimensions or design requirements.
  • Data Processing: In machine learning, often, data must be normalized to ensure efficient and accurate processing. This normalization is achieved using rescaling.

In essence, rescaling is all about adjusting values to fit perfectly within a desired range. Whether for graphics, data analysis, or other applications, mastering this concept can pave the way for more advanced mathematical and computational tasks.

--

--