chiark / gitweb /
use barriers rather than recreating threads
[moebius2.git] / energy.c
1 /*
2  * We try to find an optimal triangle grid
3  */
4
5 #include "common.h"
6 #include "minimise.h"
7 #include "mgraph.h"
8 #include "parallel.h"
9
10 double vertex_areas[N], vertex_mean_edge_lengths[N], edge_lengths[N][V6];
11
12 static double best_energy= DBL_MAX;
13
14 static void addcost(double *energy, double tweight, double tcost, int pr);
15
16 /*---------- main energy computation, weights, etc. ----------*/
17
18 typedef double CostComputation(const Vertices vertices, int section);
19 typedef void PreComputation(const Vertices vertices, int section);
20
21 typedef struct {
22   double weight;
23   CostComputation *fn;
24 } CostContribution;
25
26 #define NPRECOMPS ((sizeof(precomps)/sizeof(precomps[0])))
27 #define NCOSTS ((sizeof(costs)/sizeof(costs[0])))
28 #define COST(weight, compute) { (weight),(compute) },
29
30 static PreComputation *const precomps[]= {
31   compute_edge_lengths,
32   compute_vertex_areas
33 };
34
35 static const CostContribution costs[]= {
36
37 #if XBITS==3
38 #define STOP_EPSILON 1e-6
39     COST(  3e3,   line_bending_cost)
40     COST(  3e3,   edge_length_variation_cost)
41     COST( 0.4e3,  rim_proximity_cost)
42     COST(  1e6,   edge_angle_cost)
43                   #define EDGE_ANGLE_COST_CIRCCIRCRAT (0.5/1.7)
44 //    COST(  1e1,   small_triangles_cost)
45     COST(  1e12,   noncircular_rim_cost)
46 #endif
47
48 #if XBITS==4
49 #define STOP_EPSILON 1e-6
50     COST(  3e5,   line_bending_cost)
51     COST( 10e2,   edge_length_variation_cost)
52     COST( 9.0e1,  rim_proximity_cost) // 5e1 is too much
53                                       // 2.5e1 is too little
54     // 0.2e1 grows compared to previous ?
55     // 0.6e0 shrinks compared to previous ?
56
57     COST(  1e12,   edge_angle_cost)
58                   #define EDGE_ANGLE_COST_CIRCCIRCRAT (0.5/1.3)
59     COST(  1e18,   noncircular_rim_cost)
60 #endif
61
62 };
63
64 const double edge_angle_cost_circcircrat= EDGE_ANGLE_COST_CIRCCIRCRAT;
65
66 void energy_init(void) {
67   stop_epsilon= STOP_EPSILON;
68 }
69
70 /*---------- energy computation machinery ----------*/
71
72 void compute_energy_separately(const struct Vertices *vs,
73                          int section, void *energies_v, void *totals_v) {
74   double *energies= energies_v;
75   int ci;
76   
77   for (ci=0; ci<NPRECOMPS; ci++) {
78     costs[ci].fn(vs->a, section);
79     inparallel_barrier();
80   }
81   for (ci=0; ci<NCOSTS; ci++)
82     energies[ci]= costs[ci].fn(vs->a, section);
83 }
84
85 void compute_energy_combine(const struct Vertices *vertices,
86                          int section, void *energies_v, void *totals_v) {
87   int ci;
88   double *energies= energies_v;
89   double *totals= totals_v;
90
91   for (ci=0; ci<NCOSTS; ci++)
92     totals[ci] += energies[ci];
93 }
94
95 double compute_energy(const struct Vertices *vs) {
96   static int bests_unprinted;
97
98   double totals[NCOSTS], energy;
99   int ci, printing;
100
101   printing= printing_check(pr_cost,0);
102
103   if (printing) printf("%15lld c>e |", evaluations);
104
105   for (ci=0; ci<NCOSTS; ci++)
106     totals[ci]= 0;
107
108   inparallel(vs,
109              compute_energy_separately,
110              compute_energy_combine,
111              sizeof(totals) /* really, size of energies */,
112              totals);
113
114   energy= 0;
115   for (ci=0; ci<NCOSTS; ci++)
116     addcost(&energy, costs[ci].weight, totals[ci], printing);
117
118   if (printing) printf("| total %# e |", energy);
119
120   if (energy < best_energy) {
121     FILE *best_f;
122     int r;
123
124     if (printing) {
125       printf(" BEST");
126       if (bests_unprinted) printf(" [%4d]",bests_unprinted);
127       bests_unprinted= 0;
128     } else {
129       bests_unprinted++;
130     }
131
132     best_f= fopen(best_file_tmp,"wb");  if (!best_f) diee("fopen new out");
133     r= fwrite(vs->a,sizeof(vs->a),1,best_f); if (r!=1) diee("fwrite");
134     if (fclose(best_f)) diee("fclose new best");
135     if (rename(best_file_tmp,best_file)) diee("rename install new best");
136
137     best_energy= energy;
138   }
139   if (printing) {
140     putchar('\n');
141     flushoutput();
142   }
143
144   evaluations++;
145   return energy;
146 }
147
148 static void addcost(double *energy, double tweight, double tcost, int pr) {
149   double tenergy= tweight * tcost;
150   if (pr) printf(" %# e x %g > %# e* |", tcost, tweight, tenergy);
151   *energy += tenergy;
152 }
153
154 /*---------- Precomputations ----------*/
155
156 void compute_edge_lengths(const Vertices vertices, int section) {
157   int v1,e,v2;
158
159   FOR_EDGE(v1,e,v2, OUTER)
160     edge_lengths[v1][e]= hypotD(vertices[v1],vertices[v2]);
161 }
162
163 void compute_vertex_areas(const Vertices vertices, int section) {
164   int v0,v1,v2, e1,e2;
165 //  int k;
166
167   FOR_VERTEX(v0, OUTER) {
168     double total= 0.0, edges_total=0;
169     int count= 0;
170
171     FOR_VEDGE(v0,e1,v1) {
172       e2= (e1+1) % V6;
173       v2= EDGE_END2(v0,e2);
174       if (v2<0) continue;
175
176       edges_total += edge_lengths[v0][e1];
177
178 //      double e1v[D3], e2v[D3], av[D3];
179 //      K {
180 //      e1v[k]= vertices[v1][k] - vertices[v0][k];
181 //      e2v[k]= vertices[v2][k] - vertices[v0][k];
182 //      }
183 //      xprod(av, e1v, e2v);
184 //      total += magnD(av);
185
186       count++;
187     }
188     vertex_areas[v0]= total / count;
189     vertex_mean_edge_lengths[v0]= edges_total / count;
190   }
191 }
192
193 /*---------- Edgewise vertex displacement ----------*/
194
195   /*
196    * Definition:
197    *
198    *    At each vertex Q, in each direction e:
199    *
200    *                                  e
201    *                           Q ----->----- R
202    *                      _,-'\__/
203    *                  _,-'       delta
204    *               P '
205    *
206    *                      r
207    *       cost    = delta          (we use r=3)
208    *           Q,e
209    *
210    *
211    * Calculation:
212    *
213    *      Let vector A = PQ
214    *                 B = QR
215    *
216    *                   -1   A . B
217    *      delta =  tan     -------
218    *                      | A x B |
219    *
220    *      which is always in the range 0..pi because the denominator
221    *      is nonnegative.  We add epsilon to |AxB| to avoid division
222    *      by zero.
223    *
224    *                     r
225    *      cost    = delta
226    *          Q,e
227    */
228
229 double line_bending_cost(const Vertices vertices, int section) {
230   static const double axb_epsilon= 1e-6;
231   static const double exponent_r= 4;
232
233   int pi,e,qi,ri, k;
234   double  a[D3], b[D3], axb[D3];
235   double total_cost= 0;
236
237   FOR_EDGE(qi,e,ri, OUTER) {
238     pi= EDGE_END2(qi,(e+3)%V6); if (pi<0) continue;
239
240 //if (!(qi&XMASK)) fprintf(stderr,"%02x-%02x-%02x (%d)\n",pi,qi,ri,e);
241
242     K a[k]= -vertices[pi][k] + vertices[qi][k];
243     K b[k]= -vertices[qi][k] + vertices[ri][k];
244
245     xprod(axb,a,b);
246
247     double delta= atan2(magnD(axb) + axb_epsilon, dotprod(a,b));
248     double cost= pow(delta,exponent_r);
249
250     total_cost += cost;
251   }
252   return total_cost;
253 }
254
255 /*---------- edge length variation ----------*/
256
257   /*
258    * Definition:
259    *
260    *    See the diagram above.
261    *                                r
262    *       cost    = ( |PQ| - |QR| )
263    *           Q,e
264    */
265
266 double edge_length_variation_cost(const Vertices vertices, int section) {
267   double diff, cost= 0, exponent_r= 2;
268   int q, e,r, eback;
269
270   FOR_EDGE(q,e,r, OUTER) {
271     eback= edge_reverse(q,e);
272     diff= edge_lengths[q][e] - edge_lengths[q][eback];
273     cost += pow(diff,exponent_r);
274   }
275   return cost;
276 }
277
278 /*---------- rim proximity cost ----------*/
279
280 static void find_nearest_oncircle(double oncircle[D3], const double p[D3]) {
281   /* By symmetry, nearest point on circle is the one with
282    * the same angle subtended at the z axis. */
283   oncircle[0]= p[0];
284   oncircle[1]= p[1];
285   oncircle[2]= 0;
286   double mult= 1.0/ magnD(oncircle);
287   oncircle[0] *= mult;
288   oncircle[1] *= mult;
289 }
290
291 double rim_proximity_cost(const Vertices vertices, int section) {
292   double oncircle[3], cost=0;
293   int v;
294
295   FOR_VERTEX(v, OUTER) {
296     int y= v >> YSHIFT;
297     int nominal_edge_distance= y <= Y/2 ? y : Y-1-y;
298     if (nominal_edge_distance==0) continue;
299
300     find_nearest_oncircle(oncircle, vertices[v]);
301
302     cost +=
303       vertex_mean_edge_lengths[v] *
304       (nominal_edge_distance*nominal_edge_distance) /
305       (hypotD2(vertices[v], oncircle) + 1e-6);
306   }
307   return cost;
308 }
309
310 /*---------- noncircular rim cost ----------*/
311
312 double noncircular_rim_cost(const Vertices vertices, int section) {
313   int vy,vx,v;
314   double cost= 0.0;
315   double oncircle[3];
316
317   FOR_RIM_VERTEX(vy,vx,v, OUTER) {
318     find_nearest_oncircle(oncircle, vertices[v]);
319
320     double d2= hypotD2(vertices[v], oncircle);
321     cost += d2*d2;
322   }
323   return cost;
324 }
325
326 /*---------- overly sharp edge cost ----------*/
327
328   /*
329    *
330    *                Q `-_
331    *              / |    `-_                           P'Q' ------ S'
332    *             /  |       `-.                      _,' `.       .
333    *            /   |           S                 _,'     :      .
334    *           /    |      _,-'                _,'        :r    .r
335    *          /     |  _,-'                R' '           `.   .
336    *         /    , P '                        ` .   r     :  .
337    *        /  ,-'                                  `  .   : 
338    *       /,-'                                          ` C'
339    *      /'
340    *     R
341    *
342    *
343    *
344    *  Let delta =  angle between two triangles' normals
345    *
346    *  Giving energy contribution:
347    *
348    *                                   2
349    *    E             =  F   .  delta
350    *     vd, edge PQ      vd
351    */
352
353 double edge_angle_cost(const Vertices vertices, int section) {
354   double pq1[D3], rp[D3], ps[D3], rp_2d[D3], ps_2d[D3], rs_2d[D3];
355   double a,b,c,s,r;
356   const double minradius_base= 0.2;
357
358   int pi,e,qi,ri,si, k;
359 //  double our_epsilon=1e-6;
360   double total_cost= 0;
361
362   FOR_EDGE(pi,e,qi, OUTER) {
363 //    if (!(RIM_VERTEX_P(pi) || RIM_VERTEX_P(qi))) continue;
364
365     si= EDGE_END2(pi,(e+V6-1)%V6);  if (si<0) continue;
366     ri= EDGE_END2(pi,(e   +1)%V6);  if (ri<0) continue;
367
368     K {
369       pq1[k]= -vertices[pi][k] + vertices[qi][k];
370       rp[k]=  -vertices[ri][k] + vertices[pi][k];
371       ps[k]=  -vertices[pi][k] + vertices[si][k];
372     }
373
374     normalise(pq1,1,1e-6);
375     xprod(rp_2d, rp,pq1); /* projects RP into plane normal to PQ */
376     xprod(ps_2d, ps,pq1); /* likewise PS */
377     K rs_2d[k]= rp_2d[k] + ps_2d[k];
378     /* radius of circumcircle of R'P'S' from Wikipedia
379      * `Circumscribed circle' */
380     a= magnD(rp_2d);
381     b= magnD(ps_2d);
382     c= magnD(rs_2d);
383     s= 0.5*(a+b+c);
384     r= a*b*c / sqrt((a+b+c)*(a-b+c)*(b-c+a)*(c-a+b) + 1e-6);
385
386     double minradius= minradius_base + edge_angle_cost_circcircrat*(a+b);
387     double deficit= minradius - r;
388     if (deficit < 0) continue;
389     double cost= deficit*deficit;
390
391     total_cost += cost;
392   }
393
394   return total_cost;
395 }
396
397 /*---------- small triangles cost ----------*/
398
399   /*
400    *
401    *                Q `-_
402    *              / |    `-_
403    *             /  |       `-.
404    *            /   |           S
405    *           /    |      _,-'
406    *          /     |  _,-'
407    *         /    , P '
408    *        /  ,-'
409    *       /,-'
410    *      /'
411    *     R
412    *
413    *  Let delta =  angle between two triangles' normals
414    *
415    *  Giving energy contribution:
416    *
417    *                                   2
418    *    E             =  F   .  delta
419    *     vd, edge PQ      vd
420    */
421
422 double small_triangles_cost(const Vertices vertices, int section) {
423   double pq[D3], ps[D3];
424   double x[D3];
425   int pi,e,qi,si, k;
426 //  double our_epsilon=1e-6;
427   double total_cost= 0;
428
429   FOR_EDGE(pi,e,qi, OUTER) {
430 //    if (!(RIM_VERTEX_P(pi) || RIM_VERTEX_P(qi))) continue;
431
432     si= EDGE_END2(pi,(e+V6-1)%V6);  if (si<0) continue;
433
434     K {
435       pq[k]= vertices[qi][k] - vertices[pi][k];
436       ps[k]= vertices[si][k] - vertices[pi][k];
437     }
438     xprod(x, pq,ps);
439
440     double cost= 1/(magnD2(x) + 0.01);
441
442 //double cost= pow(magnD(spqxpqr), 3);
443 //assert(dot>=-1 && dot <=1);
444 //double cost= 1-dot;
445     total_cost += cost;
446   }
447
448   return total_cost;
449 }