23 |
23 |
24 RingFinder::RingFinder() {} |
24 RingFinder::RingFinder() {} |
25 |
25 |
26 // ============================================================================= |
26 // ============================================================================= |
27 // |
27 // |
28 bool RingFinder::findRingsRecursor (double r0, double r1, Solution& currentSolution) |
28 bool RingFinder::findRingsRecursor(double r0, double r1, Solution& currentSolution) |
29 { |
29 { |
30 // Don't recurse too deep. |
30 // Don't recurse too deep. |
31 if (m_stack >= 5 or r1 < r0) |
31 if (m_stack >= 5 or r1 < r0) |
32 return false; |
32 return false; |
33 |
33 |
34 // Find the scale and number of a ring between r1 and r0. |
34 // Find the scale and number of a ring between r1 and r0. |
35 double scale = r1 - r0; |
35 double scale = r1 - r0; |
36 double num = r0 / scale; |
36 double num = r0 / scale; |
37 |
37 |
38 // If the ring number is integral, we have found a fitting ring to r0 -> r1! |
38 // If the ring number is integral, we have found a fitting ring to r0 -> r1! |
39 if (isInteger (num)) |
39 if (isInteger(num)) |
40 { |
40 { |
41 Component cmp; |
41 Component cmp; |
42 cmp.scale = scale; |
42 cmp.scale = scale; |
43 cmp.num = (int) round (num); |
43 cmp.num = (int) round(num); |
44 currentSolution.addComponent (cmp); |
44 currentSolution.addComponent(cmp); |
45 |
45 |
46 // If we're still at the first recursion, this is the only |
46 // If we're still at the first recursion, this is the only |
47 // ring and there's nothing left to do. Guess we found the winner. |
47 // ring and there's nothing left to do. Guess we found the winner. |
48 if (m_stack == 0) |
48 if (m_stack == 0) |
49 { |
49 { |
50 m_solutions.push_back (currentSolution); |
50 m_solutions.push_back(currentSolution); |
51 return true; |
51 return true; |
52 } |
52 } |
53 } |
53 } |
54 else |
54 else |
55 { |
55 { |
56 // Try find solutions by splitting the ring in various positions. |
56 // Try find solutions by splitting the ring in various positions. |
57 if (isZero (r1 - r0)) |
57 if (isZero(r1 - r0)) |
58 return false; |
58 return false; |
59 |
59 |
60 double interval; |
60 double interval; |
61 |
61 |
62 // Determine interval. The smaller delta between radii, the more precise |
62 // Determine interval. The smaller delta between radii, the more precise |
75 for (double r = r0 + interval; r < r1; r += interval) |
75 for (double r = r0 + interval; r < r1; r += interval) |
76 { |
76 { |
77 Solution sol = currentSolution; |
77 Solution sol = currentSolution; |
78 |
78 |
79 m_stack++; |
79 m_stack++; |
80 bool res = findRingsRecursor (r0, r, sol) and findRingsRecursor (r, r1, sol); |
80 bool res = findRingsRecursor(r0, r, sol) and findRingsRecursor(r, r1, sol); |
81 m_stack--; |
81 m_stack--; |
82 |
82 |
83 if (res) |
83 if (res) |
84 { |
84 { |
85 // We succeeded in finding radii for this segment. If the stack is 0, this |
85 // We succeeded in finding radii for this segment. If the stack is 0, this |
88 // |
88 // |
89 // If not, when this function ends, it will be called again with more arguments. |
89 // If not, when this function ends, it will be called again with more arguments. |
90 // Accept the solution to this segment by setting currentSolution to sol, and |
90 // Accept the solution to this segment by setting currentSolution to sol, and |
91 // return true to continue processing. |
91 // return true to continue processing. |
92 if (m_stack == 0) |
92 if (m_stack == 0) |
93 m_solutions.push_back (sol); |
93 m_solutions.push_back(sol); |
94 else |
94 else |
95 { |
95 { |
96 currentSolution = sol; |
96 currentSolution = sol; |
97 return true; |
97 return true; |
98 } |
98 } |
105 return true; |
105 return true; |
106 } |
106 } |
107 |
107 |
108 // |
108 // |
109 // This is the main algorithm of the ring finder. It tries to use math |
109 // This is the main algorithm of the ring finder. It tries to use math |
110 // to find the one ring between r0 and r1. If it fails (the ring number |
110 // to find the one ring between r0 and r1. If it fails(the ring number |
111 // is non-integral), it finds an intermediate radius (ceil of the ring |
111 // is non-integral), it finds an intermediate radius(ceil of the ring |
112 // number times scale) and splits the radius at this point, calling this |
112 // number times scale) and splits the radius at this point, calling this |
113 // function again to try find the rings between r0 - r and r - r1. |
113 // function again to try find the rings between r0 - r and r - r1. |
114 // |
114 // |
115 // This does not always yield into usable results. If at some point r == |
115 // This does not always yield into usable results. If at some point r == |
116 // r0 or r == r1, there is no hope of finding the rings, at least with |
116 // r0 or r == r1, there is no hope of finding the rings, at least with |
117 // this algorithm, as it would fall into an infinite recursion. |
117 // this algorithm, as it would fall into an infinite recursion. |
118 // |
118 // |
119 bool RingFinder::findRings (double r0, double r1) |
119 bool RingFinder::findRings(double r0, double r1) |
120 { |
120 { |
121 m_solutions.clear(); |
121 m_solutions.clear(); |
122 Solution sol; |
122 Solution sol; |
123 |
123 |
124 // If we're dealing with fractional radii, try upscale them into integral |
124 // If we're dealing with fractional radii, try upscale them into integral |
125 // ones. This should yield in more reliable and more optimized results. |
125 // ones. This should yield in more reliable and more optimized results. |
126 // For instance, using r0=1.5, r1=3.5 causes the algorithm to fail but |
126 // For instance, using r0=1.5, r1=3.5 causes the algorithm to fail but |
127 // r0=3, r1=7 (scaled up by 2) yields a 2-component solution. We can then |
127 // r0=3, r1=7(scaled up by 2) yields a 2-component solution. We can then |
128 // downscale the radii back by dividing the scale fields of the solution |
128 // downscale the radii back by dividing the scale fields of the solution |
129 // components. |
129 // components. |
130 double scale = 1.0; |
130 double scale = 1.0; |
131 |
131 |
132 if (not isZero (scale = r0 - floor (r0)) or not isZero (scale = r1 - floor (r1))) |
132 if (not isZero(scale = r0 - floor(r0)) or not isZero(scale = r1 - floor(r1))) |
133 { |
133 { |
134 double r0f = r0 / scale; |
134 double r0f = r0 / scale; |
135 double r1f = r1 / scale; |
135 double r1f = r1 / scale; |
136 |
136 |
137 if (isInteger (r0f) and isInteger (r1f)) |
137 if (isInteger(r0f) and isInteger(r1f)) |
138 { |
138 { |
139 r0 = r0f; |
139 r0 = r0f; |
140 r1 = r1f; |
140 r1 = r1f; |
141 } |
141 } |
142 // If the numbers are both at most one-decimal fractions, we can use a scale of 10 |
142 // If the numbers are both at most one-decimal fractions, we can use a scale of 10 |
143 else if (isInteger (r0 * 10) and isInteger (r1 * 10)) |
143 else if (isInteger(r0 * 10) and isInteger(r1 * 10)) |
144 { |
144 { |
145 scale = 0.1; |
145 scale = 0.1; |
146 r0 *= 10; |
146 r0 *= 10; |
147 r1 *= 10; |
147 r1 *= 10; |
148 } |
148 } |
151 { |
151 { |
152 scale = 1.0; |
152 scale = 1.0; |
153 } |
153 } |
154 |
154 |
155 // Recurse in and try find solutions. |
155 // Recurse in and try find solutions. |
156 findRingsRecursor (r0, r1, sol); |
156 findRingsRecursor(r0, r1, sol); |
157 |
157 |
158 // If we had upscaled our radii, downscale back now. |
158 // If we had upscaled our radii, downscale back now. |
159 if (scale != 1.0) |
159 if (scale != 1.0) |
160 { |
160 { |
161 for (Solution& sol : m_solutions) |
161 for (Solution& sol : m_solutions) |
162 sol.scaleComponents (scale); |
162 sol.scaleComponents(scale); |
163 } |
163 } |
164 |
164 |
165 // Compare the solutions and find the best one. The solution class has an operator> |
165 // Compare the solutions and find the best one. The solution class has an operator> |
166 // overload to compare two solutions. |
166 // overload to compare two solutions. |
167 m_bestSolution = nullptr; |
167 m_bestSolution = nullptr; |
168 |
168 |
169 for (Solution const& sol : m_solutions) |
169 for (Solution const& sol : m_solutions) |
170 { |
170 { |
171 if (m_bestSolution == nullptr or sol.isSuperiorTo (m_bestSolution)) |
171 if (m_bestSolution == nullptr or sol.isSuperiorTo(m_bestSolution)) |
172 m_bestSolution = / |
172 m_bestSolution = / |
173 } |
173 } |
174 |
174 |
175 return (m_bestSolution); |
175 return (m_bestSolution); |
176 } |
176 } |
186 // yield less new primitives and cleaner definitions. |
186 // yield less new primitives and cleaner definitions. |
187 // |
187 // |
188 // The solution which is found superior to every other solution |
188 // The solution which is found superior to every other solution |
189 // will be the one returned by RingFinder::bestSolution(). |
189 // will be the one returned by RingFinder::bestSolution(). |
190 // |
190 // |
191 bool RingFinder::Solution::isSuperiorTo (const Solution* other) const |
191 bool RingFinder::Solution::isSuperiorTo(const Solution* other) const |
192 { |
192 { |
193 // If one solution has less components than the other one, it is definitely |
193 // If one solution has less components than the other one, it is definitely |
194 // better. |
194 // better. |
195 if (getComponents().size() != other->getComponents().size()) |
195 if (getComponents().size() != other->getComponents().size()) |
196 return getComponents().size() < other->getComponents().size(); |
196 return getComponents().size() < other->getComponents().size(); |