chiark / gitweb /
best interpolation so far
[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
9 double vertex_areas[N], vertex_mean_edge_lengths[N], edge_lengths[N][V6];
10
11 static double best_energy= DBL_MAX;
12
13 static void addcost(double *energy, double tweight, double tcost, int pr);
14 #define COST(weight, compute) addcost(&energy, (weight), (compute), printing)
15
16 void energy_init(void) {
17 }
18
19 /*---------- main energy computation and subroutines ----------*/
20
21 double compute_energy(const struct Vertices *vs) {
22   static int bests_unprinted;
23   
24   double energy;
25   int printing;
26
27   compute_edge_lengths(vs->a);
28   compute_vertex_areas(vs->a);
29   energy= 0;
30
31   printing= printing_check(pr_cost,0);
32
33   if (printing) printf("%15lld c>e |", evaluations);
34
35   COST(  3e2,   line_bending_cost(vs->a));
36   COST(  1e3,   edge_length_variation_cost(vs->a));
37   COST( 0.2e3,  rim_proximity_cost(vs->a));
38   COST(  1e8,   noncircular_rim_cost(vs->a));
39
40   if (printing) printf("| total %# e |", energy);
41
42   if (energy < best_energy) {
43     FILE *best_f;
44     int r;
45
46     if (printing) {
47       printf(" BEST");
48       if (bests_unprinted) printf(" [%4d]",bests_unprinted);
49       bests_unprinted= 0;
50     } else {
51       bests_unprinted++;
52     }
53
54     best_f= fopen(best_file_tmp,"wb");  if (!best_f) diee("fopen new out");
55     r= fwrite(vs->a,sizeof(vs->a),1,best_f); if (r!=1) diee("fwrite");
56     if (fclose(best_f)) diee("fclose new best");
57     if (rename(best_file_tmp,best_file)) diee("rename install new best");
58
59     best_energy= energy;
60   }
61   if (printing) {
62     putchar('\n');
63     flushoutput();
64   }
65
66   evaluations++;
67   return energy;
68 }
69
70 static void addcost(double *energy, double tweight, double tcost, int pr) {
71   double tenergy= tweight * tcost;
72   if (pr) printf(" %# e x %g > %# e* |", tcost, tweight, tenergy);
73   *energy += tenergy;
74 }
75
76 /*---------- Precomputations ----------*/
77
78 void compute_edge_lengths(const Vertices vertices) {
79   int v1,e,v2;
80
81   FOR_EDGE(v1,e,v2)
82     edge_lengths[v1][e]= hypotD(vertices[v1],vertices[v2]);
83 }
84
85 void compute_vertex_areas(const Vertices vertices) {
86   int v0,v1,v2, e1,e2;
87 //  int k;
88
89   FOR_VERTEX(v0) {
90     double total= 0.0, edges_total=0;
91     int count= 0;
92
93     FOR_VEDGE(v0,e1,v1) {
94       e2= (e1+1) % V6;
95       v2= EDGE_END2(v0,e2);
96       if (v2<0) continue;
97
98       edges_total += edge_lengths[v0][e1];
99
100 //      double e1v[D3], e2v[D3], av[D3];
101 //      K {
102 //      e1v[k]= vertices[v1][k] - vertices[v0][k];
103 //      e2v[k]= vertices[v2][k] - vertices[v0][k];
104 //      }
105 //      xprod(av, e1v, e2v);
106 //      total += magnD(av);
107
108       count++;
109     }
110     vertex_areas[v0]= total / count;
111     vertex_mean_edge_lengths[v0]= edges_total / count;
112   }
113 }
114
115 /*---------- Edgewise vertex displacement ----------*/
116
117   /*
118    * Definition:
119    *
120    *    At each vertex Q, in each direction e:
121    *
122    *                                  e
123    *                           Q ----->----- R
124    *                      _,-'\__/
125    *                  _,-'       delta
126    *               P '
127    *
128    *                      r
129    *       cost    = delta          (we use r=3)
130    *           Q,e
131    *
132    *
133    * Calculation:
134    *
135    *      Let vector A = PQ
136    *                 B = QR
137    *
138    *                   -1   A . B
139    *      delta =  tan     -------
140    *                      | A x B |
141    *
142    *      which is always in the range 0..pi because the denominator
143    *      is nonnegative.  We add epsilon to |AxB| to avoid division
144    *      by zero.
145    *
146    *                     r
147    *      cost    = delta
148    *          Q,e
149    */
150
151 double line_bending_cost(const Vertices vertices) {
152   static const double axb_epsilon= 1e-6;
153   static const double exponent_r= 3;
154
155   int pi,e,qi,ri, k;
156   double  a[D3], b[D3], axb[D3];
157   double total_cost= 0;
158
159   FOR_EDGE(qi,e,ri) {
160     pi= EDGE_END2(qi,(e+3)%V6); if (pi<0) continue;
161
162     K a[k]= -vertices[pi][k] + vertices[qi][k];
163     K b[k]= -vertices[qi][k] + vertices[ri][k];
164
165     xprod(axb,a,b);
166
167     double delta= atan2(magnD(axb) + axb_epsilon, dotprod(a,b));
168     double cost= pow(delta,exponent_r);
169
170     if (!e && !(qi & YMASK))
171       cost *= 10;
172
173     total_cost += cost;
174   }
175   return total_cost;
176 }
177
178 /*---------- edge length variation ----------*/
179
180   /*
181    * Definition:
182    *
183    *    See the diagram above.
184    *                                r
185    *       cost    = ( |PQ| - |QR| )
186    *           Q,e
187    */
188
189 double edge_length_variation_cost(const Vertices vertices) {
190   double diff, cost= 0, exponent_r= 2;
191   int q, e,r, eback;
192
193   FOR_EDGE(q,e,r) {
194     eback= edge_reverse(q,e);
195     diff= edge_lengths[q][e] - edge_lengths[q][eback];
196     cost += pow(diff,exponent_r);
197   }
198   return cost;
199 }
200
201 /*---------- rim proximity cost ----------*/
202
203 static void find_nearest_oncircle(double oncircle[D3], const double p[D3]) {
204   /* By symmetry, nearest point on circle is the one with
205    * the same angle subtended at the z axis. */
206   oncircle[0]= p[0];
207   oncircle[1]= p[1];
208   oncircle[2]= 0;
209   double mult= 1.0/ magnD(oncircle);
210   oncircle[0] *= mult;
211   oncircle[1] *= mult;
212 }
213
214 double rim_proximity_cost(const Vertices vertices) {
215   double oncircle[3], cost=0;
216   int v;
217
218   FOR_VERTEX(v) {
219     int y= v >> YSHIFT;
220     int nominal_edge_distance= y <= Y/2 ? y : Y-1-y;
221     if (nominal_edge_distance==0) continue;
222
223     find_nearest_oncircle(oncircle, vertices[v]);
224
225     cost +=
226       vertex_mean_edge_lengths[v] *
227       (nominal_edge_distance*nominal_edge_distance) /
228       (hypotD2(vertices[v], oncircle) + 1e-6);
229   }
230   return cost;
231 }
232
233 /*---------- noncircular rim cost ----------*/
234
235 double noncircular_rim_cost(const Vertices vertices) {
236   int vy,vx,v;
237   double cost= 0.0;
238   double oncircle[3];
239
240   FOR_RIM_VERTEX(vy,vx,v) {
241     find_nearest_oncircle(oncircle, vertices[v]);
242
243     double d2= hypotD2(vertices[v], oncircle);
244     cost += d2*d2;
245   }
246   return cost;
247 }