| 1 | // Copyright 2012 Google Inc. |
| 2 | // Copyright 2020 Yevhenii Reizner |
| 3 | // |
| 4 | // Use of this source code is governed by a BSD-style license that can be |
| 5 | // found in the LICENSE file. |
| 6 | |
| 7 | /* |
| 8 | Find the intersection of a line and cubic by solving for valid t values. |
| 9 | |
| 10 | Analogous to line-quadratic intersection, solve line-cubic intersection by |
| 11 | representing the cubic as: |
| 12 | x = a(1-t)^3 + 2b(1-t)^2t + c(1-t)t^2 + dt^3 |
| 13 | y = e(1-t)^3 + 2f(1-t)^2t + g(1-t)t^2 + ht^3 |
| 14 | and the line as: |
| 15 | y = i*x + j (if the line is more horizontal) |
| 16 | or: |
| 17 | x = i*y + j (if the line is more vertical) |
| 18 | |
| 19 | Then using Mathematica, solve for the values of t where the cubic intersects the |
| 20 | line: |
| 21 | |
| 22 | (in) Resultant[ |
| 23 | a*(1 - t)^3 + 3*b*(1 - t)^2*t + 3*c*(1 - t)*t^2 + d*t^3 - x, |
| 24 | e*(1 - t)^3 + 3*f*(1 - t)^2*t + 3*g*(1 - t)*t^2 + h*t^3 - i*x - j, x] |
| 25 | (out) -e + j + |
| 26 | 3 e t - 3 f t - |
| 27 | 3 e t^2 + 6 f t^2 - 3 g t^2 + |
| 28 | e t^3 - 3 f t^3 + 3 g t^3 - h t^3 + |
| 29 | i ( a - |
| 30 | 3 a t + 3 b t + |
| 31 | 3 a t^2 - 6 b t^2 + 3 c t^2 - |
| 32 | a t^3 + 3 b t^3 - 3 c t^3 + d t^3 ) |
| 33 | |
| 34 | if i goes to infinity, we can rewrite the line in terms of x. Mathematica: |
| 35 | |
| 36 | (in) Resultant[ |
| 37 | a*(1 - t)^3 + 3*b*(1 - t)^2*t + 3*c*(1 - t)*t^2 + d*t^3 - i*y - j, |
| 38 | e*(1 - t)^3 + 3*f*(1 - t)^2*t + 3*g*(1 - t)*t^2 + h*t^3 - y, y] |
| 39 | (out) a - j - |
| 40 | 3 a t + 3 b t + |
| 41 | 3 a t^2 - 6 b t^2 + 3 c t^2 - |
| 42 | a t^3 + 3 b t^3 - 3 c t^3 + d t^3 - |
| 43 | i ( e - |
| 44 | 3 e t + 3 f t + |
| 45 | 3 e t^2 - 6 f t^2 + 3 g t^2 - |
| 46 | e t^3 + 3 f t^3 - 3 g t^3 + h t^3 ) |
| 47 | |
| 48 | Solving this with Mathematica produces an expression with hundreds of terms; |
| 49 | instead, use Numeric Solutions recipe to solve the cubic. |
| 50 | |
| 51 | The near-horizontal case, in terms of: Ax^3 + Bx^2 + Cx + D == 0 |
| 52 | A = (-(-e + 3*f - 3*g + h) + i*(-a + 3*b - 3*c + d) ) |
| 53 | B = 3*(-( e - 2*f + g ) + i*( a - 2*b + c ) ) |
| 54 | C = 3*(-(-e + f ) + i*(-a + b ) ) |
| 55 | D = (-( e ) + i*( a ) + j ) |
| 56 | |
| 57 | The near-vertical case, in terms of: Ax^3 + Bx^2 + Cx + D == 0 |
| 58 | A = ( (-a + 3*b - 3*c + d) - i*(-e + 3*f - 3*g + h) ) |
| 59 | B = 3*( ( a - 2*b + c ) - i*( e - 2*f + g ) ) |
| 60 | C = 3*( (-a + b ) - i*(-e + f ) ) |
| 61 | D = ( ( a ) - i*( e ) - j ) |
| 62 | |
| 63 | For horizontal lines: |
| 64 | (in) Resultant[ |
| 65 | a*(1 - t)^3 + 3*b*(1 - t)^2*t + 3*c*(1 - t)*t^2 + d*t^3 - j, |
| 66 | e*(1 - t)^3 + 3*f*(1 - t)^2*t + 3*g*(1 - t)*t^2 + h*t^3 - y, y] |
| 67 | (out) e - j - |
| 68 | 3 e t + 3 f t + |
| 69 | 3 e t^2 - 6 f t^2 + 3 g t^2 - |
| 70 | e t^3 + 3 f t^3 - 3 g t^3 + h t^3 |
| 71 | */ |
| 72 | |
| 73 | use super::cubic64::{self, Cubic64}; |
| 74 | use super::point64::SearchAxis; |
| 75 | use super::Scalar64; |
| 76 | |
| 77 | pub fn horizontal_intersect(cubic: &Cubic64, axis_intercept: f64, roots: &mut [f64; 3]) -> usize { |
| 78 | let (a, b, c, mut d) = cubic64::coefficients(&cubic.as_f64_slice()[1..]); |
| 79 | d -= axis_intercept; |
| 80 | let mut count = cubic64::roots_valid_t(a, b, c, d, roots); |
| 81 | let mut index = 0; |
| 82 | while index < count { |
| 83 | let calc_pt = cubic.point_at_t(roots[index]); |
| 84 | if !calc_pt.y.approximately_equal(axis_intercept) { |
| 85 | let mut extreme_ts = [0.0; 6]; |
| 86 | let extrema = cubic64::find_extrema(&cubic.as_f64_slice()[1..], &mut extreme_ts); |
| 87 | count = cubic.search_roots( |
| 88 | extrema, |
| 89 | axis_intercept, |
| 90 | SearchAxis::Y, |
| 91 | &mut extreme_ts, |
| 92 | roots, |
| 93 | ); |
| 94 | break; |
| 95 | } |
| 96 | |
| 97 | index += 1; |
| 98 | } |
| 99 | |
| 100 | count |
| 101 | } |
| 102 | |
| 103 | pub fn vertical_intersect(cubic: &Cubic64, axis_intercept: f64, roots: &mut [f64; 3]) -> usize { |
| 104 | let (a, b, c, mut d) = cubic64::coefficients(&cubic.as_f64_slice()); |
| 105 | d -= axis_intercept; |
| 106 | let mut count = cubic64::roots_valid_t(a, b, c, d, roots); |
| 107 | let mut index = 0; |
| 108 | while index < count { |
| 109 | let calc_pt = cubic.point_at_t(roots[index]); |
| 110 | if !calc_pt.x.approximately_equal(axis_intercept) { |
| 111 | let mut extreme_ts = [0.0; 6]; |
| 112 | let extrema = cubic64::find_extrema(&cubic.as_f64_slice(), &mut extreme_ts); |
| 113 | count = cubic.search_roots( |
| 114 | extrema, |
| 115 | axis_intercept, |
| 116 | SearchAxis::X, |
| 117 | &mut extreme_ts, |
| 118 | roots, |
| 119 | ); |
| 120 | break; |
| 121 | } |
| 122 | |
| 123 | index += 1; |
| 124 | } |
| 125 | |
| 126 | count |
| 127 | } |
| 128 | |