arsd.pixmappresenter PR merged, stars and snow demo like the olden days, and update on me and D

Posted 2023-12-25

Core D Development Statistics

In the community

Community announcements

See more at the announce forum.

new arsd module - reminder of my beginnings

Elias Batek (0xEAB) submitted a new module to the arsd collection last week: arsd.pixmappresenter, which gives you a simple array of pixels to play with that are reflected on screen.

Of course, you could do something similar to this with simpledisplay's Image class, but it is a bit of a pain to use and doesn't perform especially well (it trades convenience for performance but achieves neither), while still lacking in some nice features, like being unable to scale up on linux. So while you *could* do it, it wasn't exactly something you'd *want* to do.

The pixmappresenter makes it both easy and fast, while adding scaling to window size (while maintaining the pixels themselves and the aspect ratio, etc.) that Just Works.

It reminds me of the old days programming for DOS, how I really got started as a coder. Then I went looking for an old file to recreate some of the old DOS demos and found it online:

http://textfiles.com/programming/astrainer.txt

That isn't the first programming tutorial I read, but it is among the early ones, and this is is memorable not for stumping me like some of the early ones (Seriously, I still remember the sentence "registers are not variables" and how it took me a long time to get past that. I know what it means now, but I still question if that's a good way to explain it. Oh well), but rather this one brought some joy.

I find graphics programming to be kinda dull nowadays, but I still much prefer demos that you can see and play with than just yet another exceedingly boring console hello world. Yeah, yeah, I get the point of hello world, it is a good enough step one, but if step two is like scanf, whoa, don't do that! First, scanf just plain sucks, I wish it would die, if you want a string from the user, there's better ways to get it... but second, and more importantly: booorrrrriinnnnnggggg. Make step two something cool like a little graphics demo. I'm a sucker for "flying through space", like the Windows 95 screensaver. Then step three adds some interactivity by a key press or mouse click doing something on the screen. You can switch from space to snow with just minor modifications - let the student experiment and see the result. That's what captivated me about the DOS VGA thing, and that tutorial previously linked is all about that.

DOS programming on the VGA though isn't just a fun result. It is also easy to do: you write some values to a particular array and, boom, it appears on screen. There's *some* setup and teardown, but it is just an easy function call you can copy/paste now and understand later, then you get right into the simplest action of just simply setting values and seeing results. It is easy to do.

That's my big problem with so much of the modern graphics programming stuff: there's just so much.... work. This is why I started writing simpledisplay.d back in the day - to make displaying things simple again. And it partially succeeded... but of course, if it fully succeeded, I wouldn't be writing this here today. Instead, the focus shifted a bit from recreating the old days to being a solid foundation to build so many other varied things on top of. It is still a reasonably simple to use display library, but its drawing primitives are lines, rectangles, arcs, images, and even shaders... not pixels.

So when Elias described his project, I was pretty enthusiastic. This was something I used to enjoy playing with and have missed for a while. So, of course, I was eager to get it merged!

I've adopted a great many modules to the arsd umbrella before - about 1/6th of the repo is user contributions! If you have something you'd like to add, let's talk about it!

My main requirement is that there be a complete usage example attached to each module, unless it is just a fairly obvious utility library, so people can quickly get the jist of getting started with just copy/paste. I also generally avoid dependencies, but there can be exceptions made on that.

I don't care if you use spaces or tabs, or put {s on their own line, or whatever other annoying things you might expect. If I do want anything changed (and good chance it won't be much), I'll work right alongside you to reach a result that we both like.

arsd.pixmappresenter will be officially released as an experimental module, subject to change without notice, in the 11.4 dub tag, and its API will be declared stable at some point in the future once it is clear everything works well from more actual usage experience. You can already play with it on git master if you like.

Speaking of which, enough talk! Time to play. Let's try the new module in making and old-school basic demo!

1 // I started with the example in the docs and tweaked a lil
2 import arsd.pixmappresenter;
3 import arsd.simpledisplay;
4 
5 int main() {
6 	const resolution = Size(320, 200);
7 
8 	auto cfg = PresenterConfig();
9 	cfg.window.title = "Snow / Stars Demo";
10 	cfg.window.size = Size(960, 480);
11 	cfg.renderer.resolution = resolution;
12 	cfg.renderer.scaling = Scaling.integer;
13 
14 	// use Pixel.black for stars
15 	cfg.renderer.background = ColorF(0.2, 0.2, 0.25);
16 
17 	auto presenter = new PixmapPresenter(cfg);
18 
19 	Color emptyColor = /* for stars */ /* Color.black */ /* for snow */ Color(150, 150, 165);
20 
21 	// set a background for snow
22 	presenter.framebuffer.data[] = emptyColor;
23 
24 	static struct Star {
25 		float x;
26 		float y;
27 		float z;
28 
29 		void reset() {
30 			import std.random;
31 			this.x = uniform(-1.0, 1.0);
32 			this.y = uniform(-1.0, 1.0);
33 			this.z = uniform(0.1, 1.0);
34 
35 			if(this.x == 0)
36 				this.x = 0.1;
37 			if(this.y == 0)
38 				this.y = 0.1;
39 		}
40 	}
41 
42 	Star[] stars;
43 	stars.length = 164; // pick a number!
44 	foreach(ref star; stars) {
45 		star.reset();
46 	}
47 
48 	enum max_distance = 0.5; // anything > 0 and <= 1.0
49 
50 	return presenter.eventLoop(delegate() {
51 		foreach(ref star; stars) {
52 			auto drawX = cast(int) ((star.x * (1.0 - star.z * max_distance) + 1.0) / 2 * resolution.width);
53 			auto drawY = cast(int) ((star.y * (1.0 - star.z * max_distance) + 1.0) / 2 * resolution.height);
54 
55 			// erase the old position... unless you're accumulating snow
56 
57 			bool accumulate;
58 			if(drawY >= resolution.height - 8) {
59 				// at the bottom of the screen = always accumulate
60 				accumulate = true;
61 			} else {
62 				// there's already snow under us right down to the bottom of the screen, we pile on top
63 				foreach(dy; drawY .. resolution.height) {
64 					accumulate = presenter.framebuffer.data[dy * resolution.width + drawX] == rgb(0xFF, 0xFF, 0xFF);
65 					if(!accumulate)
66 						break;
67 				}
68 			}
69 
70 			if(!accumulate)
71 				presenter.framebuffer.data[drawY * resolution.width + drawX] = emptyColor;
72 
73 			/+
74 			// for flying through space, adjust z
75 			star.z -= 0.005;
76 			if(star.z <= 0)
77 				star.reset();
78 			+/
79 
80 			// for snow, adjust y
81 			star.x += 0.001;
82 			star.y += 0.005; // and maybe x to show a slight wind
83 
84 			if(star.y >= 1 || star.z < 0)
85 				star.reset();
86 
87 			drawX = cast(int) ((star.x * (1.0 - star.z * max_distance) + 1.0) / 2 * resolution.width);
88 			drawY = cast(int) ((star.y * (1.0 - star.z * max_distance) + 1.0) / 2 * resolution.height);
89 
90 			// draw the new position
91 			presenter.framebuffer.data[drawY * resolution.width + drawX] = rgb(0xFF, 0xFF, 0xFF);
92 		}
93 
94 		return LoopCtrl.redrawIn(17 /* msecs */);
95 	}, delegate(MouseEvent ev) {
96 		// toggle fullscreen mode on double-click
97 		if (ev.doubleClick) {
98 			presenter.isFullscreen = !presenter.isFullscreen;
99 		}
100 	});
101 }

little pixelated snow accumulating out the computer window

Yes, the graphics are nothing special, but... fun little thing. Using the framebuffer as an array means we can read from it as well as writing easily enough - here, to see if the snow accumulates.

You can play with different numbers there to make different effects. Easy to change it to a flying-through-space style thing, but since it is Christmas, I went with some snow.

Changing the colors, or the max distance, or the number of "stars", or the redrawIn speed, or the x/y/x adjustments... all these can change things ever so slightly.

Just a little bit of fun from a simpler age, revived again with the arsd.pixmappresenter module. (It is also useful for other things, one of its design goals was to be a presenter for an emulator screen. But I couldn't resist reliving the old days myself.)

Huge thanks to Elias Batek for his contribution!

About the arsd library collection

It was started in 2007 and has been continually maintained since then, with a goal of letting the user pick their compiler, not me, meaning I maintain compatibility with a wide range of dmd releases. The general rule is I test with the latest, something from about three years ago, and something in the middle, but some of the code works with much older compilers too. I also try to limit breaking changes in scope and frequency, with usually just a handful of small adjustments made just once a year, for the users' convenience and productivity.

The collection as a whole is listed with over 326,000 downloads on dub and has been used by varied projects not included in that count, including one popular project with over 2.3 million downloads and contributions from over 800 developers, as well as almost all of D's commercial users to varying extents.

The arsd collection is also the key driver of this blog, which now has over 400 articles spanning 8 years of writing, and is instrumental in my maintaining the main dpldocs.info site, which provides online turnkey documentation hosting for over 1,800 dub packages. Additionally, the vast breadth and depth of experience in multiple domains these libraries have offered me also makes me capable of personally supporting many new D users; I've historically answered about 1/6 of all new users over the last 16 years, and am also frequently called on as an escalated support provider for people with advanced problems.

While I have written much of the D code in the collection myself, it has had over 50 other code contributors, as well as numerous people who have helped test and contributed use cases and ideas. I estimate about 1/6 of the code is direct contribution from others.

Is this the end for me?

As many of you know, I'm quite annoyed with the direction and methodology of D development and have been for a long time. Everything just takes forever, and the realized benefit is rarely proportional to the amount of time you have to put in, and this is the way it has been for a long time.

I've raised two specific issues on their monthly calls that I've been joining since July that would alleviate long-standing things that I've wanted: a way to break module constructor cycles from template mixins, and, of course, the interpolation feature.

The first one was written by Dennis Korpel in August https://github.com/dlang/dmd/pull/15537 ... and only merged last week. But at was finally merged, so this can be checked off going forward. Yay!

The second one I implemented myself https://github.com/dlang/dmd/pull/15715 and is still stalled, pending their process delays. I've made it clear to D leadership that I've set a deadline that this needs to happen by the end of the year if they want our relationship to continue. On the November call, Walter demanded a spec, in addition to the implementation, documentation, compiler tests, user examples, demonstrations, and differential explanation from other formal DIPs I've already provided (and several other people had no trouble understanding). I was not happy with this demand for more when he hasn't even looked at what was already done!

Atila offered to write the spec to appease Walter in early November, but did not start it until after another 5 week delay, and is doing it in the format of a DIP instead of as a corresponding spec PR, so you know after it is written, the next thing they'll want is that spec PR, converting the markdown over to ddoc and injecting it in the existing grammar pages and whatnot. At least that should be reasonably straightforward.

Meanwhile, the actual implementation continues to require ongoing labor to resolve merge conflicts (so draining), but otherwise is ready to go. There's no design flaws identified, no known bugs in the implementation, no fundamental opposition to the general idea. (I actually just realized myself that I forgot to put in changelog entry in the PR. Maybe I'll add that myself on the next rebase tomorrow, but that hasn't been the official blocker lol.) All they have to do is lift their hold on the feature - they don't even need to actually click the button, just lift the hold and allow someone else to click the merge button!

And yet we're still waiting. It is simply driving me nuts. I hoped there'd be major change this year... but now I'm told they're still working on a plan to change and maybe next year they'll have some ideas. Everything just takes forever and I'm out of patience.

Maybe see you next year.