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