chiark / gitweb /
do not have threads at all if NPROCESSORS=1
[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
20 typedef struct {
21   double weight;
22   CostComputation *fn;
23 } CostContribution;
24
25 static const CostContribution costs[]= {
26 #define PRECOMP(compute) { 0,(compute) },
27 #define COST(weight, compute) { (weight),(compute) },
28
29   PRECOMP(compute_edge_lengths)
30   PRECOMP(compute_vertex_areas)
31
32 #if XBITS==3
33 #define STOP_EPSILON 1e-6
34     COST(  3e3,   line_bending_cost)
35     COST(  3e3,   edge_length_variation_cost)
36     COST( 0.4e3,  rim_proximity_cost)
37     COST(  1e6,   edge_angle_cost)
38                   #define EDGE_ANGLE_COST_CIRCCIRCRAT (0.5/1.7)
39 //    COST(  1e1,   small_triangles_cost)
40     COST(  1e12,   noncircular_rim_cost)
41 #endif
42
43 #if XBITS==4
44 #define STOP_EPSILON 1e-6
45     COST(  3e5,   line_bending_cost)
46     COST( 10e2,   edge_length_variation_cost)
47     COST( 9.0e1,  rim_proximity_cost) // 5e1 is too much
48                                       // 2.5e1 is too little
49     // 0.2e1 grows compared to previous ?
50     // 0.6e0 shrinks compared to previous ?
51
52     COST(  1e12,   edge_angle_cost)
53                   #define EDGE_ANGLE_COST_CIRCCIRCRAT (0.5/1.3)
54     COST(  1e18,   noncircular_rim_cost)
55 #endif
56 };
57
58 #define NCOSTS ((sizeof(costs)/sizeof(costs[0])))
59
60 const double edge_angle_cost_circcircrat= EDGE_ANGLE_COST_CIRCCIRCRAT;
61
62 void energy_init(void) {
63   stop_epsilon= STOP_EPSILON;
64 }
65
66 /*---------- energy computation machinery ----------*/
67
68 typedef struct {
69   double total;
70   const CostContribution *cc;
71 } CostComputationData;
72
73 void compute_energy_separately(const struct Vertices *vs,
74                          int section, void *energy_v, void *ccd_v) {
75   CostComputationData *ccd= ccd_v;
76   double *energy= energy_v;
77   *energy= ccd->cc->fn(vs->a, section);
78 }
79
80 void compute_energy_combine(const struct Vertices *vertices,
81                          int section, void *energy_v, void *ccd_v) {
82   CostComputationData *ccd= ccd_v;
83   double *energy= energy_v;
84   ccd->total += *energy;
85 }
86
87 double compute_energy(const struct Vertices *vs) {
88   static int bests_unprinted;
89
90   double energy;
91   int ci, printing;
92   CostComputationData ccd;
93
94   printing= printing_check(pr_cost,0);
95
96   if (printing) printf("%15lld c>e |", evaluations);
97
98   energy= 0;
99
100   for (ci=0; ci<NCOSTS; ci++) {
101     ccd.total= 0;
102     ccd.cc= &costs[ci];
103     
104     inparallel(vs,
105                compute_energy_separately,
106                compute_energy_combine,
107                sizeof(energy),
108                &ccd);
109
110     if (ccd.cc->weight != 0)
111       addcost(&energy, costs[ci].weight, ccd.total, printing);
112   }
113
114   if (printing) printf("| total %# e |", energy);
115
116   if (energy < best_energy) {
117     FILE *best_f;
118     int r;
119
120     if (printing) {
121       printf(" BEST");
122       if (bests_unprinted) printf(" [%4d]",bests_unprinted);
123       bests_unprinted= 0;
124     } else {
125       bests_unprinted++;
126     }
127
128     best_f= fopen(best_file_tmp,"wb");  if (!best_f) diee("fopen new out");
129     r= fwrite(vs->a,sizeof(vs->a),1,best_f); if (r!=1) diee("fwrite");
130     if (fclose(best_f)) diee("fclose new best");
131     if (rename(best_file_tmp,best_file)) diee("rename install new best");
132
133     best_energy= energy;
134   }
135   if (printing) {
136     putchar('\n');
137     flushoutput();
138   }
139
140   evaluations++;
141   return energy;
142 }
143
144 static void addcost(double *energy, double tweight, double tcost, int pr) {
145   double tenergy= tweight * tcost;
146   if (pr) printf(" %# e x %g > %# e* |", tcost, tweight, tenergy);
147   *energy += tenergy;
148 }
149
150 /*---------- Precomputations ----------*/
151
152 double compute_edge_lengths(const Vertices vertices, int section) {
153   int v1,e,v2;
154
155   FOR_EDGE(v1,e,v2, OUTER)
156     edge_lengths[v1][e]= hypotD(vertices[v1],vertices[v2]);
157
158   return 0;
159 }
160
161 double compute_vertex_areas(const Vertices vertices, int section) {
162   int v0,v1,v2, e1,e2;
163 //  int k;
164
165   FOR_VERTEX(v0, OUTER) {
166     double total= 0.0, edges_total=0;
167     int count= 0;
168
169     FOR_VEDGE(v0,e1,v1) {
170       e2= (e1+1) % V6;
171       v2= EDGE_END2(v0,e2);
172       if (v2<0) continue;
173
174       edges_total += edge_lengths[v0][e1];
175
176 //      double e1v[D3], e2v[D3], av[D3];
177 //      K {
178 //      e1v[k]= vertices[v1][k] - vertices[v0][k];
179 //      e2v[k]= vertices[v2][k] - vertices[v0][k];
180 //      }
181 //      xprod(av, e1v, e2v);
182 //      total += magnD(av);
183
184       count++;
185     }
186     vertex_areas[v0]= total / count;
187     vertex_mean_edge_lengths[v0]= edges_total / count;
188   }
189
190   return 0;
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 }