chiark / gitweb /
ed50bc7e0effa6d235d6c29d02c22aab7feee5b5
[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 static void compute_vertex_areas(const Vertices vertices, double areas[N]);
10 static double best_energy= DBL_MAX;
11
12 static void addcost(double *energy, double tweight, double tcost, int pr);
13 #define COST(weight, compute) addcost(&energy, (weight), (compute), printing)
14
15 /*---------- main energy computation and subroutines ----------*/
16
17 double compute_energy(const struct Vertices *vs) {
18   double vertex_areas[N], energy;
19   int printing;
20
21   compute_vertex_areas(vs->a, vertex_areas);
22   energy= 0;
23
24   printing= printing_check(pr_cost);
25
26   if (printing) printf("cost > energy |");
27
28   COST(1e2, edgewise_vertex_displacement_cost(vs->a));
29   COST(1e2, graph_layout_cost(vs->a,vertex_areas));
30 //  COST(1e4, noncircular_rim_cost(vs->a));
31
32   if (printing) printf("| total %# e |", energy);
33
34   if (energy < best_energy) {
35     FILE *best_f;
36     int r;
37
38     if (printing) printf(" BEST");
39
40     best_f= fopen(output_file_tmp,"wb");  if (!best_f) diee("fopen new out");
41     r= fwrite(vs->a,sizeof(vs->a),1,best_f); if (r!=1) diee("fwrite");
42     if (fclose(best_f)) diee("fclose new best");
43     if (rename(output_file_tmp,output_file)) diee("rename install new best");
44
45     best_energy= energy;
46   }
47   if (printing) {
48     putchar('\n');
49     flushoutput();
50   }
51
52   return energy;
53 }
54
55 static void addcost(double *energy, double tweight, double tcost, int pr) {
56   double tenergy= tweight * tcost;
57   if (pr) printf(" %# e > %# e |", tcost, tenergy);
58   *energy += tenergy;
59 }
60
61 static void compute_vertex_areas(const Vertices vertices, double areas[N]) {
62   int v0,v1,v2, e1,e2, k;
63
64   FOR_VERTEX(v0) {
65     double total= 0.0;
66     int count= 0;
67
68     FOR_VEDGE(v0,e1,v1) {
69       e2= (e1+1) % V6;
70       v2= EDGE_END2(v0,e2);
71       if (v2<0) continue;
72
73       double e1v[D3], e2v[D3], av[D3];
74       K {
75         e1v[k]= vertices[v1][k] - vertices[v0][k];
76         e2v[k]= vertices[v2][k] - vertices[v0][k];
77       }
78       xprod(av, e1v, e2v);
79       total += magnD(av);
80       count++;
81     }
82     areas[v0]= total / count;
83   }
84 }
85
86 /*---------- Edgewise vertex displacement ----------*/
87
88   /*
89    *
90    *
91    *
92    *                Q `-_
93    *              / |    `-_
94    *             /  |       `-.
95    *            /   M - - - - - S
96    *           /  ' |      _,-'
97    *          /  '  |  _,-'
98    *         / '  , P '
99    *        / ',-'
100    *       /,-'
101    *      /'
102    *     R
103    *
104    *  Let delta =  180deg - angle RMS
105    *
106    *  Let  l = |PQ|
107    *       d = |RS|
108    *
109    *  Giving energy contribution:
110    *
111    *                                   3
112    *                            l delta
113    *    E             =  F   .  --------
114    *     vd, edge PQ      vd       d
115    *
116    *
117    *  (The dimensions of this are those of F_vd.)
118    *
119    *  We calculate delta as  atan2(|AxB|, A.B)
120    *  where A = PQ, B = QR
121    *
122    *  In practice to avoid division by zero we'll add epsilon to d and
123    *  |AxB| and the huge energy ought then to be sufficient for the
124    *  model to avoid being close to R=S.
125    */
126
127 double edgewise_vertex_displacement_cost(const Vertices vertices) {
128   static const double axb_epsilon= 1e-6;
129
130   int pi,e,qi,ri, k; //,si
131   double  a[D3], b[D3], axb[D3]; //m[D3],
132   double total_cost= 0;
133
134   FOR_EDGE(qi,e,ri) {
135     pi= EDGE_END2(qi,(e+3)%V6); if (pi<0) continue;
136
137 //    K m[k]= (vertices[pi][k] + vertices[qi][k]) * 0.5;
138     K a[k]= -vertices[pi][k] + vertices[qi][k];
139     K b[k]= -vertices[qi][k] + vertices[ri][k];
140
141     xprod(axb,a,b);
142     
143     double delta= atan2(magnD(axb) + axb_epsilon, dotprod(a,b));
144     double cost= pow(delta,3);
145
146     if (!e && !(qi & YMASK))
147       cost *= 10;
148
149     total_cost += cost;
150   }
151   return total_cost;
152 }
153
154 /*---------- noncircular rim cost ----------*/
155
156 double noncircular_rim_cost(const Vertices vertices) {
157   int vy,vx,v;
158   double cost= 0.0;
159
160   FOR_RIM_VERTEX(vy,vx,v) {
161     double oncircle[3];
162     /* By symmetry, nearest point on circle is the one with
163      * the same angle subtended at the z axis. */
164     oncircle[0]= vertices[v][0];
165     oncircle[1]= vertices[v][1];
166     oncircle[2]= 0;
167     double mult= 1.0/ magnD(oncircle);
168     oncircle[0] *= mult;
169     oncircle[1] *= mult;
170     double d2= hypotD2(vertices[v], oncircle);
171     cost += d2*d2;
172   }
173   return cost;
174 }