imgui
imgui copied to clipboard
ImDrawList coding party - deadline Nov 30, 2020!
ImDrawList coding party!
- create small visual effects
- 1024 bytes of source code
- 320x180 output
- only use dear imgui's crappy low-level shape drawing api
Two days ago I thought: "It'd be fun to organize a contest for writing special effects using the ImDrawList api, with a constraint on source code size. May be fun to see what people can come up with using shape/vector-based api (quite different from a pixel-shading function). Would you participate?"
It's by no mean a great api but it has served many well, to draw custom widgets etc, let's get it some extra love..
Here's a testbed imdrawlist_party.cpp
which you can paste in your application or any dear imgui example:
https://gist.github.com/ocornut/51367cc7dfd2c41d607bb0acfa6caf66
Rules:
- Your source file
fx.inl
must be <= 1024 bytes. - Effect should be reasonably portable (not relying on specific render backend callback)
- OK to use preprocessor define or other tricks as long as they are reasonably portable.
- Included are: math.h, imgui.h, imgui_internal.h with ImVec2 maths operators
- The effect should not use ImGui:: functions, only use ImDrawList facilities.
- Canvas ratio is expected to be 16/9, canvas size expected to be 320 by 180.
- For simplicity we assume you can encode a color as 0xAAGGBBRR instead of using the IM_COL32() macro, therefore IMGUI_USE_BGRA_PACKED_COLOR config option is not supported!
Prizes:
- This is mostly for fun so please feel free to make multiple submissions or tweak them as you feel. No pressure. Mostly, I'm supposed to work and ship tables this or next week so hey why I am doing this?!
- We could possibly include some of them in imgui_demo if they strike a good balance of cool and educational. If we end up doing that we'll rework code to be neat and proper for a demo (aka not densely packed into 1024 characters).
Function signature:
void FX(ImDrawList* d, ImVec2 a, ImVec2 b, ImVec2 sz, ImVec4 mouse, float t);
d : draw list
a : upper-left corner
b : lower-right corner
sz : size (== b - a)
mouse : x,y = mouse position (normalized so 0,0 over 'a'; 1,1 is over 'b', not clamped)
z,w = left/right button held. <-1.0f not pressed, 0.0f just pressed, >0.0f time held.
t : time
If not using a given parameter, you can omit its name in your function to save a few characters.
To record gif files you can use ShareX https://getsharex.com or ScreenToGif https://www.screentogif.com/
Let's get started! You can post on this thread, on discord or on twitter!
Known issues
- ImDrawList polygon stroking with thick lines looks broken on acute angle, good luck!!
Here are some examples I made to get you started:
Circles
#define V2 ImVec2
void FX(ImDrawList* d, V2 a, V2 b, V2 sz, ImVec4 mouse, float t)
{
for (int n = 0; n < (1.0f + sinf(t * 5.7f)) * 40.0f; n++)
d->AddCircle(V2(a.x + sz.x * 0.5f, a.y + sz.y * 0.5f), sz.y * (0.01f + n * 0.03f),
IM_COL32(255, 140 - n * 4, n * 3, 255));
}
Squares
#define V2 ImVec2
void FX(ImDrawList* d, V2 a, V2 b, V2 sz, ImVec4, float t)
{
float sx = 1.f / 16.f;
float sy = 1.f / 9.f;
for (float ty = 0.0f; ty < 1.0f; ty += sy)
for (float tx = 0.0f; tx < 1.0f; tx += sx)
{
V2 c((tx + 0.5f * sx), (ty + 0.5f * sy));
float k = 0.45f;
d->AddRectFilled(
V2(a.x + (c.x - k * sx) * sz.x, a.y + (c.y - k * sy) * sz.y),
V2(a.x + (c.x + k * sx) * sz.x, a.y + (c.y + k * sy) * sz.y),
IM_COL32(ty * 200, tx * 255, 100, 255));
}
}
Curves
#define V2 ImVec2
void FX(ImDrawList* d, V2 a, V2 b, V2 sz, ImVec4, float t0)
{
for (float t = t0; t < t0 + 1.0f; t += 1.0f / 100.0f)
{
V2 cp0(a.x, b.y);
V2 cp1(b);
float ts = t - t0;
cp0.x += (0.4f + sin(t) * 0.3f) * sz.x;
cp0.y -= (0.5f + cos(ts * ts) * 0.4f) * sz.y;
cp1.x -= (0.4f + cos(t) * 0.4f) * sz.x;
cp1.y -= (0.5f + sin(ts * t) * 0.3f) * sz.y;
d->AddBezierCurve(V2(a.x, b.y), cp0, cp1, b, IM_COL32(100 + ts*150, 255 - ts*150, 60, ts * 200), 5.0f);
}
}
Waves
#define V2 ImVec2
#define ARFM d->AddRectFilledMultiColor
#define ACF d->AddCircleFilled
#define CH s.SetCurrentChannel
void FX(ImDrawList* d, V2 a, V2 b, V2 sz, ImVec4, float t)
{
ARFM(a, b, 0xFF1E1E1E, 0xFF1E281E, 0xFF1E1E5A, 0xFF321E78);
ARFM(a, V2(b.x, a.y + sz.y*0.4f), 0x1EFFDCFF, 0x14FFFFDC, 0x001E1E5A, 0x00321E78);
auto s = d->_Splitter;
s.Split(d, 2);
for (int n = 0; n < 256; n++)
{
V2 c(a.x + n / 256.f * sz.x, b.y + 20 - cos(t - 0.1f * n / 2) * 10 + cos(t) * 5);
float r(40 + sin(t + n / 2) * 40);
CH(d, 0);
ACF(c, r + 1, 0x80FFFFFF);
CH(d, 1);
ACF(c, r, IM_COL32(255, n / 2, n / 8, 255));
}
s.Merge(d);
d->AddRect(a, b, IM_COL32(128, 128, 128, 100));
}
Thunder Storm
#define min(x,y) ((x)<(y)?x:y)
#define wh(a) ImColor(1.f,1.f,1.f,a)
void FX(ImDrawList* d, ImVec2 a, ImVec2 b, ImVec2 sz, ImVec2, float t)
{
static float fl;
if ((rand() % 500) == 0) fl = t;
if ((t-fl) > 0)
{
auto ft = 0.25f;
d->AddRectFilled(a, b, wh((ft - (t - fl)) / ft));
}
for (int i = 0; i < 2000; ++i) {
unsigned h = ImGui::GetID(d+i + int(t/4));
auto f = fmodf(t + fmodf(h / 777.f, 99), 99);
auto tx = h % (int)sz.x;
auto ty = h % (int)sz.y;
if (f < 1) {
auto py = ty - 1000 * (1 - f);
d->AddLine({ a.x + tx, a.y + py }, { a.x + tx, a.y + min(py + 10,ty) }, (ImU32)-1);
}
else if (f < 1.2f)
d->AddCircle({ a.x + tx, a.y + ty }, (f - 1) * 10 + h % 5, wh(1-(f-1)*5.f));
}
}
The Matrix effect, slowly been shaving off spaces and making the code uglier and uglier
//v1.1
#define V2 ImVec2
void FX(ImDrawList*d,V2 a,V2,V2 sz,ImVec4,float t)
{
static struct{int y,h,c; float t,s;} m[40]={0};
static int S=0x1234;
static float t0=t;
float ZI=t*.07f,Z=ZI+1.f;
for(int x=0;x<40;x++)
{
auto& M=m[x];
int i=x>=15&&x<25;
if(M.s==0.f||M.y>16)
{
M.h = (t<7.f||i)*((int)(2+t*.5f) + S%(int)(6+(t*0.3f)));
M.y = (M.s==0.f)*-(S%15)-M.h;
M.c += S;
M.s = (5+(S%14))*(.01f-t*.001f);
if(t>5.f&&i)
{
M.c = (340003872375972UL>>(x*5-75))&31;
M.h = i?(x!=19):0;
}
}
if((M.t-=t-t0)<0.f)
{
if(t<6.f||!i||M.y!=6)
M.y++;
M.t += M.s;
}
char c=64|M.c%42;
for(int j=0; j<M.h; j++,c=64|(c^M.c+M.h^j)%42)
for(int f=(j+1==M.h)?13:4+(M.c&1);f--;)
d->AddText(0, 13*(i?Z:-Z), V2(a.x-(sz.x*.5f*ZI)+x*8*Z+sin(j+t*f), a.y-(sz.y*.5f*ZI)+(M.y+j)*13*Z+cos(x*f-t)), 0x3c68bb5b, &c, &c+1);
S|=((S&1)^((S&8)>>2))<<16;
S>>=1;
}
t0 = t;
}
Tiny Loading Screen!
Had a bunch of fun trying to get this as small I could, managed to get it just under 600 bytes for now.
#define V ImVec2
#define R d->AddRectFilled
#define C(x) IM_COL32((sin(x) + 1) * 255 / 2, (cos(x) + 1) * 255 / 2, 99, 255)
#include <set>
void FX(ImDrawList* d, V a, V b, V s, ImVec4, float t) {
static auto z = 0.f;
if (t > z + 2) z += 2;
auto c = C(z + 2);
R(a, b, C(z));
for (auto B = b.x, i = 0.f, o = s.y / 8; i < 8; ++i, B = b.x) {
if (auto w = (i / 7) * .2f, x = std::max(t - z - w, 0.f); t - z < w + 1)
B = a.x + (x < .5 ? 8 * x * x * x * x : std::min(1 - pow(-2 * x + 2, 4.f) / 2, 1.f)) * s.x;
R(V(a.x, a.y + o * i), V(B, a.y + o * i + o), c);
}
}
Quicksort visualization.
#define V2 ImVec2
#include <vector>
#include <array>
using namespace std;
int N = 64, S, J;
#define V vector<int>
V v = [] {
V r;
for (; J < N; J++) r.push_back(rand() % 180);
return r;
}();
vector<array<int,4>> st { { 0, N-1, 0, 0} };
#define A st.back()[0]
#define B st.back()[1]
#define I st.back()[2]
void FX(ImDrawList* d,V2 a,V2 b,V2 s,ImVec4,float t)
{
float bs = s.x / N, y, c;
for (int i = 0; i < N; i++) {
y=a.y+v[i];
c=70+v[i];
d->AddRectFilled(V2(a.x+bs*i,y),V2(a.x+bs*(i+1),b.y),IM_COL32(c,255-c,255,255));
}
d->AddText(a, -1u, "Quicksort");
if (st.empty()) return;
d->AddRect(V2(a.x+bs*A,a.y),V2(a.x+bs*(B+1),b.y),0xFF00FF00,8,ImDrawCornerFlags_Top,2);
switch (S) {
case 0:case 5: if(A>=B)st.pop_back(),S+=3;else I=J=A,S++;break;
case 1:case 6:
if(v[J]>v[B])swap(v[I],v[J]),I++;
if(++J>B)swap(v[I],v[B]),S++;
break;
case 2:case 7:st.push_back({A,I-1,A,3});S=0;break;
case 3:st.push_back({I+1,B,A,8});S=5;break;
case 8:S = st.back()[3]; st.pop_back();
}
}
Blobs.
#define V2 ImVec2
void FX (ImDrawList* d, V2 a, V2 b, V2 s, ImVec4 m, float t)
{
int N = 25;
float sp = s.y / N, y, st = sin(t) * 0.5 + 0.5,
r[3] = { 1500, 1087 + 200 * st, 1650 },
ctr[3][2] = { { 150, 140 }, { s.x * m.x, s.y * m.y },
{ 40 + 200 * st, 73 + 20*sin(st * 5) } };
for (int i = 0; i < N; i++) {
y = a.y + sp*(i+.5);
for (int x = a.x; x <= b.x; x += 2) {
float D = 0, o = 255;
for (int k = 0; k < 3; k++)
D += r[k]/(pow(x-a.x-ctr[k][0], 2) + pow(y-a.y-ctr[k][1], 2));
if (D < 1) continue;
if (D>2.5) D = 2.5;
if (D < 1.15) o /= 2;
d->AddLine(V2(x, y), V2(x+2, y), IM_COL32(239, 129, 19, o), D + sin(2.3 * t + 0.3 * i));
}
}
}
3D cube.
#define V2 ImVec2
void FX (ImDrawList* d, V2 a, V2 b, V2 s, ImVec4 m, float t)
{
a.x += s.x/2, a.y += s.y / 2;
float S = sin(m.x), C = cos(m.x), x = 50, y, z = (m.y * 2 - 1) * x;
float v[8][3] { { x, x, z+x }, { x, -x, z+x }, { -x, -x, z+x }, { -x, x, z+x },
{ x, x, z-x }, { x, -x, z-x }, { -x, -x, z-x }, { -x, x, z-x } };
for (int i = 0; i < 8; i++) {
x = v[i][0] * C - v[i][1] * S;
y = v[i][0] * S + v[i][1] * C + 120;
z = v[i][2];
v[i][0] = x / y * 50;
v[i][1] = z / y * 50;
v[i][2] = y;
}
#define L(A,B) z = 500 / (v[A][2] + v[B][2]); \
d->AddLine(V2(a.x+v[A][0],a.y+v[A][1]),V2(a.x+v[B][0],a.y+v[B][1]),-1u,z);
L(0, 1) L(1, 2) L(2, 3) L(0, 3)
L(4, 5) L(5, 6) L(6, 7) L(4, 7)
L(0, 4) L(1, 5) L(2, 6) L(3, 7)
}
Real-time visualization of the interweb blogosphere.
#define V2 ImVec2
#include <vector>
int N = 300;
auto v = [] {
std::vector<std::pair<V2,V2>>r(N);
for (auto&p:r)
p.second = p.first = V2(rand() % 320, rand() % 180);
return r;
}();
float l2 (V2 x) { return x.x*x.x + x.y*x.y; }
void FX(ImDrawList* d,V2 a,V2 b,V2 s,ImVec4,float t)
{
float D, T;
for (auto&p:v) {
D = sqrt(l2(p.first - p.second));
if (D > 0) p.first += (p.second - p.first) / D;
if (D < 4) p.second = V2(rand() % 320, rand() % 180);
}
for (int i = 0; i < N; i++) {
for (int j = i+1; j < N; j++) {
D = l2(v[i].first - v[j].first);
T = l2((v[i].first + v[j].first) - s) / 200;
if (T > 255) T = 255;
if (D < 400) d->AddLine(a+v[i].first, a+v[j].first, IM_COL32(T, 255-T, 255, 70), 2);
}
}
}
Here's my attempt at some abstract fireworks... with about four bytes to spare. The code has ended up pretty unreadable as a result, sadly.
#define V ImVec2
#define R rand()
#define I int
#define F float
#define S ((F)R/32767)
#define T(d,b,e)p[j].d=(b*(j&1^j>>1?1:-1))+(e*((j>1)?1:-1))+c.d+a.d;
#define X(a)I a=128+(R&127);
struct P {F x,y,vx,vy,a;I l,s,f,d;};P p[8192]={0};
void A(V o, I f){P t;t.x=o.x;t.y=o.y;t.vx=S*3-1.5;t.vy=S*2.5-(f?4:1);t.l=R%(f?50:99);t.s=R;t.f=f;for(I j=0;j<9;j++){t.d=j;t.a=1-(F)j/9;for(I i=0;i<8192;i++)if(!p[i].l){p[i]=t;break;}}}
void FX(ImDrawList*d,V a,V b,V sz,V mn,F t0){I g=R;for(I i=200;i;--i)d->AddLine(V(a.x,a.y+i),V(b.x,a.y+i),IM_COL32(0,0,i,255));I e=0;for(I i=0;i<8192;i++){P&c=p[i];if(c.l){if(c.d)c.d--;else{srand(c.s);c.x+=c.vx;c.y+=c.vy;c.vy+=.04;c.l-=(c.vy>0)?1:0;F s=(S*3+.1)*(c.f+1);F n=(t0*(c.l<0?0:1))+(i*S*2.5-1.5);F sa=sin(n)*s;F ca=cos(n)*s;V p[4];for(I j=0;j<4;j++){T(x,sa,ca);T(y,ca,-sa);}X(r)X(g)X(b)d->AddConvexPolyFilled(p,c.f?4:3,IM_COL32(r,g,b,(c.f?255:c.l)*c.a));if(!c.l&&c.f&&c.a==1){I l=R%40+15;for(I j=0;j<l;j++)A(V(c.x,c.y),0);}}if(c.f)e++;}}
srand(g);if(e<512)A(V(S*420-50,200),1);}
Old school plasma.
#define V2 ImVec2
#define S sinf
#define C cosf
#define I int
#define F float
#define CL(x,l,h) (((x)>(h))?(h):(((x)<(l))?(l):(x)))
#define PI 3.1415926535
#define CO(c,b) (int(c*255)<<b)
void FX(ImDrawList* d, V2 a, V2 b, V2 s, ImVec4 m, float t)
{
t*=3;
F ix=s.x/320;
F iy=s.y/180;
F sz=s.x / 15;
for (F x=a.x;x<b.x;x+=ix)
for (F y=a.y;y<b.y;y+=iy){
F v=0;
v+=S((x/sz+t));
v+=S((y/sz+t)/2.0f);
v+=S((x/sz+y/sz+t)/2.0f);
F cx=x/sz/10+0.3*S(t/3.0);
F cy=y/sz/10+0.3f*C(t/2.0);
v+=S(sqrt(100*(cx*cx+cy*cy+1))+t);
v=CL(v/2, 0, 1);
F r=S(v*PI)*.5f+.5f;
F g=S(v*PI+PI/3)*.5f+.5f;
F b=S(v*PI+PI)*.5f+.5f;
d->AddQuadFilled({x,y},{x+ix,y},{x+ix,y+iy},{x,y+iy},0xff000000|CO(b,16)|CO(g,8)|CO(r,0));
}
}
Guitar for strumming. ~~Unfortunately, requires that ImGuiWindowFlags_NoMove
be set for the window in the testbed to work correctly.~~ Using InvisibleButton
instead of Dummy
also works.
#define V2 ImVec2
#define C1 0xA7A830FF
#define C2 0x775839FF
float amp[6], ml;
int pk;
void FX(ImDrawList* d, V2 a, V2 b, V2 s, ImVec4 m, float t)
{
if (m.z > -1)
pk |= 2;
m.y *= s.y; m.x *= s.x;
m.y += a.y; m.x += a.x;
float st = sin(10*t), th, y, w;
d->AddRectFilledMultiColor(V2(a.x, a.y+40), V2(b.x, b.y-40), C1, C1, C2, C2);
for (float i = a.x + 10, j = 20; i < b.x; i += (j += 10))
d->AddRectFilled(V2(i, a.y+38), V2(i + 8, b.y-38), 0xFF888888, 8);
for (int i = 0; i < 6; i++) {
y = a.y + 48 + i * 16.6; th = 4-i*.5f; w = 1;
int N = 10;
for (int j = 0; j < N; j++) {
float x = a.x + j * s.x / N, k = (w *= -1) * amp[i] * st;
d->AddBezierCurve(V2(x,y+k),V2(x+10,y+k),V2(x+s.x/N-10,y-k),V2(x+s.x/N,y-k),-1u,th);
}
amp[i] *= 0.9;
if (pk == 3) {
float A=ml, B=m.y;
if((A<=y&&B>y)||(A>=y&&B<y))amp[i]+=A-B;
}
}
ml = m.y;
if (pk >>= 1) d->AddTriangleFilled(V2(m.x, m.y), V2(m.x-15, m.y-15), V2(m.x+10, m.y-25), 0xFF0000FF);
}
Complementary colors I guess? I have to optimize the hell out of it to get it under 1K, the code looks so ugly now :'( But it's quite fun!
#define min(a,b) (a<b?a:b)
#define max(a,b) (a>b?a:b)
#define fract(x) (abs(x-floor(x)))
#define rand(x,se) (fract(sin(x)*se))
#define eps 1e-3
#define V2 ImVec2
#define C 10000.0f
float mix(float a,float b,float v) {return a+(b-a)*v;}float ss(float a,float b,float v) {return mix(a,b,3.0f*v*v-2.0f*v*v*v);}float perlin(float v,float se) {v=v*0.7f+eps;float f=floor(v);float s=fract(v);float a=rand(f,se);float b=rand(f+1.0f,se);return ss(a,b,s);}float perlin_d(float v,float se) {v *= 2.0f;float f=floor(v);float s=fract(v);float a=rand(f,se);float b=rand(f+1.0f,se);return ss(a,b,s);}void FX(ImDrawList* d,ImVec2 a,ImVec2 b,ImVec2 sz,ImVec4 mouse,float t) {t *= 0.3f;float sa[]={255.0f,0.0f};float sb[]={125.0f,130.0f};float sc[]={0.0f,255.0f};for(int i=0;i<2;i++) {for(int x=0;x<sz.x;x++) {float p=0.5f+rand(1.0f,(C-sa[i]));float bt=perlin(x/sz.x+t*p,C-sa[i])*sz.y;float tp=perlin(x/sz.x+t*p,C-sb[i])*sz.y;float h=tp-bt;d->AddRectFilled(V2(a.x+x,a.y+bt),V2(a.x+x+1,a.y+bt+h),IM_COL32(sa[i],sb[i],sc[i],200));}}}
Unfortunately, requires that ImGuiWindowFlags_NoMove be set for the window in the testbed to work correctly.
I updated the test bed today (to 0.22) to use InvisibleButton()
instead of Dummy()
for the canvas, so it catches inputs.
Beautiful submissions everyone!
Snake Game (781 bytes)
#define v2 ImVec2
#define px(v,c) d->AddRectFilled(a+v*2,a+v*2+v2(2,2),c);
#define fr(v,x) for(int v=0;v<x;++v)
#define eq(l,r) l.x==r.x&l.y==r.y
#define kd(k) ImGui::IsKeyDown(ImGui::GetKeyIndex(k))
v2 sn[160*90]={{20,20}};
int ln=0,tg=4;
float lf;
v2 dr(1,0);
v2 fd(99,45);
void FX(ImDrawList* d, v2 a, v2 b, v2 sz, v2, float t)
{
if (t-lf>0.033f){
lf=t;
if (ln<tg){ln++;sn[ln]=sn[ln-1];}
else fr(x,ln)sn[x]=sn[x+1];
sn[ln]+=dr;
bool e=sn[ln].x<0|sn[ln].x>159|sn[ln].y<0|sn[ln].y>89;
fr(x,ln-1)e|=eq(sn[x],sn[ln]);
if(e)ln=0,tg=4,sn[0]={20,20};
if(eq(sn[ln],fd))tg+=4,fd={rand()%150+5.f,rand()%80+5.f};
if(kd(1))dr={-1,0};
if(kd(2))dr={1,0};
if(kd(3))dr={0,-1};
if(kd(4))dr={0,1};
}
fr(x,ln+1)px(sn[x],-1);
px(fd,-256);
}
Mosaic. It wasn't what I was trying to do, but ended up in a neat place.
#define v2 ImVec2
#define wh(a) ImColor(1.f,1.f,1.f,a)
#define px(v,c) d->AddRectFilled(a+v*2,a+v*2+v2(2,2),c);
#define fr(v,x) for(int v=0;v<x;++v)
#define frc(x) ((x)-int(x))
#define hash(x) (srand(x),ImLerp(rand()/32767.f,rand()/32767.f,0.5f))
#define noise(x) ImLerp(hash(x),hash(x+1),frc(x))
void FX(ImDrawList* d, v2 a, v2 b, v2 sz, v2 mm, float t)
{
fr(x,160)fr(y,90){
auto rx=x-80;
auto ry=y-45;
auto an=ImAtan2(rx,ry);an=an<0?M_PI*2+an:an;
auto len=(rx*rx+ry*ry+0.1f)+t*4;
auto n0 = noise(an);
auto n1 = noise(len);
auto al= n0+n1;
px(v2(x,y),wh(al));
}
}
Hershey vectorial font! 3.8 KiB. I have not stripped the ASCII chars in the font to make the code fit under 1KiB, bc I wanted to make something useful as well. Sorry for breaking the rules! :D
void Hershey(ImDrawList* d, ImVec2 pos, ImVec2 sca, ImVec2 old, char *s) {
static const char *fnt[] = {
"AQ","IKFVFH@@FCEBFAGBFC","FQEVEO@@MVMO","LVLZE:@@RZK:@@EMSM@@DGRG","[UIZI=@@MZ"
"M=@@RSPUMVIVFUDSDQEOFNHMNKPJQIRGRDPBMAIAFBDD","`YVVDA@@IVKTKRJPHOFODQDSEUGVIVK"
"UNTQTTUVV@@RHPGOEOCQASAUBVDVFTHRH","c[XMXNWOVOUNTLRGPDNBLAHAFBECDEDGEIFJMNNOOQ"
"OSNULVJUISIQJNLKQDSBUAWAXBXC","HKFTEUFVGUGSFQEP","KOLZJXHUFQELEHFCH?J<L:","KOD"
"ZFXHUJQKLKHJCH?F<D:","IQIVIJ@@DSNM@@NSDM","F[NSNA@@EJWJ","IKGBFAEBFCGBG@F>E=",\
"C[EJWJ","FKFCEBFAGBFC","CWUZC:","RUJVGUERDMDJEEGBJALAOBQERJRMQROULVJV","EUGRIS"
"LVLA","OUEQERFTGUIVMVOUPTQRQPPNNKDARA","PUFVQVKNNNPMQLRIRGQDOBLAIAFBECDE","GUN"
"VDHSH@@NVNA","RUPVFVEMFNIOLOONQLRIRGQDOBLAIAFBECDE","XUQSPUMVKVHUFREMEHFDHBKAL"
"AOBQDRGRHQKOMLNKNHMFKEH","FURVHA@@DVRV","^UIVFUESEQFOHNLMOLQJRHREQCPBMAIAFBECD"
"EDHEJGLJMNNPOQQQSPUMVIV","XUQOPLNJKIJIGJELDODPESGUJVKVNUPSQOQJPENBKAIAFBED","L"
"KFOENFMGNFO@@FCEBFAGBFC","OKFOENFMGNFO@@GBFAEBFCGBG@F>E=","DYUSEJUA","F[EMWM@@"
"EGWG","DYESUJEA","USDQDRETFUHVLVNUOTPRPPONNMJKJH@@JCIBJAKBJC","x\\SNRPPQMQKPJO"
"ILIIJGLFOFQGRI@@MQKOJLJIKGLF@@SQRIRGTFVFXHYKYMXPWRUTSUPVMVJUHTFREPDMDJEGFEHCJB"
"MAPASBUCVD@@TQSISGTF","ISJVBA@@JVRA@@EHOH","XVEVEA@@EVNVQURTSRSPRNQMNL@@ELNLQK"
"RJSHSERCQBNAEA","SVSQRSPUNVJVHUFSEQDNDIEFFDHBJANAPBRDSF","PVEVEA@@EVLVOUQSRQSN"
"SIRFQDOBLAEA","LTEVEA@@EVRV@@ELML@@EARA","ISEVEA@@EVRV@@ELML","WVSQRSPUNVJVHUF"
"SEQDNDIEFFDHBJANAPBRDSFSI@@NISI","IWEVEA@@SVSA@@ELSL","CIEVEA","KQMVMFLCKBIAGA"
"EBDCCFCH","IVEVEA@@SVEH@@JMSA","FREVEA@@EAQA","LYEVEA@@EVMA@@UVMA@@UVUA","IWEV"
"EA@@EVSA@@SVSA","VWJVHUFSEQDNDIEFFDHBJANAPBRDSFTITNSQRSPUNVJV","NVEVEA@@EVNVQU"
"RTSRSORMQLNKEK","YWJVHUFSEQDNDIEFFDHBJANAPBRDSFTITNSQRSPUNVJV@@MES?","QVEVEA@@"
"EVNVQURTSRSPRNQMNLEL@@LLSA","UURSPUMVIVFUDSDQEOFNHMNKPJQIRGRDPBMAIAFBDD","FQIV"
"IA@@BVPV","KWEVEGFDHBKAMAPBRDSGSV","FSBVJA@@RVJA","LYCVHA@@MVHA@@MVRA@@WVRA",""
"FUDVRA@@RVDA","GSBVJLJA@@RVJL","IURVDA@@DVRV@@DARA","LOEZE:@@FZF:@@EZLZ@@E:L:",
"COAVO>","LOJZJ:@@KZK:@@DZKZ@@D:K:","KQGPISKP@@DMIRNM@@IRIA","CQA?Q?","HKGVFUES"
"EQFPGQFR","RTPOPA@@PLNNLOIOGNELDIDGEDGBIALANBPD","RTEVEA@@ELGNIOLONNPLQIQGPDNB"
"LAIAGBED","OSPLNNLOIOGNELDIDGEDGBIALANBPD","RTPVPA@@PLNNLOIOGNELDIDGEDGBIALANB"
"PD","RSDIPIPKOMNNLOIOGNELDIDGEDGBIALANBPD","IMKVIVGUFRFA@@COJO","WTPOP?O<N;L:I"
":G;@@PLNNLOIOGNELDIDGEDGBIALANBPD","KTEVEA@@EKHNJOMOONPKPA","IIDVEUFVEWDV@@EOE"
"A","LKFVGUHVGWFV@@GOG>F;D:B:","IREVEA@@OOEE@@IIPA","CIEVEA","S_EOEA@@EKHNJOMOO"
"NPKPA@@PKSNUOXOZN[K[A","KTEOEA@@EKHNJOMOONPKPA","RTIOGNELDIDGEDGBIALANBPDQGQIP"
"LNNLOIO","RTEOE:@@ELGNIOLONNPLQIQGPDNBLAIAGBED","RTPOP:@@PLNNLOIOGNELDIDGEDGBI"
"ALANBPD","INEOEA@@EIFLHNJOMO","RROLNNKOHOENDLEJGILHNGOEODNBKAHAEBDD","IMFVFEGB"
"IAKA@@COJO","KTEOEEFBHAKAMBPE@@POPA","FQCOIA@@OOIA","LWDOHA@@LOHA@@LOPA@@TOPA",
"FRDOOA@@OODA","JQCOIA@@OOIAG=E;C:B:","IROODA@@DOOO@@DAOA","hOJZHYGXFVFTGRHQIOI"
"MGK@@HYGWGUHSIRJPJNILEJIHJFJDIBHAG?G=H;@@GIIGIEHCGBF@F>G<H;J:","CIEZE:","hOFZH"
"YIXJVJTIRHQGOGMIK@@HYIWIUHSGRFPFNGLKJGHFFFDGBHAI?I=H;@@IIGGGEHCIBJ@J>I<H;F:",""
"XYDGDIELGMIMKLOIQHSHUIVK@@DIEKGLILKKOHQGSGUHVKVM" };
for( char c, *glyph; (c = *s++, glyph = (char*)fnt[c - 32], c > 0 && c < 127); ) {
if( c != '\n' && c != '\r' ) {
if( c > 32 ) for( int pen = 0, i = 0; i < (glyph[0] - 65); i++ ) {
int x = glyph[2 + i*2 + 0] - 65, y = glyph[2 + i*2 + 1] - 65;
if( x == -1 && y == -1 ) pen = 0; else {
ImVec2 next = ImVec2(pos.x+sca.x*x, pos.y-sca.y*y);
if( !pen ) pen = 1; else d->AddLine(old, next, 0.9*(ImU32)-1);
old = next;
}
}
pos.x += sca.x * (glyph[1] - 65);
}
}
}
void FX(ImDrawList* d, ImVec2 a, ImVec2 b, ImVec2 sz, ImVec4 mouse, float t) {
ImVec2 pos(a.x,((a+b)/2).y), sca(1,sin(t*5)+1+0.5f);
char str[128]; sprintf(str, "Hello imgui! %5.2fs", t);
Hershey(d, pos, sca, ImVec2(), str);
}
Ugly Doom Fire
#define V2 ImVec2
#define W 64
#define H 48
#define S 0x07
#define T 0x1F
#define U 0x0F
#define I int
#define F float
I w=0,P[]={S,S,S,T,S,S,0x2F,U,S,0x47,U,S,0x57,0x17,S,0x67,T,S,0x77,T,S,0x8F,0x27,S,0x9F,0x2F,S,0xAF,0x3F,S,0xBF,0x47,S,0xC7,0x47,S,0xDF,0x4F,S,0xDF,0x57,S,0xDF,0x57,S,0xD7,0x5F,S,0xD7,0x5F,S,0xD7,0x67,U,0xCF,0x6F,U,0xCF,0x77,U,0xCF,0x7F,U,0xCF,0x87,0x17,0xC7,0x87,0x17,0xC7,0x8F,0x17,0xC7,0x97,T,0xBF,0x9F,T,0xBF,0x9F,T,0xFF,0xFF,0xFF};
char i[W*H];
void FX(ImDrawList *d,V2 a,V2 b,V2 sz,ImVec4,F t){if(!w){memset(i,0,W*H);memset(i+(H-1)*W,27,W);w=1;}for(I x=0;x<W;x++){for(I y=1;y<H;y++){I j=y*W+x,p=i[j];if(!p)i[j-W]=0;else{I r=I(3.f*rand()/RAND_MAX),d=j-r+1;i[d-W]=p-(r&1);}}};I j=0;F sx=1.f/W,sy=1.f/H,k=0.45f,ty=0;for(I y=0;y<H;y++,ty+=sy){F tx=0;for(I x=0;x<W;x++,tx+=sx){V2 c((tx+.5f*sx),(ty+.5f*sy));I *z=P+i[j++]*3;d->AddRectFilled(V2(a.x+(c.x-k*sx)*sz.x,a.y+(c.y-k*sy)*sz.y),V2(a.x+(c.x+k*sx)*sz.x,a.y+(c.y+k*sy)*sz.y),IM_COL32(z[0],z[1],z[2],255));}}}
This one (The Business Card Raytracer from Paul Heckbert) is too big 1 510 bytes 😢 but still fun
#define V2 ImVec2
#define op operator
#define rt return
typedef int i;typedef float f;struct v{f x,y,z;v op+(v r){rt v(x+r.x,y+r.y,z+r.z);}v op*(f r){rt v(x*r,y*r,z*r);}f op%(v r){rt x*r.x+y*r.y+z*r.z;}v(){}v op^(v r){rt v(y*r.z-z*r.y,z*r.x-x*r.z,x*r.y-y*r.x);}v(f a,f b,f c){x=a;y=b;z=c;}v op!(){rt*this*(1/sqrt(*this%*this));}};i G[]={247570,280596,280600,249748,18578,18577,231184,16,16};f R(){rt(f)rand()/RAND_MAX;}i T(v o,v d,f&t,v&n){t=1e9;i m=0;f p=-o.z/d.z;if(.01<p)t=p,n=v(0,0,1),m=1;for(i k=19;k--;)for(i j=9;j--;)if(G[j]&1<<k){v p=o+v(-k,0,-j-4);f b=p%d,c=p%p-1,q=b*b-c;if(q>0){f s=-b-sqrt(q);if(s<t&&s>.01)t=s,n=!(p+d*t),m=2;}}rt m;}v S(v o,v d){f t;v n;i m=T(o,d,t,n);if(!m)rt v(.7,.6,1)*pow(1-d.z,4);v h=o+d*t,l=!(v(9+R(),9+R(),16)+h*-1),r=d+n*(n%d*-2);f b=l%n;if(b<0||T(h,l,t,n))b=0;f p=pow(l%r*(b>0),99);if(m&1){h=h*.2;rt((i)(ceil(h.x)+ceil(h.y))&1?v(3,1,1):v(3,3,3))*(b*.2+.1);}rt v(p,p,p)+S(h,r)*.5;}
v img[512*512];i y=512;void FX(ImDrawList*d,V2 z,V2,V2 sz,ImVec4,f){v g=!v(-6,-16,0),a=!(v(0,0,1)^g)*.002,b=!(g^a)*.002,c=(a+b)*-256+g;f sx=1.f/512.f;f sy=1.f/512.f;if(y){y--;for(i x=512;x--;){v p(13,13,13);for(i r=64;r--;){v t=a*(R()-.5)*99+b*(R()-.5)*99;p=S(v(17,16,8)+t,!(t*-1+(a*(R()+x)+b*(y+R())+c)*16))*3.5+p;}img[y*512+x]=p;}}v*img2=img;f k=0.45f;for(f ty=1.0f;ty;ty-=sy)for(f tx=1.0f;tx;tx-=sx){V2 u((tx+0.5f*sx),(ty+0.5f*sy));v q=*img2++;d->AddRectFilled(V2(z.x+(u.x-k*sx)*sz.x,z.y+(u.y-k*sy)*sz.y),V2(z.x+(u.x+k*sx)*sz.x,z.y+(u.y+k*sy)*sz.y),IM_COL32((i)q.x,(i)q.y,(i)q.z,255));}}
tribute to https://tixy.land/ 473 bytes
#define V2 ImVec2
void FX(ImDrawList* d, V2 a, V2 b, V2 sz, ImVec4, float t)
{
int i =0;
for (int y = 0;y <= sz.y *0.2;y++)
{
for (int x = 0;x<=sz.x * 0.2;x++, i++)
{
//float v = cos(t * cos(i * 2)) * cos(i * cos(x * 2));
float v = tan(t * cos(i * 2))* sin(i * cos(x * 2));
v = ImClamp(v, -1.f, 1.f);
d->AddCircleFilled(V2(x * 10 + a.x,y * 10 + a.x), 5 * fabsf(v), (v>0.f)?0xFFFFFFFF:0xFF0000FF, 12);
}
}
}
Yet another entry because it's a lot of fun!
I tried to emulate DOF with alpha and bigger disks.
800 bytes
#define V2 ImVec2
#define F float
F k;
int i{};
F r() { return F(rand() / 32768.f) * 2.f - 1.f; };
struct P {F x,y,z,a,b,c;void A(){x+=a*k;y+=b*k;z+=c*k;
F ng{0.008f};z-=5.f;F xp=x*cosf(ng)-z*sinf(ng);F zp=x*sinf(ng)+z*cosf(ng);
x=xp;z=zp+5.f;a-=x*k+r()*k;b-=y*k+r()*k;c-=(z-5.0f)*k+r()*k;}};
P p[64];
void FX(ImDrawList* d, V2 o, V2 b, V2 sz, ImVec4, F t)
{int j{};
if (!i) {i=1;for (P&a:p){a={r(),r(),r()+5.f,r(),r(),r() };}}
for (P&a:p){
if (a.z<0.001) continue;
V2 s((a.x/a.z)*sz.x*2.f+sz.x*0.5f+o.x,(a.y/a.z)*sz.y*2.f+sz.y*0.5f+o.y);
int x=(j++)%16;
k=cosf((j/64.f)*3.14592f)*0.002f+0.002f;
F dist=fabsf(a.z-5.f)/2.5f,sc=(10.f+dist*100.f)/a.z;
int tr=int(ImMin(dist*128.f,128.f)+127)<<24;
ImColor col=ImColor::HSV(k*9.f+0.06f,0.8f,1.f,1.f-sqrtf(dist));
d->AddCircleFilled(s,sc,col,12);a.A();
}}
A Breakout/Arkanoid type of game implementation in 1014 bytes. (Control paddle with mouse, use right-click to reset)
#define V2 ImVec2
#define I int
#define F float
#define B bool
#define T static
#define W 320
#define H 180
#define FR(i,m) for(I i=0;i<m;++i)
#define C 0xffffffff
struct B2{V2 l,h;B a=1;B in(V2 p){return(p.x>l.x&&p.x<h.x)&&(p.y>l.y&&p.y<h.y);}};
void FX(ImDrawList* d,V2 a,V2 b,V2 s,ImVec4 m,float t)
{T B re=1;T F cx,cy,r,vx,vy;T F lt=t;T F dt=0;T F bw=W/10;T F bh=H/12;T B2 br[60];T B2 p{{0,b.y-5},{0,b.y}};if(re){cx=a.x+W/2;cy=a.y+H-8;r=3;vx=-1;vy=-3;FR(i,6)FR(j,10){B2& b=br[j+i*10];b.a=1;b.l={a.x+j*bw,a.y+i*bh};b.h={a.x+(j+1)*bw,a.y+(i+1)*bh};}re=0;}FR(i,60){B2& b=br[i];if(!b.a)continue;if(!b.in({cx,cy}))continue;b.a=0;F ol=cx-b.l.x;F or=b.h.x-cx;F ot=cy-b.l.y;F ob=b.h.y-cy;F ox=min(ol,or);F oy=min(ot,ob);B lo=ol<ob;B to=ot<ob;ox<oy?vx=-vx:vy=-vy;}dt=t-lt;lt=t;p.l.x=a.x+m.x*s.x-20;p.h.x=p.l.x+40;if(p.in({cx,cy}))vy=-vy;FR(i,60){if(br[i].a)d->AddRect(br[i].l,br[i].h,C);}d->AddRect(p.l,p.h,C);d->AddCircle({cx,cy},r,C);cx+=vx*dt*30;cy+=vy*dt*30;if(cx<a.x||cx>b.x)vx=-vx;if(cy<a.y)vy=-vy;if (!m.w)re=1;}
Update: @ocornut 's suggestion on Twitter made we want to push it a little further and here it is in 1024 bytes with colors
using V=ImVec2;using I=int;using F=float;using B=bool;
#define T static
#define W 320
#define H 180
#define FR(i,m) for(I i=0;i<m;++i)
#define A d->AddRectFilled
struct B2{V l,h;B a=1;I c=~0;B in(V p){return(p.x>l.x&&p.x<h.x)&&(p.y>l.y&&p.y<h.y);}};
void FX(ImDrawList* d,V a,V b,V s,ImVec4 m,float t){T B re=1;T F cx,cy,r,vx,vy;T F lt=t;T F dt=0;T F bw=W/10;T F bh=H/12;T B2 br[60];T B2 p{{0,b.y-5},{0,b.y}};if(re){cx=a.x+W/2;cy=a.y+H-8;r=3;vx=-1;vy=-3;FR(i,6)FR(j,10){B2& b=br[j+i*10];b.a=1;b.l={a.x+j*bw,a.y+i*bh};b.h={a.x+(j+1)*bw,a.y+(i+1)*bh};b.c=255<<24|rand();}re=0;}FR(i,60){B2& b=br[i];if(!b.a)continue;if(!b.in({cx,cy}))continue;b.a=0;F ol=cx-b.l.x;F or=b.h.x-cx;F ot=cy-b.l.y;F ob=b.h.y-cy;F ox=min(ol,or);F oy=min(ot,ob);B lo=ol<ob;B to=ot<ob;ox<oy?vx=-vx:vy=-vy;}dt=t-lt;lt=t;p.l.x=a.x+m.x*s.x-20;p.h.x=p.l.x+40;if(p.in({cx,cy}))vy=-vy;FR(i,60){B2& b=br[i];if(b.a)A(b.l,b.h,b.c);}A(p.l,p.h,~0);d->AddCircleFilled({cx,cy},r,~0);cx+=vx*dt*30;cy+=vy*dt*30;if(cx<a.x||cx>b.x)vx=-vx;if(cy<a.y)vy=-vy;if (!m.w)re=1;}
Came across this by accident. I think it looks quite cool.
521 bytes
#define V ImVec2
#define CF d->AddCircleFilled
#define RAND(a) (float)rand()/(float)(RAND_MAX/a)
void FX(ImDrawList*d,V a,V b,V s,ImVec4 m,float t) {
static bool o = true;
static V bs[1000];
if(o){
o=false;
for(int i=0;i<1000;++i){
float g=RAND(IM_PI/2)+(IM_PI/4);
float r=RAND(50)+5;
bs[i]=V(g,r);
}
}
for(int i=0;i<1000;++i){
float
g=bs[i].x,
r=bs[i].y;
r+=sin(t+i)*100;
CF(V(r*cos(g),r*sin(g))+(s/2+a)+V(r*cos(t),0),i%2+1,IM_COL32(r+100,50,fabs(r)+100,200));
}
}
I did a poor man’s tetris I’d like top put some more features (~~like, detection for when the game is lost, for example~~ [mostly done]), but I’m at ~~1021~~ ~~994~~ 1024 bytes, so I can’t really add more
By the way, I also took the liberty to change from mouse_data.y = (io.MousePos.y - p1.y) / size.y;
to mouse_data.y = (io.MousePos.y - p0.y) / size.y;
in the gist, so that it corresponds to the specs on the OP
In case anyone wonders about the controls : the piece follows the mouse’s y coordinate, mouse left click rotates it, and right click accelerates it.
disclaimer: colors were modified to squeeze some space for end-game detection
source code (1024 bytes)
#define F(a,b)for(I a=b;a--;)
#define S(a,b)s(c[a],c[b]);
#define I int
#define V ImVec2
#define L(w,z) d->AddLine(a+V(p,y)*8,a+V(p+w,y+z)*8,g-1^~0);
#define P(x,y) a+V(x,y)*8,a+V(x+1,y+1)*8
#define R(x,y,c) d->AddRectFilled(P(x,y),c);
I g=1;I pc[][9]={{1,1,1},{1,1,0,1,1},{1,1,1,0,1},{1,1,1,1},{1,1,1,0,0,1},{1,1,0,0,1,1},{0,1,1,1,1}};I*c=0;I p=0;I f=0;I T[41][21];void s(I&a,I&b){I d=a;a=b;b=d;}void FX(ImDrawList * d,V a,V,V,ImVec4 m,float t){F(i,21)T[40][i]=1;if(!p)F(i,40){c=pc[(I)t%7];I s=-21;F(j,21)s+=T[i][j];if(!s){F(j,i)F(k,21)T[j+1][k]=T[j][k];++i;}}if(!m.z){S(0,6)S(1,5)S(2,8)S(3,7)S(3,5)S(0,8)}I y=m.y*23-1.;if(y<0){if(!(c[0]|c[1]|c[2]))F(i,3){c[i]=c[i+3];c[i+3]=c[i+6];c[i+6]=0;}y=0;}if(y>18){if(!(c[6]|c[7]|c[8]))F(i,3){c[i+6]=c[i+3];c[i+3]=c[i];c[i]=0;}y=18;}p+=m.w>0|!(++f%8);F(i,3)F(j,3)if(c[j*3+i]&T[p+i+1][y+j]){F(k,3)F(l,3)T[p+k][y+l]|=g&c[l*3+k];p=0;}F(i,21)g&=!T[0][i];F(i,40)F(j,21)if(T[i][j])R(i,j,g?~0xFDA:~0)if(g)F(i,3)F(j,3)if(c[j*3+i])R(i+p,j+y,~0xFF14)L(3,0)L(0,3)p+=3;y+=3;L(0,-3)L(-3,0)p-=3;}
expanded source code (3409 bytes)
#include <algorithm>
#include <numeric>
static int board[41][21];
static int pieces_list[][9] = {
{1, 1, 1,
0, 0, 0,
0, 0, 0},
{0, 1, 1,
0, 1, 1,
0, 0, 0},
{1, 1, 1,
0, 1, 0,
0, 0, 0},
{1, 1, 1,
1, 0, 0,
0, 0, 0},
{1, 1, 1,
0, 0, 1,
0, 0, 0},
{1, 1, 0,
0, 1, 1,
0, 0, 0},
{0, 1, 1,
1, 1, 0,
0, 0, 0}};
static int * current_piece = nullptr;
static int piece_pos = 0;
static int frame_count = 0;
static int game_ongoing = 1;
void FX(ImDrawList * d, ImVec2 a, ImVec2, ImVec2, ImVec4 mouse, float t) {
++frame_count;
std::fill_n(board[40], 21, 1);// could be done only once
if (piece_pos == 0) {
for (int i = 39; i >= 0; --i) {
current_piece = pieces_list[static_cast<int>(t) % 7];// selecting new piece (such randomness)
if (std::accumulate(board[i], board[i] + 21, 0) == 21) {// checking for full line
for (int j = i; j > 0; --j) {
for (int k = 0; k < 21; ++k) {
board[j + 1][k] = board[j][k];
}
}
++i;// rollback
}
}
}
if (mouse.z == 0.f) {// rotating piece
int copy[9]{};
std::copy(current_piece, current_piece + 9, copy);
current_piece[0] = copy[2];
current_piece[1] = copy[5];
current_piece[2] = copy[8];
current_piece[3] = copy[1];
current_piece[5] = copy[7];
current_piece[6] = copy[0];
current_piece[7] = copy[3];
current_piece[8] = copy[6];
}
int y = mouse.y * 23 - 1.;// piece y coord
if (y < 0) {
if (!current_piece[0] && !current_piece[1] && !current_piece[2]) {
for (int i = 0; i < 3; ++i) {
current_piece[i] = current_piece[i + 3];
current_piece[i + 3] = current_piece[i + 6];
current_piece[i + 6] = 0;
}
}
y = 0;
} else if (y > 18) {
if (!current_piece[6] && !current_piece[7] && !current_piece[8]) {
for (int i = 0; i < 3; ++i) {
current_piece[i + 6] = current_piece[i + 3];
current_piece[i + 3] = current_piece[i];
current_piece[i] = 0;
}
}
y = 18;
}
if (mouse.w > 0 || frame_count % 8 == 0) {
++piece_pos;
}
bool collided = false;
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
if (current_piece[j * 3 + i] && board[piece_pos + i + 1][y + j]) {
collided = true;
}
}
}
if (collided) {
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
if (game_ongoing && current_piece[j * 3 + i]) {
board[piece_pos + i][y + j] = 1;
}
}
}
piece_pos = 0;
}
if (std::accumulate(board[0], board[0] + 21, 0) != 0) {
// game lost
game_ongoing = 0;
}
for (int i = 0; i < 40; ++i) {
for (int j = 0; j < 21; ++j) {
if (board[i][j]) {
d->AddRectFilled(a + ImVec2(i, j) * 8, a + ImVec2(i + 1, j + 1) * 8, game_ongoing ? IM_COL32(37,240,255,255) : IM_COL32_WHITE);
}
}
}
if (game_ongoing) {
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
if (current_piece[j * 3 + i]) {
d->AddRectFilled(a + ImVec2(i + piece_pos, j + y) * 8, a + ImVec2(i + piece_pos + 1, j + y + 1) * 8, IM_COL32(235,0,255,255));
}
}
}
d->AddLine(a + ImVec2(piece_pos, y) * 8, a + ImVec2(piece_pos + 3, y) * 8, IM_COL32_WHITE);
d->AddLine(a + ImVec2(piece_pos, y) * 8, a + ImVec2(piece_pos, y + 3) * 8, IM_COL32_WHITE);
d->AddLine(a + ImVec2(piece_pos + 3, y + 3) * 8, a + ImVec2(piece_pos + 3, y) * 8, IM_COL32_WHITE);
d->AddLine(a + ImVec2(piece_pos + 3, y + 3) * 8, a + ImVec2(piece_pos, y + 3) * 8, IM_COL32_WHITE);
}
}
I made a Mandelbrot set visualization with mouse-centered zoom/de-zoom:
I also took the liberty to change from mouse_data.y = (io.MousePos.y - p1.y) / size.y;
to mouse_data.y = (io.MousePos.y - p0.y) / size.y;
in the gist, so that it corresponds to the specs on the OP
Source code (1024 bytes)
#include <complex>
ImVec2 shift(-2.12,-0.9);
float scale = 0.01;
float zoom_factor = 0.9;
size_t max_iter = 32;
void FX(ImDrawList* d, ImVec2 a, ImVec2, ImVec2 sz, ImVec4 m, float)
{
if((m.x >= 0 && m.x <= 1 && m.y >= 0 && m.y <= 1) && (m.z > 0 || m.w > 0))
{
float zf = m.z > 0 ? zoom_factor : 1 / zoom_factor;
shift.x -= (m.x * sz.x * scale * (zf - 1));
shift.y -= (m.y * sz.y * scale * (zf - 1));
scale *= zf;
}
for(size_t x = 0; x < sz.x; ++x)
{
for (size_t y = 0; y < sz.y; ++y)
{
std::complex<double> c(shift.x + x /(sz.x-1.0)*(sz.x * scale), shift.y + y /(sz.y-1.0)*(sz.y * scale)), z;
size_t iter;
for (iter = 0; iter < max_iter && std::abs(z) < 2.0; ++iter)
z = z * z + c;
double v = std::log(iter) / std::log(max_iter - 1);
if(iter < max_iter)
d->AddRectFilled(ImVec2(a.x+ x,a.y+ y), ImVec2(a.x+ x +1,a.y+ y +1), IM_COL32((v*255), 255-(v*255), 255, 255));
else
d->AddRectFilled(ImVec2(a.x+ x,a.y+ y), ImVec2(a.x+ x +1,a.y+ y +1), IM_COL32(0, 0,0, 255));
}
}
}
Mandatory tunnel 971 bytes
#define V2 ImVec2
#define F float
V2 conv(V2 v, F z, V2 sz, V2 o){return V2((v.x/z)*sz.x*5.f+sz.x*0.5f,(v.y/z)*sz.y*5.f+sz.y*0.5f)+o;}
V2 R(V2 v, F ng){ng*=0.1f;return V2(v.x*cosf(ng)-v.y*sinf(ng),v.x*sinf(ng)+v.y*cosf(ng));}
void FX(ImDrawList* d, V2 o, V2 b, V2 sz, ImVec4, F t){d->AddRectFilled(o,b,0xFF000000,0);t*=4;
for (int i = 0; i < 20; i++){
F z=21.-i-(t-floorf(t))*2.,ng=-t*2.1+z,ot0=-t+z*0.2,ot1=-t+(z+1.)*0.2,os=0.3;
V2 s[]={V2(cosf((t+z)*0.1)*0.2+1.,sinf((t+z)*0.1)*0.2+1.),V2(cosf((t+z+1.)*0.1)*0.2+1.,sinf((t+z+1.)*0.1)*0.2+1.)};
V2 of[]={V2(cosf(ot0)*os,sinf(ot0)*os),V2(cosf(ot1)*os,sinf(ot1)*os)};
V2 p[]={V2(-1,-1),V2(1,-1),V2(1,1),V2(-1,1)};
ImVec2 pts[8];int j;
for (j=0;j<8;j++){int n = (j/4);pts[j]=conv(R(p[j%4]*s[n]+of[n],ng+n),(z+n)*2.,sz,o);}
for (j=0;j<4;j++){V2 q[4]={pts[j],pts[(j+1)%4],pts[((j+1)%4)+4],pts[j+4]};
F it=(((i&1)?0.5:0.6)+j*0.05)*((21.-z)/21.);
d->AddConvexPolyFilled(q,4,ImColor::HSV(0.6+sinf(t*0.03)*0.5,1,sqrtf(it)));
}}}
Awesome stuff so far.
I started by looking at some of the first entries, and decided to play with circles. So I went on googling for something to find some inspiration, and I though it would be cool to replicate this: Black and White Minimal Circles by Colin Saunders.
Anyway, while playing with the drawing algorithm something went wrong and I ended up with an out of control Death Star laser-shooting randomly onto the galaxy!
Death Star - 926 bytes
#define V2 ImVec2
#define CR(o,r,c) d->AddCircle(o,r,c)
#define LN(a,b,c) d->AddLine(a,b,c)
#define FORI(n) for(int i=0;i<n;++i)
void FX(ImDrawList*d,V2 a,V2 b,V2 sz,ImVec4,float t){
static float pr{t}, tm{};
static auto st=[](){ImVector<V2> v; v.resize(512); FORI(512) v[i]=V2(rand()%480,rand()%270); return v;}();
V2 hsz{sz/2}, o{a+hsz}, dir{sinf(tm),cosf(tm)}, c, sf;
float dt{}, rad{hsz.y*0.7}, r;
int cnt{rad/2}, sh{!(int(t)%2)}, inc;
FORI(512) CR(st[i],1,0xFF333333);
if(!sh) dt=t-pr,tm+=dt;
pr=t;
ImU32 BL{0xFF000000}, WH{0xFFFFFFFF}, G{0xFF00FF00};
FORI(cnt){r=rad-i,inc=i; c={o.x+inc*dir.x,o.y+inc*dir.y}; CR(c,r,WH);}
V2 la{o.x-rad,o.y}, lb{o.x+rad,o.y};
LN(la,lb,BL);
FORI(cnt){r=rad-(cnt+i), inc=(cnt-i); c={o.x+inc*dir.x,o.y+inc*dir.y}; CR(c,r,WH);}
if(sh){la=o+dir*rad/8; lb=o+dir*hsz.x*2; sf={3*sinf(t*1024),3*cosf(t*1024)}; LN(la,lb,G); LN(la+sf,lb+sf,G); LN(la-sf,lb-sf,G);}
}
Following the Mandelbrot set visualization, this time I made a Julia sets visualization:
Inspired by this Wikipedia gif, the Julia sets represented are generated by
z^{2} + 0.7885 e^{ia}
where a
ranges from 0 to 2π.
Source code (1024 bytes)
#include <complex>
#define C(r,g,b) IM_COL32(255*(r), 255*(g), 255*(b), 255)
size_t max_iter = 256;
float color_f = 48.0;
float speed_f = 2.0;
ImU32 to_color(float v)
{
v = 1.f - std::log(v * color_f+1) / std::log(color_f+1);
size_t p = v * 5.0;
double g = (v * 5.0 - p);
ImU32 colors[] = {C(1, 0, 1-g),C(1, g, 0),C(1-g, 1, 0),C(0, 1, g),C(0, 1-g, 1),C(0, 0, 1),};
return colors[p];
}
void FX(ImDrawList* d, ImVec2 a, ImVec2, ImVec2 sz, ImVec4, float t)
{
for (size_t x = 0; x < sz.x; ++x)
for (size_t y = 0; y < sz.y; ++y)
{
std::complex<double> z(-2.1+x/(sz.x-1.0)*(sz.x*0.013), -1.15+y/(sz.y-1.0)*(sz.y*0.013));
std::complex<double> c(0.7885*std::cos(t/speed_f),0.7885*std::sin(t/speed_f));
size_t iter;
for (iter = 0; iter < max_iter && std::abs(z) < 2.0; ++iter)
z=z*z+c;
if(iter < max_iter)
d->AddRectFilled(ImVec2(a.x+x,a.y+y), ImVec2(a.x+x+1,a.y+y+1), to_color((float)iter / max_iter));
else
d->AddRectFilled(ImVec2(a.x+x,a.y+y), ImVec2(a.x+x+1,a.y+y+1), 0XFF000000);
}
}
A little driving game. Use the mouse buttons to move left and right.
#define V2 ImVec2
#define R d->AddRectFilled
#define RED 0xff0000ff
void FX(ImDrawList* d, V2 a, V2 b, V2 sz, ImVec4 m, float t)
{
static float mx = a.x + 160;
float dy = 36, dt = 8;
int i = fmodf(dt * t, 2) < 1 ? 1 : 0;
auto v = fmodf(dt * t, 1), y = a.y - dy + v * dy;
for (int s = 1 + sz.y / dy; s > 0; --s, y += dy) {
float c = sinf(t + v / dy - s / dy) * 40;
V2 tl = { c + a.x + sz.x / 2 - 64, y };
V2 br = { c + a.x + sz.x / 2 + 64, y + dy };
R(tl, br, (++i & 1) ? 0xffffffff : RED);
tl.x += 8;
br.x -= 8;
R(tl, br, 0xff3f3f3f);
}
if (m.z >= 0) mx--; if (m.w >= 0) mx++;
R({ mx - 8, b.y - sz.y / 4 - 15 }, { mx + 8, b.y - sz.y / 4 + 15 }, 0xff00ff00, 4);
R({ mx - 7, b.y - sz.y / 4 - 8 }, { mx + 7, b.y - sz.y / 4 + 12 }, 0xff007f00, 4);
R({ mx - 6, b.y - sz.y / 4 + 12 }, { mx - 2, b.y - sz.y / 4 + 14 }, RED);
R({ mx + 2, b.y - sz.y / 4 + 12 }, { mx + 6, b.y - sz.y / 4 + 14 }, RED);
}
Inspired by this party, my entry is a Game of Life pulsar oscillator (see Wikipedia)
Uses left mouse button clicks to process next generation.
My algorithm is probably not efficient, and I need quite a big data input, so had to make the code ugly to get to 994 bytes:
#define V2 ImVec2
#define S 17
#define L 16
#define I int
#define F float
I h[S][S],g[S][S]={{0},{0},{0,0,0,0,1,1,1,0,0,0,1,1,1},{0},{0,0,1,0,0,0,0,1,0,1,0,0,0,0,1},{0,0,1,0,0,0,0,1,0,1,0,0,0,0,1},{0,0,1,0,0,0,0,1,0,1,0,0,0,0,1},{0,0,0,0,1,1,1,0,0,0,1,1,1},{0},{0,0,0,0,1,1,1,0,0,0,1,1,1},{0,0,1,0,0,0,0,1,0,1,0,0,0,0,1},{0,0,1,0,0,0,0,1,0,1,0,0,0,0,1},{0,0,1,0,0,0,0,1,0,1,0,0,0,0,1},{0},{0,0,0,0,1,1,1,0,0,0,1,1,1}};
void FX(ImDrawList* d,V2 a,V2 b,V2 sz,ImVec4 m,F t)
{
if (!m.z)
{
for(I i=1;i<L;i++)
for(I j=1;j<L;j++)
{
I c=g[i-1][j-1]+g[i-1][j]+g[i-1][j+1]+g[i][j-1]+g[i][j+1]+g[i+1][j-1]+g[i+1][j]+g[i+1][j+1];
h[i][j]=(g[i][j]&&c==2)||c==3;
}
for(I i=1;i<L;i++)
for(I j=1;j<L;j++)
g[i][j]=h[i][j];
}
F s=1.f/S; I i=0,j=0;
for(F y=0.f;y<1.f;y+=s,j++,i=0)
for(F x=0.f;x<1.f;x+=s,i++)
{
V2 c((x+.5f*s),(y+.5f*s));
F k = .45f;
d->AddRectFilled(
V2(a.x+(c.x-k*s)*sz.x,a.y+(c.y-k*s)*sz.y),
V2(a.x+(c.x+k*s)*sz.x,a.y+(c.y+k*s)*sz.y),
g[i][j]*0xFFFF0000);
}
}
Non competing (I mean it’s not really my idea ; and truthfully it’s not really an effect), inspired by the previous comment, Conway’s Game of Life
Use left click to place/remove a cell, and right click to start/stop.
Source code (1024 bytes)
#define V ImVec2
#define R(c)d->AddRectFilled(a+V(1,1)+V(i-1,j-1)*8, a-V(1,1)+V(i,j)*8,c)
int board[42][24]{};
int r = 0;
int f = 0;
void FX(ImDrawList * d,V a,V,V,ImVec4 m, float t) {
if (r && ++f%10==1) {
int n[42][22] = {};
for (int i = 41; --i;) {
for (int j = 23; --j;) {
int s = -board[i][j];
for (int k = 3; k--;) {
for (int l = 3; l--;) {
s += board[i + k - 1][j + l - 1];
}
}
if (s == 3)
n[i][j] = board[i][j]?1:2;
if (s == 2)
n[i][j] = board[i][j];
}
}
for (int i = 41; --i;)
for (int j = 23; --j;)
board[i][j] = n[i][j];
}
int x = int(m.x * 40);
int y = int(m.y * 22.5f);
if (m.z == 0.f && !r)
if (x >= 0 && x < 41 && y >= 0 && y < 23)
board[x+1][y+1] = !board[x+1][y+1];
if (m.w == 0.f)
r = !r;
for (int i = 41; --i;)
for (int j = 23; --j;) {
if (board[i][j] == 2) {
board[i][j] = 1+!!(f%10);
R(0xFF55AAAA);
}
if (board[i][j] == 1)
R(0xFFFF8855);
}
d->AddRect(a + V(x,y) * 8, a + V(x+1,y+1) * 8, ~0);
}
A kind of 2D version of the old electropaint effect using translucent circles. Works well against either a light or dark background.
Source Code (1021 bytes)
typedef float F;
struct G { F v0, v1; int mc, w; F ms, ma; int c; F v, d, a; };
F x(G& g){
g.c++;
if (g.c > g.mc){
g.a = (drand48() - 0.5) * 2 * g.ma;
g.c = 0;
}
g.d += g.a;
g.d = fmin(g.ms, fmax(-g.ms, g.d));
g.v += g.d;
if (g.w)
g.v = g.v0 + fmodf(g.v - g.v0, g.v1 - g.v0);
else
g.v = fmin(g.v1, fmax(g.v0, g.v));
return g.v;
}
int mx=999;
G rc={-15, 15, 150, 0, 0.05, 0.005, mx};
G ac={0, 360, 120, 1, 1, 0.025, mx};
G da={0, 360, 80, 1, 0.1, 0.01, mx};
G rm={0, 255, 95, 0, 5, 1.275, mx};
G gm={0, 255, 40, 0, 5, 1.275, mx};
struct Elt{F r;F a;F da;F cr;F cg;};
Elt ne() {return {x(rc),x(ac),x(da),x(rm),x(gm)};}
#include <deque>
std::deque<Elt> es(80);
void FX(ImDrawList* d, V2 a, V2 b, V2 sz, ImVec4, F){
es.pop_back();
es.push_front(ne());
V2 o = a + sz * 0.5;
for (Elt& e : es){
F ar=e.a*M_PI/180;
V2 p(sinf(ar), cosf(ar));
p *= sz.y*e.r/37.5;
F cr = sz.y/10 * (0.25 + fabs(e.r/20));
d->AddCircleFilled(o+p, cr, IM_COL32(e.cr, e.cg, (255-e.cr*e.cg/255), 48));
d->AddCircle(o+p, cr, 64<<24);
e.a += e.da;
}
}
Simple DFS Maze Generation, I had a rough time getting it under 1024 bytes
Source Code (989 bytes)
#include <random>
#include <functional>
#define arf q->AddRectFilled
#define al q->AddLine
#define V ImVec2
#define I(p,o) V(a.x+j*5+p,a.y+i*5+o)
#define J I(0,0)
#define K I(5,5)
#define wh 0xFFFFFFFF
#define bl 0xFF000000
#define fr(x,y) for(int x=0;x<y;++x)
#define P int p,int&c,int m,int&f
int u=1,d=2,r=4,l=8;auto S=std::random_device()();float W=0,I=.002;void FX(ImDrawList*q,V a,V,V s,ImVec4,float t){if(t>W+I)W=t;int w=64,h=36,C=0,F=-1;std::vector<int>G(w*h,15);std::mt19937 N(S);std::function<void(P)>fn=[&](P){if(c==m){f=p;return;}int n[]={-w,w,1,-1};int T[]={0,1,2,3};std::shuffle(T,T+4,N);fr(i,4){if(c==m)return;int L=p+n[T[i]];if(L<0||L>w*h-1||(L<p&&!(p%w))||(L>p&&!((p+1)%w)))continue;if(G[L]==15){auto v=1<<T[i];G[p]^=v;G[L]^=(v==u||v==r)?v*2:v/2;fn(L,++c,m,f);}}};fn(0,C,W/I,F);fr(i,h)fr(j,w){int e=G[i*w+j];if(i*w+j==F)arf(J,K,0xFF00FF00);else if(e!=15)arf(J,K,wh);if(e&u)al(J,I(5,0),bl);if(e&d)al(I(0,5),K,bl);if(e&l)al(I(0,5),J,bl);if(e&r)al(K,I(5,0),bl);}}
Non competing (I mean it’s not really my idea ; and truthfully it’s not really an effect), inspired by the previous comment, Conway’s Game of Life
![]()
Use left click to place/remove a cell, and right click to start/stop. Source code (1024 bytes)
That's a nice improvement over my attempt! And it can double as a paint program :-D