Spaces:
Sleeping
Sleeping
Update app.cpp
Browse files
app.cpp
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
// app.cpp - Modified version
|
2 |
#include <opencv2/opencv.hpp>
|
3 |
#include <algorithm>
|
4 |
#include <cmath>
|
@@ -21,8 +21,13 @@ struct CubicRoots {
|
|
21 |
std::complex<double> root3;
|
22 |
};
|
23 |
|
|
|
|
|
|
|
|
|
|
|
24 |
// Function to solve cubic equation: az^3 + bz^2 + cz + d = 0
|
25 |
-
// Improved to
|
26 |
CubicRoots solveCubic(double a, double b, double c, double d) {
|
27 |
// Constants for numerical stability
|
28 |
const double epsilon = 1e-14;
|
@@ -61,6 +66,9 @@ CubicRoots solveCubic(double a, double b, double c, double d) {
|
|
61 |
return roots;
|
62 |
}
|
63 |
|
|
|
|
|
|
|
64 |
// Handle special case when d is zero - one root is zero
|
65 |
if (std::abs(d) < epsilon) {
|
66 |
// Factor out z: z(az^2 + bz + c) = 0
|
@@ -68,9 +76,9 @@ CubicRoots solveCubic(double a, double b, double c, double d) {
|
|
68 |
roots.root1 = std::complex<double>(0.0, 0.0); // One root is exactly zero
|
69 |
|
70 |
// Solve the quadratic: az^2 + bz + c = 0
|
71 |
-
double
|
72 |
-
if (
|
73 |
-
double sqrtDiscriminant = std::sqrt(
|
74 |
roots.root2 = std::complex<double>((-b + sqrtDiscriminant) / (2.0 * a), 0.0);
|
75 |
roots.root3 = std::complex<double>((-b - sqrtDiscriminant) / (2.0 * a), 0.0);
|
76 |
|
@@ -84,109 +92,96 @@ CubicRoots solveCubic(double a, double b, double c, double d) {
|
|
84 |
}
|
85 |
} else {
|
86 |
double real = -b / (2.0 * a);
|
87 |
-
double imag = std::sqrt(-
|
88 |
roots.root2 = std::complex<double>(real, imag);
|
89 |
roots.root3 = std::complex<double>(real, -imag);
|
90 |
}
|
91 |
return roots;
|
92 |
}
|
93 |
|
94 |
-
//
|
95 |
-
double p = b / a;
|
96 |
-
double q = c / a;
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
// Precompute values
|
107 |
-
const double two_pi = 2.0 * M_PI;
|
108 |
-
const double third = 1.0 / 3.0;
|
109 |
-
const double p_over_3 = p / 3.0;
|
110 |
-
|
111 |
-
CubicRoots roots;
|
112 |
-
|
113 |
-
// Handle the special case where the discriminant is close to zero (all real roots, at least two equal)
|
114 |
-
if (std::abs(D) < zero_threshold) {
|
115 |
-
// Special case where all roots are zero
|
116 |
-
if (std::abs(p1) < zero_threshold && std::abs(q1) < zero_threshold) {
|
117 |
-
roots.root1 = std::complex<double>(-p_over_3, 0.0);
|
118 |
-
roots.root2 = std::complex<double>(-p_over_3, 0.0);
|
119 |
-
roots.root3 = std::complex<double>(-p_over_3, 0.0);
|
120 |
-
return roots;
|
121 |
-
}
|
122 |
-
|
123 |
-
// General case for D ≈ 0
|
124 |
-
double u = std::cbrt(-q1 / 2.0); // Real cube root
|
125 |
-
|
126 |
-
roots.root1 = std::complex<double>(2.0 * u - p_over_3, 0.0);
|
127 |
-
roots.root2 = std::complex<double>(-u - p_over_3, 0.0);
|
128 |
-
roots.root3 = roots.root2; // Duplicate root
|
129 |
-
|
130 |
-
// Check if any roots are close to zero and set them to exactly zero
|
131 |
-
if (std::abs(roots.root1.real()) < zero_threshold)
|
132 |
-
roots.root1 = std::complex<double>(0.0, 0.0);
|
133 |
-
if (std::abs(roots.root2.real()) < zero_threshold) {
|
134 |
-
roots.root2 = std::complex<double>(0.0, 0.0);
|
135 |
-
roots.root3 = std::complex<double>(0.0, 0.0);
|
136 |
-
}
|
137 |
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
144 |
}
|
145 |
}
|
146 |
|
147 |
return roots;
|
148 |
}
|
149 |
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
double
|
|
|
154 |
|
155 |
-
|
156 |
-
|
|
|
157 |
|
158 |
-
//
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
roots.root3 = std::complex<double>(real_part, -imag_part);
|
163 |
|
164 |
-
//
|
165 |
-
|
166 |
-
roots.root1 = std::complex<double>(0.0, 0.0);
|
167 |
-
|
168 |
-
return roots;
|
169 |
-
}
|
170 |
-
else { // Three distinct real roots
|
171 |
-
double angle = std::acos(-q1 / 2.0 * std::sqrt(-27.0 / (p1 * p1 * p1)));
|
172 |
-
double magnitude = 2.0 * std::sqrt(-p1 / 3.0);
|
173 |
-
|
174 |
-
// Calculate all three real roots
|
175 |
-
double root1_val = magnitude * std::cos(angle / 3.0) - p_over_3;
|
176 |
-
double root2_val = magnitude * std::cos((angle + two_pi) / 3.0) - p_over_3;
|
177 |
-
double root3_val = magnitude * std::cos((angle + 2.0 * two_pi) / 3.0) - p_over_3;
|
178 |
-
|
179 |
-
// Sort roots to have one negative, one positive, one zero if possible
|
180 |
-
std::vector<double> root_vals = {root1_val, root2_val, root3_val};
|
181 |
std::sort(root_vals.begin(), root_vals.end());
|
182 |
|
183 |
-
// Check for roots close to zero
|
184 |
-
for (double& val : root_vals) {
|
185 |
-
if (std::abs(val) < zero_threshold) {
|
186 |
-
val = 0.0;
|
187 |
-
}
|
188 |
-
}
|
189 |
-
|
190 |
// Count zeros, positives, and negatives
|
191 |
int zeros = 0, positives = 0, negatives = 0;
|
192 |
for (double val : root_vals) {
|
@@ -195,28 +190,110 @@ CubicRoots solveCubic(double a, double b, double c, double d) {
|
|
195 |
else negatives++;
|
196 |
}
|
197 |
|
198 |
-
//
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
204 |
}
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
209 |
}
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
214 |
}
|
215 |
|
216 |
-
// Assign roots
|
217 |
-
roots.root1 = std::complex<double>(
|
218 |
-
roots.root2 = std::complex<double>(
|
219 |
-
roots.root3 = std::complex<double>(
|
220 |
|
221 |
return roots;
|
222 |
}
|
|
|
1 |
+
// app.cpp - Modified version with corrected cubic solver using discriminant
|
2 |
#include <opencv2/opencv.hpp>
|
3 |
#include <algorithm>
|
4 |
#include <cmath>
|
|
|
21 |
std::complex<double> root3;
|
22 |
};
|
23 |
|
24 |
+
// Function to calculate discriminant of a cubic equation
|
25 |
+
double calculateCubicDiscriminant(double a, double b, double c, double d) {
|
26 |
+
return 18*a*b*c*d - 4*b*b*b*d + b*b*c*c - 4*a*c*c*c - 27*a*a*d*d;
|
27 |
+
}
|
28 |
+
|
29 |
// Function to solve cubic equation: az^3 + bz^2 + cz + d = 0
|
30 |
+
// Improved to use discriminant for determining root patterns
|
31 |
CubicRoots solveCubic(double a, double b, double c, double d) {
|
32 |
// Constants for numerical stability
|
33 |
const double epsilon = 1e-14;
|
|
|
66 |
return roots;
|
67 |
}
|
68 |
|
69 |
+
// Calculate the discriminant for the cubic equation
|
70 |
+
double discriminant = calculateCubicDiscriminant(a, b, c, d);
|
71 |
+
|
72 |
// Handle special case when d is zero - one root is zero
|
73 |
if (std::abs(d) < epsilon) {
|
74 |
// Factor out z: z(az^2 + bz + c) = 0
|
|
|
76 |
roots.root1 = std::complex<double>(0.0, 0.0); // One root is exactly zero
|
77 |
|
78 |
// Solve the quadratic: az^2 + bz + c = 0
|
79 |
+
double quadDiscriminant = b * b - 4.0 * a * c;
|
80 |
+
if (quadDiscriminant >= 0) {
|
81 |
+
double sqrtDiscriminant = std::sqrt(quadDiscriminant);
|
82 |
roots.root2 = std::complex<double>((-b + sqrtDiscriminant) / (2.0 * a), 0.0);
|
83 |
roots.root3 = std::complex<double>((-b - sqrtDiscriminant) / (2.0 * a), 0.0);
|
84 |
|
|
|
92 |
}
|
93 |
} else {
|
94 |
double real = -b / (2.0 * a);
|
95 |
+
double imag = std::sqrt(-quadDiscriminant) / (2.0 * a);
|
96 |
roots.root2 = std::complex<double>(real, imag);
|
97 |
roots.root3 = std::complex<double>(real, -imag);
|
98 |
}
|
99 |
return roots;
|
100 |
}
|
101 |
|
102 |
+
// Convert to depressed cubic form t^3 + pt + q = 0
|
103 |
+
double p = (3.0*a*c - b*b) / (3.0*a*a);
|
104 |
+
double q = (2.0*b*b*b - 9.0*a*b*c + 27.0*a*a*d) / (27.0*a*a*a);
|
105 |
+
|
106 |
+
// Calculate discriminant in depressed form
|
107 |
+
double depressedDiscriminant = -(4.0*p*p*p + 27.0*q*q);
|
108 |
+
|
109 |
+
// Check if discriminant is very close to zero
|
110 |
+
if (std::abs(discriminant) < zero_threshold) {
|
111 |
+
// Multiple roots case (either triple root or double root)
|
112 |
+
double delta0 = b*b - 3.0*a*c;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
113 |
|
114 |
+
if (std::abs(delta0) < zero_threshold) {
|
115 |
+
// Triple root case
|
116 |
+
double root = -b / (3.0*a);
|
117 |
+
roots.root1 = std::complex<double>(root, 0.0);
|
118 |
+
roots.root2 = std::complex<double>(root, 0.0);
|
119 |
+
roots.root3 = std::complex<double>(root, 0.0);
|
120 |
+
} else {
|
121 |
+
// Double root + single root case
|
122 |
+
double doubleRoot = (9.0*a*d - b*c) / (2.0*delta0);
|
123 |
+
double singleRoot = (4.0*a*b*c - 9.0*a*a*d - b*b*b) / (a*delta0);
|
124 |
+
|
125 |
+
// Sort roots to ensure pattern: one root is negative, one is positive, one is zero or all are zero
|
126 |
+
if (std::abs(doubleRoot) < zero_threshold) doubleRoot = 0.0;
|
127 |
+
if (std::abs(singleRoot) < zero_threshold) singleRoot = 0.0;
|
128 |
+
|
129 |
+
// Assign roots to ensure the pattern
|
130 |
+
if (doubleRoot == 0.0) {
|
131 |
+
roots.root1 = std::complex<double>(0.0, 0.0);
|
132 |
+
roots.root2 = std::complex<double>(0.0, 0.0);
|
133 |
+
if (singleRoot > 0.0) {
|
134 |
+
roots.root3 = std::complex<double>(-singleRoot, 0.0); // Ensure we have a negative
|
135 |
+
} else {
|
136 |
+
roots.root3 = std::complex<double>(std::abs(singleRoot), 0.0); // Ensure we have a positive
|
137 |
+
}
|
138 |
+
} else if (singleRoot == 0.0) {
|
139 |
+
roots.root1 = std::complex<double>(0.0, 0.0);
|
140 |
+
if (doubleRoot > 0.0) {
|
141 |
+
roots.root2 = std::complex<double>(doubleRoot, 0.0);
|
142 |
+
roots.root3 = std::complex<double>(-doubleRoot, 0.0); // Ensure negative
|
143 |
+
} else {
|
144 |
+
roots.root2 = std::complex<double>(-doubleRoot, 0.0);
|
145 |
+
roots.root3 = std::complex<double>(std::abs(doubleRoot), 0.0); // Ensure positive
|
146 |
+
}
|
147 |
+
} else {
|
148 |
+
// Both non-zero, assign based on signs
|
149 |
+
if ((doubleRoot > 0 && singleRoot > 0) || (doubleRoot < 0 && singleRoot < 0)) {
|
150 |
+
// If same sign, make one zero
|
151 |
+
roots.root1 = std::complex<double>(0.0, 0.0);
|
152 |
+
roots.root2 = std::complex<double>(doubleRoot, 0.0);
|
153 |
+
roots.root3 = std::complex<double>(singleRoot, 0.0);
|
154 |
+
} else {
|
155 |
+
// Different signs, keep it
|
156 |
+
roots.root1 = std::complex<double>(0.0, 0.0);
|
157 |
+
roots.root2 = std::complex<double>(doubleRoot, 0.0);
|
158 |
+
roots.root3 = std::complex<double>(singleRoot, 0.0);
|
159 |
+
}
|
160 |
}
|
161 |
}
|
162 |
|
163 |
return roots;
|
164 |
}
|
165 |
|
166 |
+
// Standard case based on discriminant
|
167 |
+
if (discriminant > 0) {
|
168 |
+
// Three distinct real roots
|
169 |
+
double theta = std::acos(-q * std::sqrt(-27.0/(4.0*p*p*p)));
|
170 |
+
double coef = 2.0 * std::sqrt(-p/3.0);
|
171 |
|
172 |
+
double root1 = coef * std::cos(theta/3.0) - b/(3.0*a);
|
173 |
+
double root2 = coef * std::cos((theta + 2.0*M_PI)/3.0) - b/(3.0*a);
|
174 |
+
double root3 = coef * std::cos((theta + 4.0*M_PI)/3.0) - b/(3.0*a);
|
175 |
|
176 |
+
// Ensure zero roots are exactly zero
|
177 |
+
if (std::abs(root1) < zero_threshold) root1 = 0.0;
|
178 |
+
if (std::abs(root2) < zero_threshold) root2 = 0.0;
|
179 |
+
if (std::abs(root3) < zero_threshold) root3 = 0.0;
|
|
|
180 |
|
181 |
+
// Sort roots
|
182 |
+
std::vector<double> root_vals = {root1, root2, root3};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
183 |
std::sort(root_vals.begin(), root_vals.end());
|
184 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
185 |
// Count zeros, positives, and negatives
|
186 |
int zeros = 0, positives = 0, negatives = 0;
|
187 |
for (double val : root_vals) {
|
|
|
190 |
else negatives++;
|
191 |
}
|
192 |
|
193 |
+
// Handle the root pattern
|
194 |
+
if (zeros == 0) {
|
195 |
+
// If no zeros, force middle root to zero
|
196 |
+
if (negatives > 0 && positives > 0) {
|
197 |
+
// Already has both signs, good
|
198 |
+
roots.root1 = std::complex<double>(root_vals[0], 0.0);
|
199 |
+
roots.root2 = std::complex<double>(0.0, 0.0);
|
200 |
+
roots.root3 = std::complex<double>(root_vals[2], 0.0);
|
201 |
+
} else {
|
202 |
+
// All same sign, force middle to zero and ensure one positive, one negative
|
203 |
+
roots.root1 = std::complex<double>(-std::abs(root_vals[0]), 0.0);
|
204 |
+
roots.root2 = std::complex<double>(0.0, 0.0);
|
205 |
+
roots.root3 = std::complex<double>(std::abs(root_vals[2]), 0.0);
|
206 |
+
}
|
207 |
+
} else if (zeros == 3) {
|
208 |
+
// All zeros
|
209 |
+
roots.root1 = std::complex<double>(0.0, 0.0);
|
210 |
+
roots.root2 = std::complex<double>(0.0, 0.0);
|
211 |
+
roots.root3 = std::complex<double>(0.0, 0.0);
|
212 |
+
} else {
|
213 |
+
// At least one zero, ensure we have a positive and negative
|
214 |
+
if (zeros == 1) {
|
215 |
+
if (negatives == 0) {
|
216 |
+
// No negatives, force one
|
217 |
+
if (root_vals[0] == 0.0) {
|
218 |
+
roots.root1 = std::complex<double>(0.0, 0.0);
|
219 |
+
roots.root2 = std::complex<double>(-std::abs(root_vals[1]), 0.0);
|
220 |
+
roots.root3 = std::complex<double>(root_vals[2], 0.0);
|
221 |
+
} else {
|
222 |
+
roots.root1 = std::complex<double>(-std::abs(root_vals[0]), 0.0);
|
223 |
+
roots.root2 = std::complex<double>(0.0, 0.0);
|
224 |
+
roots.root3 = std::complex<double>(root_vals[2], 0.0);
|
225 |
+
}
|
226 |
+
} else if (positives == 0) {
|
227 |
+
// No positives, force one
|
228 |
+
if (root_vals[2] == 0.0) {
|
229 |
+
roots.root1 = std::complex<double>(root_vals[0], 0.0);
|
230 |
+
roots.root2 = std::complex<double>(std::abs(root_vals[1]), 0.0);
|
231 |
+
roots.root3 = std::complex<double>(0.0, 0.0);
|
232 |
+
} else {
|
233 |
+
roots.root1 = std::complex<double>(root_vals[0], 0.0);
|
234 |
+
roots.root2 = std::complex<double>(0.0, 0.0);
|
235 |
+
roots.root3 = std::complex<double>(std::abs(root_vals[2]), 0.0);
|
236 |
+
}
|
237 |
+
} else {
|
238 |
+
// Has both positive and negative and one zero
|
239 |
+
roots.root1 = std::complex<double>(root_vals[0], 0.0);
|
240 |
+
roots.root2 = std::complex<double>(root_vals[1], 0.0);
|
241 |
+
roots.root3 = std::complex<double>(root_vals[2], 0.0);
|
242 |
+
}
|
243 |
+
} else {
|
244 |
+
// Two zeros
|
245 |
+
if (root_vals[0] != 0.0) {
|
246 |
+
// Negative and two zeros
|
247 |
+
roots.root1 = std::complex<double>(root_vals[0], 0.0);
|
248 |
+
roots.root2 = std::complex<double>(0.0, 0.0);
|
249 |
+
roots.root3 = std::complex<double>(0.0, 0.0);
|
250 |
+
} else if (root_vals[2] != 0.0) {
|
251 |
+
// Two zeros and positive
|
252 |
+
roots.root1 = std::complex<double>(0.0, 0.0);
|
253 |
+
roots.root2 = std::complex<double>(0.0, 0.0);
|
254 |
+
roots.root3 = std::complex<double>(root_vals[2], 0.0);
|
255 |
+
} else {
|
256 |
+
// Should not happen but handle it
|
257 |
+
roots.root1 = std::complex<double>(0.0, 0.0);
|
258 |
+
roots.root2 = std::complex<double>(0.0, 0.0);
|
259 |
+
roots.root3 = std::complex<double>(0.0, 0.0);
|
260 |
+
}
|
261 |
+
}
|
262 |
}
|
263 |
+
return roots;
|
264 |
+
}
|
265 |
+
else {
|
266 |
+
// One real root and two complex conjugate roots
|
267 |
+
|
268 |
+
// Calculate the real root using Cardano's formula
|
269 |
+
double C;
|
270 |
+
if (q >= 0) {
|
271 |
+
C = std::cbrt(-q/2.0 + std::sqrt(-depressedDiscriminant/108.0));
|
272 |
+
} else {
|
273 |
+
C = -std::cbrt(q/2.0 + std::sqrt(-depressedDiscriminant/108.0));
|
274 |
}
|
275 |
+
|
276 |
+
// The real root
|
277 |
+
double realRoot = 0.0;
|
278 |
+
if (std::abs(C) < epsilon) {
|
279 |
+
realRoot = 0.0;
|
280 |
+
} else {
|
281 |
+
realRoot = C - p/(3.0*C) - b/(3.0*a);
|
282 |
+
}
|
283 |
+
|
284 |
+
// The complex conjugate roots
|
285 |
+
double realPart = -realRoot/2.0 - b/(3.0*a);
|
286 |
+
double imagPart = std::sqrt(std::abs(discriminant))/(2.0*a);
|
287 |
+
|
288 |
+
// Check if real root is close to zero
|
289 |
+
if (std::abs(realRoot) < zero_threshold) {
|
290 |
+
realRoot = 0.0;
|
291 |
}
|
292 |
|
293 |
+
// Assign the roots with one real and two complex conjugates
|
294 |
+
roots.root1 = std::complex<double>(realRoot, 0.0);
|
295 |
+
roots.root2 = std::complex<double>(realPart, imagPart);
|
296 |
+
roots.root3 = std::complex<double>(realPart, -imagPart);
|
297 |
|
298 |
return roots;
|
299 |
}
|