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