1 /* celtic, Copyright (c) 2006 Max Froumentin <max@lapin-bleu.net>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
11 * A celtic pattern programme inspired by "Les Entrelacs Celtes", by
12 * Christian Mercat, Dossier Pour La Science, no. 47, april/june 2005.
13 * See <http://www.entrelacs.net/>
17 #include "screenhack.h"
20 #define SQRT_3 1.73205080756887729352
22 #define assert(EXP) do { if (!((EXP))) abort(); } while(0)
24 /*-----------------------------------------*/
27 unsigned long curve_width, shadow_width;
28 double shape1, shape2;
31 enum graph_type { polar, tgrid, kennicott, triangle } type;
32 unsigned long edge_size;
33 unsigned long cluster_size; /* only used if type is kennicott */
34 unsigned long delay; /* controls curve drawing speed (step delay
36 unsigned long nsteps; /* only if triangle: number of subdivisions along the side */
37 unsigned long nb_orbits; /* only used if type is polar */
38 unsigned long nb_nodes_per_orbit; /* only used if type is polar */
40 double angle; /* angle of rotation of the graph around the centre */
43 /*-----------------------------------------*/
44 typedef enum direction {
45 CLOCKWISE=0, ANTICLOCKWISE=1
49 /*-----------------------------------------*/
50 typedef struct array {
52 int nb_allocated_elements;
57 typedef struct graph {
62 typedef struct edge_couple {
67 typedef struct pattern {
68 double shape1, shape2;
82 GC gc,shadow_gc,gc_graph;
87 XWindowAttributes xgwa;
89 int reset, force_reset;
98 static Array array_new(int increment)
101 assert(new=(Array)calloc(1,sizeof(struct array)));
103 new->nb_allocated_elements=0;
104 new->increment=increment;
108 static void array_del(Array a, void (*free_element)(void*))
112 for (i=0;i<a->nb_elements;i++)
113 free_element(a->elements[i]);
118 static void array_add_element(Array a, void *element)
120 if (a->nb_elements == a->nb_allocated_elements) {
121 /* we must allocate more */
122 a->nb_allocated_elements+=a->increment;
123 a->elements=realloc(a->elements,a->nb_allocated_elements*sizeof(void *));
125 a->elements[a->nb_elements++]=element;
127 /*-----------------------------------------*/
129 typedef struct node {
134 typedef struct edge {
136 double angle1, angle2;
139 /*-----------------------------------------*/
142 static Node node_new(double x, double y)
145 assert(new = (Node)calloc(1,sizeof(struct node)));
148 new->edges = array_new(10);
152 static void node_del(void *n)
153 { /* not Node * because the function is passed to array_del */
154 array_del(((Node)n)->edges,NULL);
159 static void node_to_s(Node n, FILE *f)
161 fprintf(f,"Node: %g %g\n",n->x,n->y);
165 static void node_draw(struct state *st, Node n)
167 XDrawArc(st->dpy,st->window,st->gc_graph,(int)rint(n->x)-5,(int)rint(n->y)-5,10,10,0,360*64);
170 static void node_add_edge(Node n, Edge e)
172 array_add_element(n->edges,e);
176 /*-----------------------------------------*/
179 static Edge edge_new(Node n1, Node n2)
182 assert(new = (Edge)calloc(1,sizeof(struct edge)));
185 new->angle1=atan2(new->node2->y - new->node1->y, new->node2->x - new->node1->x);
186 if (new->angle1 < 0) new->angle1+=6.28;
188 new->angle2=atan2(new->node1->y - new->node2->y, new->node1->x - new->node2->x);
189 if (new->angle2 < 0) new->angle2+=6.28;
193 static void edge_del(void *e) /* not Edge * because the function is passed to array_del */
199 static void edge_to_s(Edge e, FILE *f)
201 fprintf(f,"Edge: (%g, %g), (%g, %g) angles: %g, %g\n",
202 e->node1->x, e->node1->y, e->node2->x, e->node2->y,
203 e->angle1, e->angle2);
207 static void edge_draw(struct state *st, Edge e)
209 XDrawLine(st->dpy,st->window,st->gc_graph, e->node1->x, e->node1->y, e->node2->x, e->node2->y);
212 static double edge_angle(Edge e, Node n)
214 /* returns the angle of the edge at Node n */
215 assert(n==e->node1 || n==e->node2);
216 if (n==e->node1) return e->angle1; else return e->angle2;
219 static Node edge_other_node(Edge e, Node n)
221 assert(n==e->node1 || n==e->node2);
222 if (n==e->node1) return e->node2; else return e->node1;
225 static double edge_angle_to(Edge e, Edge e2, Node node, Direction direction)
227 /* returns the absolute angle from this edge to "edge2" around
228 "node" following "direction" */
231 if (direction==CLOCKWISE)
232 a=edge_angle(e,node) - edge_angle(e2,node);
234 a=edge_angle(e2,node) - edge_angle(e,node);
236 if (a<0) return a+2*M_PI; else return a;
239 /*-----------------------------------------*/
241 static Graph graph_new(struct state *st)
244 assert(new = (Graph)calloc(1,sizeof(struct graph)));
245 new->nodes = array_new(100);
246 new->edges = array_new(100);
250 static void graph_del(Graph g)
252 array_del(g->nodes, &node_del);
253 array_del(g->edges, &edge_del);
258 static void graph_add_node(Graph g, Node n)
260 array_add_element(g->nodes, n);
263 static void graph_add_edge(Graph g, Edge e)
265 array_add_element(g->edges, e);
267 /* for each node n of e, add n to pointer e */
268 node_add_edge(e->node1, e);
269 node_add_edge(e->node2, e);
272 static Edge graph_next_edge_around(Graph g, Node n, Edge e, Direction direction)
274 /* return the next edge after e around node n clockwise */
275 double angle, minangle=20;
276 Edge next_edge = e, edge;
279 for (i=0;i<n->edges->nb_elements;i++) {
280 edge=n->edges->elements[i];
282 angle = edge_angle_to(e,edge,n,direction);
283 if (angle < minangle) {
293 static void graph_to_s(Graph g, FILE *f)
296 for (i=0;i<g->nodes->nb_elements;i++)
297 node_to_s(g->nodes->elements[i],f);
298 for (i=0;i<g->edges->nb_elements;i++)
299 edge_to_s(g->edges->elements[i],f);
303 static void graph_draw(struct state *st, Graph g)
307 for (i=0;i<g->nodes->nb_elements;i++)
308 node_draw(st, g->nodes->elements[i]);
309 for (i=0;i<g->edges->nb_elements;i++)
310 edge_draw(st, g->edges->elements[i]);
313 static void graph_rotate(Graph g, double angle, int cx, int cy)
315 /* rotate all the nodes of the graph around the centre */
317 float c=cos(angle),s=sin(angle),x,y;
319 for (i=0;i<g->nodes->nb_elements;i++) {
320 n=g->nodes->elements[i];
322 n->x = (x-cx)*c-(y-cy)*s + cx;
323 n->y = (x-cx)*s+(y-cy)*c + cy;
328 /*---------------------------*/
330 static Graph make_polar_graph(struct state *st,
331 int xmin, int ymin, int width, int height,
332 int nbp, /* number of points on each orbit */
333 int nbo /* number of orbits */)
334 /* make a simple grid graph, with edges present or absent randomly */
336 int cx = width/2+xmin, cy=height/2+ymin; /* centre */
337 int os = (width<height?width:height)/(2*nbo); /* orbit height */
343 assert(grid=(Node*)calloc(1+nbp*nbo,sizeof(Node)));
344 assert(g=graph_new(st));
346 graph_add_node(g, grid[0]=node_new((double)cx,(double)cy));
351 grid[1+o*nbp+p]=node_new(cx+(o+1)*os*sin(p*2*M_PI/nbp),
352 cy+(o+1)*os*cos(p*2*M_PI/nbp)));
357 for (p=0;p<nbp;p++) {
358 if (o==0) /* link first orbit nodes with centre */
359 graph_add_edge(g,edge_new(grid[1+o*nbp+p],grid[0]));
360 else /* liink orbit nodes with lower orbit */
361 graph_add_edge(g,edge_new(grid[1+o*nbp+p],grid[1+(o-1)*nbp+p]));
362 /* link along orbit */
363 graph_add_edge(g,edge_new(grid[1+o*nbp+p],
364 grid[1+o*nbp+(p+1)%nbp]));
372 static Graph make_grid_graph(struct state *st,
373 int xmin, int ymin, int width, int height, int step)
374 /* make a simple grid graph */
378 int size=(width<height?height:width);
380 /* empirically, it seems there are 2 curves only if both
381 nbcol and nbrow are even, so we round them to even */
382 int nbcol=(2+size/step)/2*2, nbrow=(2+size/step)/2*2;
385 assert(grid=(Node*)calloc(nbrow*nbcol,sizeof(Node)));
386 assert(g=graph_new(st));
389 /* adjust xmin and xmax so that the grid is centered */
390 xmin+=(width-(nbcol-1)*step)/2;
391 ymin+=(height-(nbrow-1)*step)/2;
393 /* create node grid */
394 for (row=0;row<nbrow;row++)
395 for (col=0;col<nbcol;col++) {
398 grid[row+col*nbrow]=node_new((double)x, (double)y);
399 graph_add_node(g, grid[row+col*nbrow]);
403 for (row=0;row<nbrow;row++)
404 for (col=0;col<nbcol;col++) {
406 graph_add_edge(g,edge_new(grid[row+col*nbrow],
407 grid[row+(col+1)*nbrow]));
409 graph_add_edge(g,edge_new(grid[row+col*nbrow],grid[row+1+col*nbrow]));
410 if (col!=nbcol-1 && row!=nbrow-1) {
411 graph_add_edge(g,edge_new(grid[row+col*nbrow],
412 grid[row+1+(col+1)*nbrow]));
413 graph_add_edge(g,edge_new(grid[row+1+col*nbrow],
414 grid[row+(col+1)*nbrow]));
424 static Graph make_triangle_graph(struct state *st,
425 int xmin, int ymin, int width, int height, int edge_size)
430 double L=(width<height?width:height)/2.0; /* circumradius of the triangle */
431 double cx=xmin+width/2.0, cy=ymin+height/2.0; /* centre of the triangle */
432 double p2x=cx-L*SQRT_3/2.0, p2y=cy+L/2.0; /* p2 is the bottom left vertex */
434 int nsteps=3*L/(SQRT_3*edge_size);
436 assert(grid=(Node*)calloc((nsteps+1)*(nsteps+1),sizeof(Node)));
437 assert(g=graph_new(st));
439 /* create node grid */
440 for (row=0;row<=nsteps;row++)
441 for (col=0;col<=nsteps;col++)
442 if (row+col<=nsteps) {
443 x=p2x+col*L*SQRT_3/nsteps + row*L*SQRT_3/(2*nsteps);
444 y=p2y-row*3*L/(2*nsteps);
445 grid[col+row*(nsteps+1)]=node_new((double)x, (double)y);
446 graph_add_node(g, grid[col+row*(nsteps+1)]);
450 for (row=0;row<nsteps;row++)
451 for (col=0;col<nsteps;col++)
452 if (row+col<nsteps) {
453 /* horizontal edges */
454 graph_add_edge(g,edge_new(grid[row+col*(nsteps+1)],grid[row+(col+1)*(nsteps+1)]));
456 graph_add_edge(g,edge_new(grid[row+col*(nsteps+1)],grid[row+1+col*(nsteps+1)]));
458 graph_add_edge(g,edge_new(grid[row+1+col*(nsteps+1)],grid[row+(col+1)*(nsteps+1)]));
467 static Graph make_kennicott_graph(struct state *st,
468 int xmin, int ymin, int width, int height, int step,
470 /* make a graph inspired by one of the motifs from the Kennicott bible */
471 /* square grid of clusters of the shape /|\
474 * cluster_size is the length of an edge of a cluster
479 int size=width<height?height:width;
480 int nbcol=(1+size/step)/2*2, nbrow=(1+size/step)/2*2;
483 /* there are 5 nodes by for each cluster */
484 assert(grid=(Node*)calloc(5*nbrow*nbcol,sizeof(Node)));
485 assert(g=graph_new(st));
487 /* adjust xmin and xmax so that the grid is centered */
488 xmin+=(width-(nbcol-1)*step)/2;
489 ymin+=(height-(nbrow-1)*step)/2;
491 /* create node grid */
492 for (row=0;row<nbrow;row++)
493 for (col=0;col<nbcol;col++) {
494 int ci=5*(row+col*nbrow);
498 /* create a cluster centred on x,y */
499 grid[ci ]=node_new((double)x, (double)y);
500 grid[ci+1]=node_new((double)(x+cluster_size), (double)y);
501 grid[ci+2]=node_new((double)x, (double)(y-cluster_size));
502 grid[ci+3]=node_new((double)(x-cluster_size), (double)y);
503 grid[ci+4]=node_new((double)x, (double)(y+cluster_size));
505 graph_add_node(g, grid[ci]);
506 graph_add_node(g, grid[ci+1]);
507 graph_add_node(g, grid[ci+2]);
508 graph_add_node(g, grid[ci+3]);
509 graph_add_node(g, grid[ci+4]);
512 graph_add_edge(g,edge_new(grid[ci], grid[ci+1]));
513 graph_add_edge(g,edge_new(grid[ci], grid[ci+2]));
514 graph_add_edge(g,edge_new(grid[ci], grid[ci+3]));
515 graph_add_edge(g,edge_new(grid[ci], grid[ci+4]));
516 graph_add_edge(g,edge_new(grid[ci+1], grid[ci+2]));
517 graph_add_edge(g,edge_new(grid[ci+2], grid[ci+3]));
518 graph_add_edge(g,edge_new(grid[ci+3], grid[ci+4]));
519 graph_add_edge(g,edge_new(grid[ci+4], grid[ci+1]));
523 /* create inter-cluster edges */
524 for (row=0;row<nbrow;row++)
525 for (col=0;col<nbcol;col++) {
527 /* horizontal edge from edge 1 of cluster (row, col) to edge 3
528 * of cluster (row,col+1) */
529 graph_add_edge(g,edge_new(grid[5*(row+col*nbrow)+1],grid[5*(row+(col+1)*nbrow)+3]));
531 /* vertical edge from edge 4 of cluster (row, col) to edge 2
532 * of cluster (row+1,col) */
533 graph_add_edge(g,edge_new(grid[5*(row+col*nbrow)+4],
534 grid[5*(row+1+col*nbrow)+2]));
540 /*---------------------------*/
541 typedef struct spline_segment {
542 double x1,y1,x2,y2,x3,y3,x4,y4;
545 typedef struct spline {
546 Array segments; /* array of SplineSegment */
550 static Spline spline_new(int color)
552 Spline new=(Spline)calloc(1,sizeof(struct spline));
553 new->segments=array_new(30);
558 static void spline_del(void *s)
560 array_del(((Spline)s)->segments,&free);
564 static void spline_add_segment(Spline s,
565 double x1, double y1, double x2, double y2,
566 double x3, double y3, double x4, double y4)
568 SplineSegment ss=(SplineSegment)calloc(1,sizeof(struct spline_segment));
569 ss->x1=x1; ss->x2=x2; ss->x3=x3; ss->x4=x4;
570 ss->y1=y1; ss->y2=y2; ss->y3=y3; ss->y4=y4;
571 array_add_element(s->segments,ss);
575 static void spline_to_s(Spline s, FILE *f)
579 fprintf(f,"Spline: \n");
580 for (i=0;i<s->segments->nb_elements;i++) {
581 ss=s->segments->elements[i];
582 fprintf(f," - segment %d: (%g, %g),(%g, %g),(%g, %g),(%g, %g)\n",
583 i,ss->x1,ss->y1,ss->x2,ss->y2,ss->x3,ss->y3,ss->x4,ss->y4);
588 static void spline_value_at(Spline s, double *x, double *y, double t, int *segment)
593 si = floor(t*s->segments->nb_elements);
594 tt = t*s->segments->nb_elements - si;
595 assert(tt>=0 && tt<1);
596 ss=s->segments->elements[si];
598 *x = ss->x1*(1-tt)*(1-tt)*(1-tt)+3*ss->x2*tt*(1-tt)*(1-tt)+3*ss->x3*tt*tt*(1-tt)+ss->x4*tt*tt*tt;
599 *y = ss->y1*(1-tt)*(1-tt)*(1-tt)+3*ss->y2*tt*(1-tt)*(1-tt)+3*ss->y3*tt*tt*(1-tt)+ss->y4*tt*tt*tt;
604 /*---------------------------*/
606 static EdgeCouple edge_couple_new(int nb_edges) {
608 EdgeCouple new = (EdgeCouple)calloc(1,sizeof(struct edge_couple));
609 new->array = (int **)calloc(nb_edges, sizeof(int*));
610 new->size = nb_edges;
612 for (i=0;i<nb_edges;i++) {
613 new->array[i]=(int *)calloc(2,sizeof(int));
614 new->array[i][CLOCKWISE]=0;
615 new->array[i][ANTICLOCKWISE]=0;
620 static void edge_couple_del(EdgeCouple e)
623 for (i=0;i<e->size;i++) free(e->array[i]);
628 /*---------------------------*/
630 static Pattern pattern_new(struct state *st, Graph g, double shape1, double shape2)
633 assert(new=(Pattern)calloc(1,sizeof(struct pattern)));
637 new->ec=edge_couple_new(g->edges->nb_elements);
638 new->splines=array_new(10);
639 new->ncolors=st->ncolors;
643 static void pattern_del(Pattern p)
645 edge_couple_del(p->ec);
646 array_del(p->splines,&spline_del);
650 static void pattern_edge_couple_set(Pattern p, Edge e, Direction d, int value)
653 for (i=0;i<p->graph->edges->nb_elements;i++)
654 if (p->graph->edges->elements[i]==e) {
655 p->ec->array[i][d]=value;
660 static void pattern_draw_spline_direction(Pattern p, Spline s,
661 Node node, Edge edge1, Edge edge2,
664 double x1=(edge1->node1->x+edge1->node2->x)/2.0;
665 double y1=(edge1->node1->y+edge1->node2->y)/2.0;
667 /* P2 (x2,y2) is the middle point of edge1 */
668 double x4=(edge2->node1->x+edge2->node2->x)/2.0;
669 double y4=(edge2->node1->y+edge2->node2->y)/2.0;
671 double alpha=edge_angle_to(edge1,edge2,node,direction)*p->shape1;
672 double beta=p->shape2;
674 double i1x,i1y,i2x,i2y,x2,y2,x3,y3;
676 if (direction == ANTICLOCKWISE) {
677 /* I1 must stick out to the left of NP1 and I2 to the right of NP4 */
678 i1x = alpha*(node->y-y1)+x1;
679 i1y = -alpha*(node->x-x1)+y1;
680 i2x = -alpha*(node->y-y4)+x4;
681 i2y = alpha*(node->x-x4)+y4;
682 x2 = beta*(y1-i1y) + i1x;
683 y2 = -beta*(x1-i1x) + i1y;
684 x3 = -beta*(y4-i2y) + i2x;
685 y3 = beta*(x4-i2x) + i2y;
688 /* I1 must stick out to the left of NP1 and I2 to the right of NP4 */
689 i1x = -alpha*(node->y-y1)+x1;
690 i1y = alpha*(node->x-x1)+y1;
691 i2x = alpha*(node->y-y4)+x4;
692 i2y = -alpha*(node->x-x4)+y4;
693 x2 = -beta*(y1-i1y) + i1x;
694 y2 = beta*(x1-i1x) + i1y;
695 x3 = beta*(y4-i2y) + i2x;
696 y3 = -beta*(x4-i2x) + i2y;
699 spline_add_segment(s,x1,y1,x2,y2,x3,y3,x4,y4);
702 static int pattern_next_unfilled_couple(Pattern p, Edge *e, Direction *d)
705 for (i=0;i<p->ec->size;i++) {
706 if (p->ec->array[i][CLOCKWISE]==0) {
707 *e=p->graph->edges->elements[i];
711 else if (p->ec->array[i][ANTICLOCKWISE]==0) {
712 *e=p->graph->edges->elements[i];
720 static void pattern_make_curves(Pattern p)
722 Edge current_edge, first_edge, next_edge;
723 Node current_node, first_node;
724 Direction current_direction, first_direction;
727 while (pattern_next_unfilled_couple(p, &first_edge, &first_direction)) {
728 /* start a new loop */
729 s=spline_new(random()%(p->ncolors-2)+2);
730 array_add_element(p->splines, s);
732 current_edge=first_edge;
733 current_node=first_node=current_edge->node1;
734 current_direction=first_direction;
737 pattern_edge_couple_set(p, current_edge, current_direction, 1);
738 next_edge = graph_next_edge_around(p->graph,current_node,current_edge,current_direction);
740 /* add the spline segment to the spline */
741 pattern_draw_spline_direction(p,s,current_node,
742 current_edge,next_edge,current_direction);
745 current_edge = next_edge;
746 current_node = edge_other_node(next_edge, current_node);
747 current_direction=1-current_direction;
749 } while (current_node!=first_node || current_edge!=first_edge || current_direction!=first_direction);
751 if (s->segments->nb_elements==2) /* spline is just one point: remove it */
752 p->splines->elements[p->splines->nb_elements-1]=NULL;
757 static void pattern_animate(struct state *st)
759 Pattern p = st->pattern;
762 double x,y,x2,y2,x3,y3,x4,y4;
763 int i,segment,unused;
765 double step=0.0001; /* TODO: set the step (or the delay) as a
766 * function of the spline length, so that
767 * drawing speed is constant
771 XSetLineAttributes(st->dpy,st->gc,st->params.curve_width,LineSolid,CapRound,JoinRound);
772 XSetLineAttributes(st->dpy,st->shadow_gc,st->params.shadow_width,LineSolid,CapRound,JoinRound);
774 for (ticks=0;ticks<100 && t<1;ticks++) {
775 for (i=0;i<p->splines->nb_elements;i++)
776 if ((s=p->splines->elements[i])) { /* skip if one-point spline */
777 spline_value_at(s, &x, &y, fmod(t,1.0),&segment);
778 spline_value_at(s, &x2, &y2, fmod(t+step,1.0),&unused);
780 /* look ahead for the shadow segment */
783 spline_value_at(s, &x3, &y3, fmod(t2,1.0),&unused);
784 while (t2+step<1.0 && (x3-x2)*(x3-x2)+(y3-y2)*(y3-y2) < st->params.shadow_width*st->params.shadow_width) {
786 spline_value_at(s, &x3, &y3, fmod(t2,1.0),&unused);
789 spline_value_at(s, &x4, &y4, fmod(t2+step,1.0),&unused);
791 /* draw shadow line */
792 XDrawLine(st->dpy,st->window,st->shadow_gc,
793 (int)rint(x3),(int)rint(y3),
794 (int)rint(x4),(int)rint(y4));
796 /* draw line segment */
797 if (p->splines->nb_elements==1)
798 XSetForeground(st->dpy, st->gc, st->colors[segment%(p->ncolors-3)+2].pixel);
800 XSetForeground(st->dpy, st->gc, st->colors[s->color].pixel);
801 XDrawLine(st->dpy,st->window,st->gc,
802 (int)rint(x),(int)rint(y),
803 (int)rint(x2),(int)rint(y2));
812 /* at the end we redraw back to remove shadow spillage */
813 for (i=0;i<p->splines->nb_elements;i++) {
814 if ((s=p->splines->elements[i])) {
816 XSetForeground(st->dpy, st->gc, st->colors[s->color].pixel);
817 spline_value_at(s, &x, &y, fmod(t,1.0),&unused);
819 spline_value_at(s, &x2, &y2, fmod(t-offset,1.0),&unused);
821 while ((x2-x)*(x2-x)+(y2-y)*(y2-y) < st->params.shadow_width*st->params.shadow_width) {
823 spline_value_at(s, &x2, &y2, fmod(t-offset,1.0),&unused);
826 XDrawLine(st->dpy,st->window,st->gc, (int)rint(x),(int)rint(y), (int)rint(x2),(int)rint(y2));
832 /*======================================================================*/
834 static const char *celtic_defaults[] = {
835 ".background: black",
836 ".foreground: #333333",
843 "*ignoreRotation: True",
848 static XrmOptionDescRec celtic_options[] = {
849 {"-background", ".background", XrmoptionSepArg, 0},
850 {"-foreground", ".foreground", XrmoptionSepArg, 0},
851 {"-ncolors", ".ncolors", XrmoptionSepArg, 0},
852 {"-delay", ".delay", XrmoptionSepArg, 0},
853 {"-delay2", ".delay2", XrmoptionSepArg, 0},
854 {"-graph", ".showGraph", XrmoptionNoArg, "True"},
859 static void params_to_s(FILE *f)
861 switch (st->params.type) {
862 case polar: fprintf(f,"type: polar\n");
863 fprintf(f,"nb_orbits: %ld\n",st->params.nb_orbits);
864 fprintf(f,"nb_nodes_per_orbit: %ld\n",st->params.nb_nodes_per_orbit);
866 case tgrid: fprintf(f,"type: grid\n");
867 fprintf(f,"edge_size: %ld\n",st->params.edge_size);
869 case triangle: fprintf(f,"type: triangle\n");
870 fprintf(f,"edge_size: %ld\n",st->params.edge_size);
873 fprintf(f,"type: kennicott\n");
874 fprintf(f,"edge_size: %ld\n",st->params.edge_size);
875 fprintf(f,"cluster_size: %ld\n",st->params.cluster_size);
879 fprintf(f,"curve width: %ld\n",st->params.curve_width);
880 fprintf(f,"shadow width: %ld\n",st->params.shadow_width);
881 fprintf(f,"shape1: %g\n",st->params.shape1);
882 fprintf(f,"shape2: %g\n",st->params.shape2);
883 fprintf(f,"margin: %ld\n",st->params.margin);
884 fprintf(f,"angle: %g\n",st->params.angle);
885 fprintf(f,"delay: %ld\n",st->params.delay);
890 static void colormap_to_s(int ncolors, XColor *colors)
893 printf("-- colormap (%d colors):\n",st->ncolors);
894 for (i=0;i<st->ncolors;i++)
895 printf("%d: %d %d %d\n", i, st->colors[i].red, st->colors[i].green, st->colors[i].blue);
902 celtic_init (Display *d_arg, Window w_arg)
904 struct state *st = (struct state *) calloc (1, sizeof(*st));
907 st->dpy=d_arg; st->window=w_arg;
908 st->showGraph=get_boolean_resource (st->dpy, "showGraph", "Boolean");
910 st->ncolors = get_integer_resource (st->dpy, "ncolors", "Integer");
913 XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
914 assert(st->colors = (XColor *) calloc (st->ncolors,sizeof(XColor)));
916 if (get_boolean_resource(st->dpy, "mono", "Boolean"))
920 st->colors[0].pixel = get_pixel_resource(st->dpy, st->xgwa.colormap,
921 "foreground", "Foreground");
926 make_random_colormap (st->xgwa.screen, st->xgwa.visual, st->xgwa.colormap,
927 st->colors, &st->ncolors, True, True, 0, True);
929 make_smooth_colormap (st->xgwa.screen, st->xgwa.visual, st->xgwa.colormap,
930 st->colors, &st->ncolors, True, 0, True);
935 st->colors[0].pixel = get_pixel_resource(st->dpy, st->xgwa.colormap,
936 "foreground", "Foreground");
937 st->colors[1].pixel = get_pixel_resource(st->dpy, st->xgwa.colormap,
938 "background", "Background");
943 /* graphic context for curves */
944 gcv.foreground = st->colors[0].pixel;
945 gcv.background = st->colors[1].pixel;
946 gcv.line_width = st->params.curve_width;
947 gcv.cap_style=CapRound;
948 st->gc = XCreateGC (st->dpy, st->window, GCForeground|GCBackground|GCLineWidth|GCCapStyle, &gcv);
950 /* graphic context for graphs */
951 gcv.foreground = st->colors[0].pixel;
952 gcv.background = st->colors[1].pixel;
953 st->gc_graph = XCreateGC (st->dpy, st->window, GCForeground|GCBackground, &gcv);
955 /* graphic context for shadows */
956 gcv.foreground = st->colors[1].pixel;
957 gcv.line_width = st->params.shadow_width;
958 gcv.cap_style=CapRound;
959 st->shadow_gc = XCreateGC(st->dpy, st->window, GCForeground|GCLineWidth|GCCapStyle, &gcv);
961 st->delay2 = 1000000 * get_integer_resource(st->dpy, "delay2", "Delay2");
967 celtic_draw (Display *dpy, Window window, void *closure)
969 struct state *st = (struct state *) closure;
972 st->eraser = erase_window (st->dpy, st->window, st->eraser);
976 if (st->reset || st->force_reset) {
977 int delay = (st->force_reset ? 0 : st->delay2);
982 if (st->pattern != NULL) {
983 pattern_del(st->pattern);
986 graph_del(st->graph);
988 /* recolor each time */
989 st->ncolors = get_integer_resource (st->dpy, "ncolors", "Integer");
991 make_smooth_colormap (st->xgwa.screen, st->xgwa.visual, st->xgwa.colormap,
992 st->colors, &st->ncolors, True, 0, True);
994 st->eraser = erase_window (st->dpy, st->window, st->eraser);
998 if (st->pattern == NULL) {
999 st->params.curve_width=random()%5+4;
1000 st->params.shadow_width=st->params.curve_width+4;
1001 st->params.shape1=(15+random()%15)/10.0 -1.0;
1002 st->params.shape2=(15+random()%15)/10.0 -1.0;
1003 st->params.edge_size=10*(random()%5)+20;
1004 st->params.delay=get_integer_resource(st->dpy, "delay", "Delay");
1005 st->params.angle=random()%360*2*M_PI/360;
1006 st->params.margin=(random()%8)*100-600;
1008 switch (random()%4) {
1010 st->params.type=tgrid;
1011 st->params.shape1=(random()%1*2-1.0)*(random()%10+3)/10.0;
1012 st->params.shape2=(random()%1*2-1.0)*(random()%10+3)/10.0;
1013 st->params.edge_size=10*(random()%5)+50;
1016 st->params.type=kennicott;
1017 st->params.shape1=(random()%20)/10.0 -1.0;
1018 st->params.shape2=(random()%20)/10.0 -1.0;
1019 st->params.edge_size=10*(random()%3)+70;
1020 st->params.cluster_size=st->params.edge_size/(3.0+random()%10)-1;
1023 st->params.type=triangle;
1024 st->params.edge_size=10*(random()%5)+60;
1025 st->params.margin=(random()%10)*100-900;
1028 st->params.type=polar;
1029 st->params.nb_orbits=2+random()%10;
1030 st->params.nb_nodes_per_orbit=4+random()%10;
1035 /* st->params.type= polar; */
1036 /* st->params.nb_orbits= 5; */
1037 /* st->params.nb_nodes_per_orbit= 19; */
1038 /* st->params.curve_width= 4; */
1039 /* st->params.shadow_width= 8; */
1040 /* st->params.shape1= 0.5; */
1041 /* st->params.shape2= 1.3; */
1042 /* st->params.margin= 30; */
1043 /* st->params.angle= 5.21853; */
1044 /* st->params.delay= 10000; */
1047 /* params_to_s(stdout); */
1049 /*=======================================================*/
1052 switch (st->params.type) {
1054 st->graph=make_grid_graph(st, st->params.margin,st->params.margin,
1055 st->xgwa.width-2*st->params.margin,
1056 st->xgwa.height-2*st->params.margin,
1057 st->params.edge_size);
1060 st->graph=make_kennicott_graph(st, st->params.margin,st->params.margin,
1061 st->xgwa.width-2*st->params.margin,
1062 st->xgwa.height-2*st->params.margin,
1063 st->params.edge_size,
1064 st->params.cluster_size);
1067 st->graph=make_triangle_graph(st, st->params.margin,st->params.margin,
1068 st->xgwa.width-2*st->params.margin,
1069 st->xgwa.height-2*st->params.margin,
1070 st->params.edge_size);
1073 st->graph=make_polar_graph(st, st->params.margin,st->params.margin,
1074 st->xgwa.width-2*st->params.margin,
1075 st->xgwa.height-2*st->params.margin,
1076 st->params.nb_nodes_per_orbit,
1077 st->params.nb_orbits);
1080 st->graph=make_grid_graph(st, st->params.margin,st->params.margin,
1081 st->xgwa.width-2*st->params.margin,
1082 st->xgwa.height-2*st->params.margin,
1083 st->params.edge_size);
1087 graph_rotate(st->graph,st->params.angle,st->xgwa.width/2,st->xgwa.height/2);
1090 graph_draw(st, st->graph);
1092 st->pattern=pattern_new(st, st->graph, st->params.shape1, st->params.shape2);
1093 pattern_make_curves(st->pattern);
1097 pattern_animate(st);
1099 return st->params.delay;
1104 celtic_reshape (Display *dpy, Window window, void *closure,
1105 unsigned int w, unsigned int h)
1107 struct state *st = (struct state *) closure;
1108 XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
1112 celtic_event (Display *dpy, Window window, void *closure, XEvent *event)
1114 struct state *st = (struct state *) closure;
1115 if (screenhack_event_helper (dpy, window, event))
1117 st->force_reset = 1;
1124 celtic_free (Display *dpy, Window window, void *closure)
1126 struct state *st = (struct state *) closure;
1131 XSCREENSAVER_MODULE ("Celtic", celtic)