Swap code/variable declaration to be pre-C99 compliant.
[xiph/unicode.git] / planarity / graph_generate_mesh2.c
blob264aeaa36424165a2e5d2b3a68519efa3f119e54
1 /*
3 * gPlanarity:
4 * The geeky little puzzle game with a big noodly crunch!
5 *
6 * gPlanarity copyright (C) 2005 Monty <monty@xiph.org>
7 * Original Flash game by John Tantalo <john.tantalo@case.edu>
8 * Original game concept by Mary Radcliffe
10 * gPlanarity is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2, or (at your option)
13 * any later version.
15 * gPlanarity is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with Postfish; see the file COPYING. If not, write to the
22 * Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
27 #include <stdlib.h>
28 #include <string.h>
29 #include <math.h>
31 #include "graph.h"
32 #include "random.h"
33 #include "gameboard.h"
34 #include "graph_generate.h"
35 #include "graph_arrange.h"
36 #include "graph_region.h"
38 /* Mesh1 has three primary considerations in mind:
39 1) By default, act like the algorithm in the original planarity
40 2) Conform to a population contraint that is easy to work with/modify
41 3) Playability; short edges result in graphs that are easier to solve.
43 Mesh2 is intended to be a freeform populator with two different
44 uses; harder levels that disrupt the easy solution algorithms that
45 mesh1 allows, as well as being able to densely populate arbitrary
46 regions. */
48 typedef struct {
49 graph *g;
50 int width;
51 int height;
52 int active_current;
53 int active_max;
54 } mesh;
56 // check for intersections with other edges
57 static int check_intersects_edge(mesh *m, edge *e, int intersections){
58 edge *ge = m->g->edges;
59 int count=0;
61 while(ge){
62 double xo,yo;
64 // edges that aren't in this region don't exist (for
65 // now) by definition
66 if(ge->A->active == m->active_current || ge->B->active == m->active_current){
67 // edges that share a vertex don't intersect by definition
68 if(ge->A!=e->A && ge->A!=e->B && ge->B!=e->A && ge->B!=e->B)
69 if(intersects(ge->A->orig_x,ge->A->orig_y,
70 ge->B->orig_x,ge->B->orig_y,
71 e->A->orig_x,e->A->orig_y,
72 e->B->orig_x,e->B->orig_y,
73 &xo,&yo)){
74 count++;
75 if(count>intersections)return 1;
78 ge=ge->next;
80 return 0;
83 static float dot(vertex *A, vertex *B, vertex *C){
84 return (float)(B->orig_x-A->orig_x)*(float)(C->orig_x-B->orig_x) +
85 (float)(B->orig_y-A->orig_y)*(float)(C->orig_y-B->orig_y);
88 static float cross(vertex *A, vertex *B, vertex *C){
89 return (float)(B->orig_x-A->orig_x)*(float)(C->orig_y-A->orig_y) -
90 (float)(B->orig_y-A->orig_y)*(float)(C->orig_x-A->orig_x);
93 static float sq_point_distance(vertex *A, vertex *B){
94 float xd = A->orig_x-B->orig_x;
95 float yd = A->orig_y-B->orig_y;
96 return xd*xd+yd*yd;
99 static float sq_line_distance(edge *e, vertex *v){
101 if(dot(e->A,e->B,v) > 0)
102 return sq_point_distance(e->B,v);
103 if(dot(e->B,e->A,v) > 0)
104 return sq_point_distance(e->A,v);
107 float c = cross(e->A,e->B,v);
108 return c*c/sq_point_distance(e->A,e->B);
112 // Does this edge pass within ten pixels of another vertex
113 static int check_intersects_vertex(mesh *m, edge *e){
114 vertex *v = m->g->verticies;
116 while(v){
117 if(v->active == m->active_current)
118 if(v!=e->A && v!=e->B && sq_line_distance(e,v)<16)return 1;
119 v=v->next;
122 return 0;
125 static int select_available(mesh *m,vertex *current,float length_limit,int intersections){
126 int count=0;
127 vertex *v = m->g->verticies;
129 // mark all possible choices
130 while(v){
131 v->selected = 0;
132 if(v!=current && current->active == m->active_current){
133 if(length_limit==0 || sq_point_distance(v,current)<=length_limit){
134 if(!exists_edge(v,current)){
135 edge e;
136 e.A = v;
137 e.B = current;
138 if(!region_intersects(&e)){
139 if(!check_intersects_edge(m,&e,intersections)){
140 if(!check_intersects_vertex(m,&e)){
141 v->selected=1;
142 count++;
149 v=v->next;
152 return count;
155 // Although very inefficient, it is simple and correct. Even
156 // impossibly large boards generate in a fraction of a second on old
157 // boxen. There's likely no need to bother optimizing this step of
158 // board creation. */
160 typedef struct insort{
161 int metric;
162 vertex *v;
163 } insort;
165 static int insort_c(const void *a, const void *b){
166 insort *A=(insort *)a;
167 insort *B=(insort *)b;
168 return(A->metric-B->metric);
171 static vertex *vertex_num_sel(graph *g,int num){
172 vertex *v=g->verticies;
173 if(num<0)return 0;
174 while(v){
175 if(v->selected){
176 if(!num)
177 break;
178 else
179 num--;
181 v=v->next;
183 return v;
186 static void prepopulate(mesh *m,int length_limit){
187 // sort all verticies in ascending order by their number of potential edges
188 int i=0;
189 int num=0;
190 insort index[m->g->vertex_num];
191 vertex *v=m->g->verticies;
193 while(v){
194 if(v->active == m->active_current){
195 index[num].v=v;
196 index[num].metric = select_available(m,v,0,0);
197 num++;
199 v=v->next;
201 qsort(index,num,sizeof(*index),insort_c);
203 // populate in ascending order
204 for(i=0;i<num;i++){
205 int intersections=0;
206 int edges=0;
207 v = index[i].v;
209 // does this vertex already have edges?
211 edge_list *el=v->edges;
212 while(el){
213 edges++;
214 el=el->next;
215 if(edges>=2)break;
218 if(edges>=2)continue;
220 // it's possible some intersections will be necessary, but go for
221 // fewest possible
222 while(edges<2 && intersections<10){
223 int count = select_available(m,v,length_limit,intersections);
224 if(count){
225 vertex *short0=0;
226 vertex *short1=0;
227 vertex *w=m->g->verticies;
228 long d0;
229 long d1;
231 if(length_limit){
232 // choose two at random
233 int a=random_number()%count;
234 int b=-1;
236 if(count>1)
237 while(b==-1){
238 b=random_number()%count;
239 if(b==a)b=-1;
242 short0=vertex_num_sel(m->g,a);
243 short1=vertex_num_sel(m->g,b);
245 }else{
246 // used with region-constrined meshes
247 // of the possible edges, choose the shortest two
248 while(w){
249 if(w!=v && w->selected){
250 int xd=w->orig_x-v->orig_x;
251 int yd=w->orig_y-v->orig_y;
252 long d=xd*xd+yd*yd;
253 if(!short0){
254 short0=w;
255 d0=d;
256 }else if(!short1 || d<d1){
257 if(d<d0){
258 short1=short0;
259 d1=d0;
260 short0=w;
261 d0=d;
262 }else{
263 short1=w;
264 d1=d;
268 w=w->next;
272 if(short0){
273 add_edge(m->g,v,short0);
274 edges++;
275 m->g->objective +=intersections;
276 if(intersections)m->g->objective_lessthan=1;
278 if(edges<2 && short1){
279 add_edge(m->g,v,short1);
280 edges++;
281 m->g->objective +=intersections;
282 if(intersections)m->g->objective_lessthan=1;
285 intersections++;
290 // the spanning walk is to make an attempt at a single, connected graph.
291 static void span_depth_first2(mesh *m,vertex *current, float length_limit){
293 current->grabbed=1; // overloaded; "we walked this already"
294 while(1){
295 // prefer walking along edges that already exist
297 edge_list *el=current->edges;
299 while(el){
300 edge *e=el->edge;
301 vertex *v;
302 if(e->A==current)
303 v=e->B;
304 else
305 v=e->A;
307 if(!v->grabbed){
308 span_depth_first2(m, v, length_limit);
311 el=el->next;
315 // now walk any possible edges that have not been walked
317 int count=select_available(m,current,length_limit,0);
318 int count2=0;
319 int choice;
320 vertex *v = m->g->verticies;
322 // filter out already-walked edges
323 while(v){
324 if(v->grabbed && v->selected){ // grabbed is also overloaded to mean walked
325 v->selected = 0;
326 count--;
328 v=v->next;
331 if(count == 0) return;
333 choice = random_number()%count;
334 v = m->g->verticies;
335 while(v){
336 if(v->selected){
337 if(count2++ == choice){
338 add_edge(m->g,v,current);
339 span_depth_first2(m,v, length_limit);
340 break;
343 v=v->next;
346 if(count == 1) return; // because we just took care of it
351 static void random_populate(mesh *m,vertex *current,int dense_128, float length_limit){
352 if(current->active == m->active_current){
353 int count=select_available(m,current,length_limit,0);
354 if(count){
355 vertex *v = m->g->verticies;
356 while(v){
357 if(v->selected && random_yes(dense_128)){
358 add_edge(m->g,v,current);
359 v->selected=0;
361 v=v->next;
367 /* Initial generation setup */
369 static void mesh_setup(graph *g, mesh *m, int order, int divis){
370 int flag=0;
371 int wiggle=0;
372 int n;
373 m->g = g;
374 m->width=3;
375 m->height=2;
378 while(--order){
379 if(flag){
380 flag=0;
381 m->height+=1;
382 }else{
383 flag=1;
384 m->width+=2;
388 n=m->width*m->height;
390 // is this divisible by our requested divisor if any?
391 if(divis>0 && n%divis){
392 while(1){
393 wiggle++;
395 if(!((n+wiggle)%divis)) break;
397 if(n-wiggle>6 && !((n-wiggle)%divis)){
398 wiggle = -wiggle;
399 break;
403 // refactor the rectangular mesh's dimensions.
405 int h = (int)sqrt(n+wiggle),w;
407 while( (n+wiggle)%h )h--;
409 if(h==1){
410 // double it and be content with a working result
411 h=2;
412 w=(n+wiggle);
413 }else{
414 // good factoring
415 w = (n+wiggle)/h;
418 m->width=w;
419 m->height=h;
423 new_board(g, m->width * m->height);
424 region_init(); // clear it
426 // used for intersection calcs
428 int x,y;
429 vertex *v = g->verticies;
430 for(y=0;y<m->height;y++)
431 for(x=0;x<m->width;x++){
432 v->orig_x=x*50; // not a random number
433 v->orig_y=y*50; // not a random number
434 v=v->next;
438 g->objective = 0;
439 g->objective_lessthan = 0;
440 m->active_max=0;
443 static void generate_mesh2(mesh *m, int density_128, float length_limit){
444 vertex *v;
445 int i;
447 length_limit*=50;
448 length_limit*=length_limit;
450 for(i=0;i<=m->active_max;i++){
451 m->active_current=i;
453 if(have_region())
454 prepopulate(m,0);
456 /* connect the graph into as few discrete sections as possible */
457 v = m->g->verticies;
458 while(v){
459 v->grabbed = 0;
460 v=v->next;
463 v = m->g->verticies;
464 // make sure we walk all verticies
465 while(v){
466 if(v->active == m->active_current && !v->grabbed)
467 span_depth_first2(m, m->g->verticies, length_limit);
468 v=v->next;
471 if(!have_region())
472 prepopulate(m,length_limit);
474 /* now iterate the whole mesh adding random edges */
475 v=m->g->verticies;
476 while(v){
477 random_populate(m, v, density_128, length_limit);
478 v=v->next;
483 void generate_freeform(graph *g, int order){
484 mesh m;
485 random_seed(order+1);
486 mesh_setup(g, &m, order, 0);
488 generate_mesh2(&m,48,4);
490 randomize_verticies(g);
491 if(order*.03<.3)
492 arrange_verticies_polycircle(g,4,0,order*.03,0,0,0);
493 else
494 arrange_verticies_polycircle(g,4,0,.3,0,0,0);
498 void generate_shape(graph *g, int order){
499 int mod=0;
500 int dens=64;
501 int min=8;
502 mesh m;
503 random_seed(order+1);
505 switch(order%13){
506 case 0: // star
507 mod=10; break;
508 case 1:
509 break;
510 case 2: // dashed circle
511 dens=48; break;
512 case 3: // bifur
513 dens=80; break;
514 case 4:
515 break;
516 case 5:
517 min = 12;
518 dens = 10;
519 break;
520 case 6:
521 min = 10;
522 break;
523 case 7:
524 min = 10;
525 break;
526 case 8:
527 min = 10;
528 break;
529 case 9:
530 min = 10;
531 break;
532 case 10: // ring
533 dens=128;
534 min = 11;
535 break;
536 case 11:
537 min = 12;
538 break;
539 case 12: // target
540 min = 14;
541 break;
544 mesh_setup(g, &m, (order>min?order:min), mod);
545 randomize_verticies(g);
547 switch(order % 13){
548 case 0: // star
549 arrange_region_star(g); break; //4
550 case 1: // rainbow
551 arrange_region_rainbow(g); break; //9
552 case 2: // dashed circle
553 arrange_region_dashed_circle(g); break; //0
554 case 3: // bifur
555 arrange_region_bifur(g); break; //0
556 case 4: // dairyqueen
557 arrange_region_dairyqueen(g); break; //0
558 case 5: // cloud
559 arrange_region_cloud(g); break; //0
560 case 6: // storm
561 arrange_region_storm(g); break; //11
562 case 7: // plus;
563 arrange_region_plus(g); break; //2
564 case 8:
565 arrange_region_hole3(g); break; //4
566 case 9:
567 arrange_region_hole4(g); break; //15
568 case 10: // ring
569 arrange_region_ring(g); break; //29
570 case 11:
571 arrange_region_ovals(g); break; //95
572 case 12: // target
573 arrange_region_target(g); break; //108
576 m.active_max=region_layout(g);
577 generate_mesh2(&m,dens,0);