X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=hacks%2Ftessellimage.c;h=b9a8bd93ee6f05d1307d7272948e2767e5615b22;hp=4fcf7b176c77a39e50c0d49333b336cbb87f6aba;hb=c85f503f5793839a6be4c818332aca4a96927bb2;hpb=78add6e627ee5f10e1fa6f3852602ea5066eee5a diff --git a/hacks/tessellimage.c b/hacks/tessellimage.c index 4fcf7b17..b9a8bd93 100644 --- a/hacks/tessellimage.c +++ b/hacks/tessellimage.c @@ -12,9 +12,6 @@ #include "screenhack.h" #include "delaunay.h" -#undef DO_VORONOI - - #ifndef HAVE_JWXYZ # define XK_MISCELLANY # include @@ -43,8 +40,20 @@ struct state { async_load_state *img_loader; XRectangle geom; Bool button_down_p; + enum { DELAUNAY, VORONOI } mode; }; +typedef struct { + int npoints; + XPoint ctr; + XPoint *p; +} voronoi_polygon; + +typedef struct { + XPoint p; + double slope; +} voronoi_pa; + /* Returns the current time in seconds as a double. */ @@ -276,6 +285,23 @@ analyze (struct state *st) unsigned int w, h, bw, d; unsigned long histo[256]; + { + char *s = get_string_resource (st->dpy, "mode", "Mode"); + if (!s || !*s || !strcasecmp(s, "random")) + st->mode = (random() & 1) ? DELAUNAY : VORONOI; + else if (!strcasecmp(s, "delaunay")) + st->mode = DELAUNAY; + else if (!strcasecmp(s, "voronoi")) + st->mode = VORONOI; + else + { + fprintf (stderr, + "%s: mode must be delaunay, voronoi or random, not \"%s\"\n", + progname, s); + exit (1); + } + } + flush_cache (st); /* Convert the loaded pixmap to an XImage. @@ -406,7 +432,6 @@ analyze (struct state *st) } -#ifndef DO_VORONOI /* True if the distance between any two corners is too small for it to make sense to draw an outline around this triangle. */ @@ -422,17 +447,47 @@ small_triangle_p (const XPoint *p) if (abs (p[2].y - p[0].y) < min) return True; return False; } -#endif /* DO_VORONOI */ -#ifdef DO_VORONOI +static Bool +small_cell_p (const voronoi_polygon *p) +{ + int min = 4; + if (abs (p->p[0].x - p->ctr.x) < min) return True; + if (abs (p->p[0].y - p->ctr.y) < min) return True; + return False; +} + + +static int +cmp_ccw (const void *v1, const void *v2) +{ + const voronoi_pa *p1,*p2; + p1 = v1; + p2 = v2; + if (p1->slope < p2->slope) return -1; + else if (p1->slope > p2->slope) return 1; + return 0; +} + +static void +sort_ccw (XPoint *ctr, XPoint *p, int npoints) +{ + voronoi_pa *pa = (void *) malloc (npoints * sizeof(*pa)); + int i; + for (i = 0; i < npoints; i++) + { + pa[i].p = p[i]; + pa[i].slope = atan2 (p[i].x - ctr->x, p[i].y - ctr->y); + } + qsort (pa, npoints, sizeof(*pa), cmp_ccw); + for (i = 0; i < npoints; i++) + p[i] = pa[i].p; + free (pa); +} -typedef struct { - int npoints; - XPoint *p; -} voronoi_polygon; static voronoi_polygon * -delaunay_to_voronoi (int np, XYZ *p, int nv, ITRIANGLE *v) +delaunay_to_voronoi (int np, XYZ *p, int nv, ITRIANGLE *v, double scale) { struct tri_list { int count, size; @@ -444,15 +499,6 @@ delaunay_to_voronoi (int np, XYZ *p, int nv, ITRIANGLE *v) calloc (np + 1, sizeof(*vert_to_tri)); voronoi_polygon *out = (voronoi_polygon *) calloc (np + 1, sizeof(*out)); -/* - for (i = 0; i < np; i++) - printf("# p %d = %d %d\n", i, (int)p[i].x, (int)p[i].y); - printf("\n"); - for (i = 0; i < nv; i++) - printf("@ t %d = %d %d %d\n", i, (int)v[i].p1, (int)v[i].p2, (int)v[i].p3); - printf("\n"); -*/ - /* Iterate the triangles to construct a map of vertices to the triangles that contain them. */ @@ -474,59 +520,47 @@ delaunay_to_voronoi (int np, XYZ *p, int nv, ITRIANGLE *v) } } -/* - for (i = 0; i < nv; i++) - { - struct tri_list *t = &vert_to_tri[i]; - printf("p %d [%d %d]:", i, (int)p[i].x, (int)p[i].y); - for (j = 0; j < t->count; j++) { - int tt = t->tri[j]; - printf(" t %d [%d(%d %d) %d(%d %d) %d(%d %d)]", - tt, - (int)v[tt].p1, - (int)p[v[tt].p1].x, (int)p[v[tt].p1].y, - (int)v[tt].p2, - (int)p[v[tt].p2].x, (int)p[v[tt].p2].y, - (int)v[tt].p3, - (int)p[v[tt].p3].x, (int)p[v[tt].p3].y - ); - if (tt < 0 || tt >= nv) abort(); - } - printf("\n"); - } -*/ - /* For every vertex, compose a polygon whose corners are the centers of each triangle using that vertex. Skip any with less than 3 points. + + This is currently omitting the voronoi cells that should touch the edges + of the outer rectangle. Not sure exactly how to include those. */ for (i = 0; i < np; i++) { + long ctr_x = 0, ctr_y = 0; struct tri_list *t = &vert_to_tri[i]; int n = t->count; if (n < 3) n = 0; out[i].npoints = n; + if (n == 0) continue; + out[i].ctr.x = out[i].ctr.y = 0; out[i].p = (n > 0 ? (XPoint *) calloc (out[i].npoints + 1, sizeof (*out[i].p)) : 0); -//printf("%d: ", i); for (j = 0; j < out[i].npoints; j++) { ITRIANGLE *tt = &v[t->tri[j]]; - out[i].p[j].x = (p[tt->p1].x + p[tt->p2].x + p[tt->p3].x) / 3; - out[i].p[j].y = (p[tt->p1].y + p[tt->p2].y + p[tt->p3].y) / 3; -//printf(" [%d: %d %d]", j, out[i].p[j].x, out[i].p[j].y); + out[i].p[j].x = scale * (p[tt->p1].x + p[tt->p2].x + p[tt->p3].x) / 3; + out[i].p[j].y = scale * (p[tt->p1].y + p[tt->p2].y + p[tt->p3].y) / 3; + ctr_x += out[i].p[j].x; + ctr_y += out[i].p[j].y; } -//printf("\n"); + out[i].ctr.x = ctr_x / out[i].npoints; /* long -> short */ + out[i].ctr.y = ctr_y / out[i].npoints; + if (out[i].ctr.x < 0) abort(); + if (out[i].ctr.y < 0) abort(); + sort_ccw (&out[i].ctr, out[i].p, out[i].npoints); } + for (i = 0; i < np+1; i++) + if (vert_to_tri[i].tri) + free (vert_to_tri[i].tri); free (vert_to_tri); + return out; } -#endif /* DO_VORONOI */ - - - static void tessellate (struct state *st) @@ -667,83 +701,92 @@ tessellate (struct state *st) XFillRectangle (st->dpy, st->output, st->pgc, 0, 0, st->xgwa.width, st->xgwa.height); -#ifdef DO_VORONOI - - voronoi_polygon *polys = delaunay_to_voronoi (nv, p, ntri, v); - for (i = 0; i < nv; i++) + switch (st->mode) { + case VORONOI: { - if (polys[i].npoints >= 3) + voronoi_polygon *polys = + delaunay_to_voronoi (nv, p, ntri, v, wscale); + for (i = 0; i < nv; i++) { - unsigned long color = XGetPixel (st->img, p[i].x, p[i].y); - XSetForeground (st->dpy, st->pgc, color); - XFillPolygon (st->dpy, st->output, st->pgc, - polys[i].p, polys[i].npoints, - Convex, CoordModeOrigin); - - if (st->outline_p) + if (polys[i].npoints >= 3) { - XColor bd; - double scale = 0.8; - bd.pixel = color; - XQueryColor (st->dpy, st->xgwa.colormap, &bd); - bd.red *= scale; - bd.green *= scale; - bd.blue *= scale; - - /* bd.red = 0xFFFF; bd.green = 0; bd.blue = 0; */ - - XAllocColor (st->dpy, st->xgwa.colormap, &bd); - XSetForeground (st->dpy, st->pgc, bd.pixel); - XDrawLines (st->dpy, st->output, st->pgc, - polys[i].p, polys[i].npoints, - CoordModeOrigin); - XFreeColors (st->dpy, st->xgwa.colormap, &bd.pixel, 1, 0); + unsigned long color = XGetPixel (st->img, + polys[i].ctr.x / wscale, + polys[i].ctr.y / wscale); + XSetForeground (st->dpy, st->pgc, color); + XFillPolygon (st->dpy, st->output, st->pgc, + polys[i].p, polys[i].npoints, + Convex, CoordModeOrigin); + + if (st->outline_p && !small_cell_p(&polys[i])) + { + XColor bd; + double scale = 0.8; + bd.pixel = color; + XQueryColor (st->dpy, st->xgwa.colormap, &bd); + bd.red *= scale; + bd.green *= scale; + bd.blue *= scale; + + /* bd.red = 0xFFFF; bd.green = 0; bd.blue = 0; */ + + XAllocColor (st->dpy, st->xgwa.colormap, &bd); + XSetForeground (st->dpy, st->pgc, bd.pixel); + XDrawLines (st->dpy, st->output, st->pgc, + polys[i].p, polys[i].npoints, + CoordModeOrigin); + XFreeColors (st->dpy, st->xgwa.colormap, &bd.pixel, + 1, 0); + } } + if (polys[i].p) free (polys[i].p); + polys[i].p = 0; } - if (polys[i].p) free (polys[i].p); - polys[i].p = 0; + free (polys); } - free (polys); - -#else /* !DO_VORONOI */ + break; - for (i = 0; i < ntri; i++) - { - XPoint xp[3]; - unsigned long color; - xp[0].x = p[v[i].p1].x * wscale; xp[0].y = p[v[i].p1].y * wscale; - xp[1].x = p[v[i].p2].x * wscale; xp[1].y = p[v[i].p2].y * wscale; - xp[2].x = p[v[i].p3].x * wscale; xp[2].y = p[v[i].p3].y * wscale; - - /* Set the color of this triangle to the pixel at its midpoint. */ - color = XGetPixel (st->img, - (xp[0].x + xp[1].x + xp[2].x) / (3 * wscale), - (xp[0].y + xp[1].y + xp[2].y) / (3 * wscale)); - - XSetForeground (st->dpy, st->pgc, color); - XFillPolygon (st->dpy, st->output, st->pgc, xp, countof(xp), - Convex, CoordModeOrigin); - - if (st->outline_p && !small_triangle_p(xp)) - { /* Border the triangle with a color that is darker */ - XColor bd; - double scale = 0.8; - bd.pixel = color; - XQueryColor (st->dpy, st->xgwa.colormap, &bd); - bd.red *= scale; - bd.green *= scale; - bd.blue *= scale; - - /* bd.red = 0xFFFF; bd.green = 0; bd.blue = 0; */ - - XAllocColor (st->dpy, st->xgwa.colormap, &bd); - XSetForeground (st->dpy, st->pgc, bd.pixel); - XDrawLines (st->dpy, st->output, st->pgc, - xp, countof(xp), CoordModeOrigin); - XFreeColors (st->dpy, st->xgwa.colormap, &bd.pixel, 1, 0); - } - } -#endif /* !DO_VORONOI */ + case DELAUNAY: + for (i = 0; i < ntri; i++) + { + XPoint xp[3]; + unsigned long color; + xp[0].x = p[v[i].p1].x * wscale; xp[0].y = p[v[i].p1].y * wscale; + xp[1].x = p[v[i].p2].x * wscale; xp[1].y = p[v[i].p2].y * wscale; + xp[2].x = p[v[i].p3].x * wscale; xp[2].y = p[v[i].p3].y * wscale; + + /* Set the color of this triangle to the pixel at its midpoint. */ + color = XGetPixel (st->img, + (xp[0].x + xp[1].x + xp[2].x) / (3 * wscale), + (xp[0].y + xp[1].y + xp[2].y) / (3 * wscale)); + + XSetForeground (st->dpy, st->pgc, color); + XFillPolygon (st->dpy, st->output, st->pgc, xp, countof(xp), + Convex, CoordModeOrigin); + + if (st->outline_p && !small_triangle_p(xp)) + { /* Border the triangle with a color that is darker */ + XColor bd; + double scale = 0.8; + bd.pixel = color; + XQueryColor (st->dpy, st->xgwa.colormap, &bd); + bd.red *= scale; + bd.green *= scale; + bd.blue *= scale; + + /* bd.red = 0xFFFF; bd.green = 0; bd.blue = 0; */ + + XAllocColor (st->dpy, st->xgwa.colormap, &bd); + XSetForeground (st->dpy, st->pgc, bd.pixel); + XDrawLines (st->dpy, st->output, st->pgc, + xp, countof(xp), CoordModeOrigin); + XFreeColors (st->dpy, st->xgwa.colormap, &bd.pixel, 1, 0); + } + } + break; + default: + abort(); + } free (p); free (v); @@ -935,6 +978,7 @@ static const char *tessellimage_defaults [] = { ".lowrez: True", "*dontClearRoot: True", "*fpsSolid: true", + "*mode: random", "*delay: 30000", "*duration: 120", "*duration2: 0.4", @@ -956,6 +1000,7 @@ static XrmOptionDescRec tessellimage_options [] = { { "-duration2", ".duration2", XrmoptionSepArg, 0 }, { "-max-depth", ".maxDepth", XrmoptionSepArg, 0 }, { "-max-resolution", ".maxResolution", XrmoptionSepArg, 0 }, + { "-mode", ".mode", XrmoptionSepArg, 0 }, { "-outline", ".outline", XrmoptionNoArg, "True" }, { "-no-outline", ".outline", XrmoptionNoArg, "False" }, { "-fill-screen", ".fillScreen", XrmoptionNoArg, "True" },