chiark / gitweb /
before abandon normalisation effort
[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 double density;
17
18 void energy_init(void) {
19   density= sqrt(N);
20 }
21
22 /*---------- main energy computation and subroutines ----------*/
23
24 double compute_energy(const struct Vertices *vs) {
25   double energy;
26   int printing;
27
28   compute_edge_lengths(vs->a);
29   compute_vertex_areas(vs->a);
30   energy= 0;
31
32   printing= printing_check(pr_cost,0);
33
34   if (printing) printf("cost > energy |");
35
36   COST(2.25e3, line_bending_adjcost(vs->a));
37   COST(1e3, edge_length_variation_cost(vs->a));
38   COST(0.2e3, rim_proximity_cost(vs->a));
39 //  COST(1e2, graph_layout_cost(vs->a));
40   COST(1e8, noncircular_rim_cost(vs->a));
41
42   if (printing) printf("| total %# e |", energy);
43
44   if (energy < best_energy) {
45     FILE *best_f;
46     int r;
47
48     if (printing) printf(" BEST");
49
50     best_f= fopen(output_file_tmp,"wb");  if (!best_f) diee("fopen new out");
51     r= fwrite(vs->a,sizeof(vs->a),1,best_f); if (r!=1) diee("fwrite");
52     if (fclose(best_f)) diee("fclose new best");
53     if (rename(output_file_tmp,output_file)) diee("rename install new best");
54
55     best_energy= energy;
56   }
57   if (printing) {
58     putchar('\n');
59     flushoutput();
60   }
61
62   return energy;
63 }
64
65 static void addcost(double *energy, double tweight, double tcost, int pr) {
66   double tenergy= tweight * tcost;
67   if (pr) printf(" %# e x %# e > %# e* |", tcost, tweight, tenergy);
68   *energy += tenergy;
69 }
70
71 /*---------- Precomputations ----------*/
72
73 void compute_edge_lengths(const Vertices vertices) {
74   int v1,e,v2;
75
76   FOR_EDGE(v1,e,v2)
77     edge_lengths[v1][e]= hypotD(vertices[v1],vertices[v2]);
78 }
79
80 void compute_vertex_areas(const Vertices vertices) {
81   int v0,v1,v2, e1,e2;
82 //  int k;
83
84   FOR_VERTEX(v0) {
85     double total= 0.0, edges_total=0;
86     int count= 0;
87
88     FOR_VEDGE(v0,e1,v1) {
89       e2= (e1+1) % V6;
90       v2= EDGE_END2(v0,e2);
91       if (v2<0) continue;
92
93       edges_total += edge_lengths[v0][e1];
94
95 //      double e1v[D3], e2v[D3], av[D3];
96 //      K {
97 //      e1v[k]= vertices[v1][k] - vertices[v0][k];
98 //      e2v[k]= vertices[v2][k] - vertices[v0][k];
99 //      }
100 //      xprod(av, e1v, e2v);
101 //      total += magnD(av);
102
103       count++;
104     }
105     vertex_areas[v0]= total / count;
106     vertex_mean_edge_lengths[v0]= edges_total / count;
107   }
108 }
109
110 /*---------- Edgewise vertex displacement ----------*/
111
112   /*
113    * Definition:
114    *
115    *    At each vertex Q, in each direction e:
116    *
117    *                                  e
118    *                           Q ----->----- R
119    *                      _,-'\__/
120    *                  _,-'       delta
121    *               P '
122    *
123    *                      r
124    *       cost    = delta          (we use r=3)
125    *           Q,e
126    *
127    *
128    * Calculation:
129    *
130    *      Let vector A = PQ
131    *                 B = QR
132    *
133    *                   -1   A . B
134    *      delta =  tan     -------
135    *                      | A x B |
136    *
137    *      which is always in the range 0..pi because the denominator
138    *      is nonnegative.  We add epsilon to |AxB| to avoid division
139    *      by zero.
140    *
141    *                     r
142    *      cost    = delta
143    *          Q,e
144    *
145    * Normalisation:
146    *
147    *    We want the minimum energy to remain unchanged with changes in
148    *    triangle densitiy, when the vertices lie evenly spaced on
149    *    circles, and we do this by normalising the force ie the
150    *    derivative of the energy with respect to linear motions of the
151    *    vertices.
152    *
153    *    We consider only the force on Q due to PQR, wlog.  (Forces on
154    *    P qnd R due to PQR are equal and opposite so normalising
155    *    forces on Q will normalise them too.)
156    *
157    *    Force on Q is in the plnae PQR and normal to PR, so we can
158    *    consider it only linearly in that dimension.  WLOG let that be
159    *    the x dimension.  So with f' representing df'/dx_Q:
160    *
161    *                  ,         d
162    *      F    =  cost      =  --
163    *       Q,e         Q,e           err looks like we can only do
164    *                                 this if we make some kind of
165    *                                 assumption about delta or
166    *                                 something   give up
167    *
168    *
169    *    Interposing M and N so that we have P-M-Q-N-R
170    *    generates half as much delta for each vertex.  So
171    *
172
173    In that case the force on Q
174    *    due to PQR
175    *
176    *Normalising for equal linear
177    *    forces:
178    *
179    *                                      d
180    *      linear force on Q due to e  =  -------  cost
181    *                                     d coord       Q,e
182    *                                            Q
183    *
184    *       (we will consider only one e and one coord and hope
185    *        that doesn't lead us astray.)
186    *
187    *
188    *           ,       -r
189    *       cost    =  D   . cost
190    *           Q,e              Q,e
191    *
192    *                           where D is the linear density.
193    *
194    *                ,              -r
195    *     Sigma  cost      =   N . D  .  Sigma  cost
196    *      Q,e       Q,e                  Q,e       Q,e
197    *
198    * */
199
200 double line_bending_adjcost(const Vertices vertices) {
201   static const double axb_epsilon= 1e-6;
202   static const double exponent_r= 3;
203
204   int pi,e,qi,ri, k;
205   double  a[D3], b[D3], axb[D3];
206   double total_cost= 0;
207
208   FOR_EDGE(qi,e,ri) {
209     pi= EDGE_END2(qi,(e+3)%V6); if (pi<0) continue;
210
211     K a[k]= -vertices[pi][k] + vertices[qi][k];
212     K b[k]= -vertices[qi][k] + vertices[ri][k];
213
214     xprod(axb,a,b);
215
216     double delta= atan2(magnD(axb) + axb_epsilon, dotprod(a,b));
217     double cost= pow(delta,exponent_r);
218
219     if (!e && !(qi & YMASK))
220       cost *= 10;
221
222     total_cost += cost;
223   }
224   return total_cost / (N / density);
225 }
226
227 /*---------- edge length variation ----------*/
228
229   /*
230    * Definition:
231    *
232    *    See the diagram above.
233    *
234    *       cost    =
235    *           Q,e
236
237 double edge_length_variation_cost(const Vertices vertices) {
238   double diff, cost= 0;
239   int q, e,r, eback;
240
241   FOR_EDGE(q,e,r) {
242     eback= edge_reverse(q,e);
243     diff= edge_lengths[q][e] - edge_lengths[q][eback];
244     cost += diff*diff;
245   }
246   return cost;
247 }
248
249 /*---------- rim proximity cost ----------*/
250
251 static void find_nearest_oncircle(double oncircle[D3], const double p[D3]) {
252   /* By symmetry, nearest point on circle is the one with
253    * the same angle subtended at the z axis. */
254   oncircle[0]= p[0];
255   oncircle[1]= p[1];
256   oncircle[2]= 0;
257   double mult= 1.0/ magnD(oncircle);
258   oncircle[0] *= mult;
259   oncircle[1] *= mult;
260 }
261
262 double rim_proximity_cost(const Vertices vertices) {
263   double oncircle[3], cost=0;
264   int v;
265
266   FOR_VERTEX(v) {
267     int y= v >> YSHIFT;
268     int nominal_edge_distance= y <= Y/2 ? y : Y-1-y;
269     if (nominal_edge_distance==0) continue;
270
271     find_nearest_oncircle(oncircle, vertices[v]);
272
273     cost +=
274       vertex_mean_edge_lengths[v] *
275       (nominal_edge_distance*nominal_edge_distance) /
276       (hypotD2(vertices[v], oncircle) + 1e-6);
277   }
278   return cost;
279 }
280
281 /*---------- noncircular rim cost ----------*/
282
283 double noncircular_rim_cost(const Vertices vertices) {
284   int vy,vx,v;
285   double cost= 0.0;
286   double oncircle[3];
287
288   FOR_RIM_VERTEX(vy,vx,v) {
289     find_nearest_oncircle(oncircle, vertices[v]);
290
291     double d2= hypotD2(vertices[v], oncircle);
292     cost += d2*d2;
293   }
294   return cost;
295 }