2,648 karma · joined September 22, 2013
andrew at $DOMAIN
pal.r = pal.g = pal.b = (77 * pal.r + 150 * pal.g + 29 * pal.b) >> 8;
Hardware floating point was rare before the 486 DX and Pentiums. Not to mention that Integer<->FP conversion was slow. And division of any kind has always been slow. So you'd see a lot of fixed-point math approximations with power-of-two divisors so that you can shift-right.(FWIW, I'm dealing with this sort of thing at work right now - working on a complex branch, rewriting history to keep it as a sequence of clean testable and reviewable commits, with a plan to split them out to individual PRs when I finish.)
(Note that I'm using it in more of a hands-on pair-programming mode, and not in a fully-automated vibecoding mode.)
I'm a trig-avoider too, but see it more as about not wiggling back and forth. You don't want to be computing angle -> linear algebra -> angle -> linear algebra... (I.e., once you've computed derived values from angles, you can usually stay in the derived values realm.)
Pro-tip I once learned from Eric Haines (https://erich.realtimerendering.com/) at a conference: angles should be represented in degrees until you have to convert them to radians to do the trig. That way, user-friendly angles like 90, 45, 30, 60, 180 are all exact and you can add and subtract and multiply them without floating-point drift. I.e., 90.0f is exactly representable in FP32, pi/2 is not. 1000 full revolutions of 360.0f degrees is exact, 1000 full revolutions of float(2*pi) is not.
But compiling to WASM and running side-by-side on that page is definitely something that I've thought about to make the comparison easier. (For now, I just have my test suite write out PNGs and compare them in an image viewer split-screen with the browser.)
(I did post a Show HN at the time of the original release, https://news.ycombinator.com/item?id=33148540, but it never gained traction.)
Just to answer some comments that I see:
1. This was absolutely not vibecoded!
I'd originally started with a different version control system and was still getting used to Git and GitHub at the time that I'd released this. (I was a latecomer to Git just because I hated the CLI so much.) It was easiest for me just to drop the whole thing as a snapshot in a single commit.
But my private repo for it actually started in May 2017, and it had 320 commits leading up to its release, all human-written.
For the v2.0 that I have in mind, I'm thinking of force-pushing to migrate the full development history to the public repo.
And finally I'll add that I'm a graphics engineer by education and career. Where would the fun be in vibe-coding this? :-) Oh, and this compiles down to just ~36KiB of object code on x86-64 last I checked. Good luck vibe-coding that constraint.
2. Why a single header with `#define CANVAS_ITY_IMPLEMENTATION`?
I was inspired by the STB header ibraries (https://github.com/nothings/stb) and by libraries inspired by those, all of which I've found very convenient. In particular, I like their convenience for small utilities written in a single .cpp file where I can just `g++ -O3 -o prog prog.cpp` or such to compile without even bothering with a makefile or CMake.
Since the implementation here is all within a single #ifdef block, I had figured that anyone who truly preferred separate .cpp and .h files could easily split it themselves in just a few minutes.
But anyway, I thought this would be a fun way of "giving back" to the STB header ecosystem and filling what looked to me like an obvious gap among the available header libraries. It started as something that I'd wished I'd had before, for doing some lightweight drawing on top of images, and it just kind of grew from there. (Yes, there was Skia and Cairo, but both seemed way heavier weight than they ought to be, and even just building Skia was an annoying chore.)
----
Since I mentioned a v2.0, I do have a roadmap in mind with a few things for it: beside the small upgrades mentioned in the GitHub issues to support parts of newer <canvas> API specs (alternate fill rules, conic gradients, elliptical arcs, round rectangles) and text kerning, I'm thinking about porting it to a newer C++ standard such as C++20 (I intentionally limited v1.0 to C++03 so that it could be used in as many places as possible), possibly including a small optional library on top of it to parse and rasterize a subset of SVG, and an optional Python binding.
It's the people who aggressively slide right over just a few feet in front of me (cutting off nearly all of my safety buffer) without so much as a signal that really drive me nuts.
We tried to follow the VFX reference platform once that became a thing back in 2014.
https://huggingface.co/models?other=base_model:quantized:zai...
Probably as:
I'd also argue that it's much like movie editing: you tend to only notice it when it's crappy.
> I was on the RenderMan team at the time and remember thinking it really neat that our system could stand up to that.
> I remember finding it mind blowing when I learned that in Brave, the artists weren't just using a texture/displacement mapped surface for the clothing and armor. They were using tori primitives for the chain mail, and curve primitives for the clothing. (I.e., the clothing was actually woven out of curve primitives for the threads.)
I remember find it mind blowing when I learned that in Brave, the artists weren't just using a texture/displacement mapped surface for the clothing and armor. They were using tori primitives for the chain mail, and curve primitives for the clothing. (I.e., the clothing actually woven out of curve primitives for the threads.)
[1] https://steamcdn-a.akamaihd.net/apps/valve/2007/NPAR07_Illus...
(Disclosure: I contributed a chapter.)
layout = neato;
in a dot file, call dot, and it will use the neato layout engine.(See https://graphviz.org/docs/attrs/layout/)
And if I look in my /usr/bin, I see that neato is just symlinked to dot. It's pulling the usual trick of one executable that behaves slightly differently depending on the invocation name.
And the old About noted what you just said about there being nothing preventing someone from using AI:
> If you want to use AI to help you solve puzzles, I can't really stop you, but I feel like it's harder to get better at programming if you ask an AI to do the programming for you.
It occurred to me that it would be more useful to me in Emacs, and that might make a fun little exercise.
And that's how I discovered `M-x nato-region` was already a thing.
"Practical Linear Algebra: A Geometry Toolbox" by Farin and Hansford.
- data layout (row-major vs. column major)
- pre-multiplication vs. post-multiplication of matrices
Switching either of these conventions results in a transposition of the data in memory, but knowing which one you're switching is important to getting the implementation of the math right.
And then on top of that for even more fun you have:
- Left- vs. right-handed coordinate systems
- Y-up or Z-up
- Winding order for inside/outside
There are lots of small parity things like this where you can get it right by accident but then have weird issues down the line if you aren't scrupulous. (I once had a very tedious time tracking down some inconsistencies in inside/outside determination in a production rendering system.)
It's one thing to try to steer basic, non-technical users toward an MS account by default. Fine; I may not like that, but I get it. But at this point, anyone left who's still using these methods to create a local account is likely to be a technical user who's deliberately and intentionally wanting a stay on local account for whatever their own reasons.
I suspect that's a rather small group, which leaves me puzzled: (1) is the juice really worth the squeeze, and (2) is it really worth being so hostile to your power users?