Line circle intersection: Difference between revisions

Content added Content deleted
m (→‎{{header|Phix}}: added syntax colouring, marked p2js compatible)
(Added Rust solution)
Line 2,242: Line 2,242:
a segment starting at [7.0, 4.0] and ending at [11.0, 8.0] is/are:
a segment starting at [7.0, 4.0] and ending at [11.0, 8.0] is/are:
[[8.0, 5.0]]</pre>
[[8.0, 5.0]]</pre>

=={{header|Rust}}==
{{trans|C++}}
<lang rust>
use assert_approx_eq::assert_approx_eq;

const EPS: f64 = 1e-14;

pub struct Point {
x: f64,
y: f64,
}

pub struct Line {
p1: Point,
p2: Point,
}

impl Line {
pub fn circle_intersections(&self, mx: f64, my: f64, r: f64, segment: bool) -> Vec<Point> {
let mut intersections: Vec<Point> = Vec::new();

let x0 = mx;
let y0 = my;
let x1 = self.p1.x;
let y1 = self.p1.y;
let x2 = self.p2.x;
let y2 = self.p2.y;

let ca = y2 - y1;
let cb = x1 - x2;
let cc = x2 * y1 - x1 * y2;

let a = ca.powi(2) + cb.powi(2);
let mut b = 0.0;
let mut c = 0.0;
let mut bnz = true;

if cb.abs() >= EPS {
b = 2.0 * (ca * cc + ca * cb * y0 - cb.powi(2) * x0);
c = cc.powi(2) + 2.0 * cb * cc * y0
- cb.powi(2) * (r.powi(2) - x0.powi(2) - y0.powi(2));
} else {
b = 2.0 * (cb * cc + ca * cb * x0 - ca.powi(2) * y0);
c = cc.powi(2) + 2.0 * ca * cc * x0
- ca.powi(2) * (r.powi(2) - x0.powi(2) - y0.powi(2));
bnz = false;
}
let mut d = b.powi(2) - 4.0 * a * c;
if d < 0.0 {
return intersections;
}

fn within(x: f64, y: f64, x1: f64, y1: f64, x2: f64, y2: f64) -> bool {
let d1 = ((x2 - x1).powi(2) + (y2 - y1).powi(2)).sqrt(); // distance between end-points
let d2 = ((x - x1).powi(2) + (y - y1).powi(2)).sqrt(); // distance from point to one end
let d3 = ((x2 - x).powi(2) + (y2 - y).powi(2)).sqrt(); // distance from point to other end
let delta = d1 - d2 - d3;
return delta.abs() < EPS;
}

fn fx(x: f64, ca: f64, cb: f64, cc: f64) -> f64 {
-(ca * x + cc) / cb
}

fn fy(y: f64, ca: f64, cb: f64, cc: f64) -> f64 {
-(cb * y + cc) / ca
}

fn rxy(
x: f64,
y: f64,
x1: f64,
y1: f64,
x2: f64,
y2: f64,
segment: bool,
intersections: &mut Vec<Point>,
) {
if !segment || within(x, y, x1, y1, x2, y2) {
let point = Point { x: x, y: y };
intersections.push(point);
}
}

if d == 0.0 {
if bnz {
let x = -b / (2.0 * a);
let y = fx(x, ca, cb, cc);
rxy(x, y, x1, y1, x2, y2, segment, &mut intersections);
} else {
let y = -b / (2.0 * a);
let x = fy(y, ca, cb, cc);
rxy(x, y, x1, y1, x2, y2, segment, &mut intersections);
}
} else {
d = d.sqrt();
if bnz {
let x = (-b + d) / (2.0 * a);
let y = fx(x, ca, cb, cc);
rxy(x, y, x1, y1, x2, y2, segment, &mut intersections);
let x = (-b - d) / (2.0 * a);
let y = fx(x, ca, cb, cc);
rxy(x, y, x1, y1, x2, y2, segment, &mut intersections);
} else {
let y = (-b + d) / (2.0 * a);
let x = fy(y, ca, cb, cc);
rxy(x, y, x1, y1, x2, y2, segment, &mut intersections);
let y = (-b - d) / (2.0 * a);
let x = fy(y, ca, cb, cc);
rxy(x, y, x1, y1, x2, y2, segment, &mut intersections);
}
}

intersections.sort_unstable_by(|a, b| a.x.partial_cmp(&b.x).unwrap());
intersections
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_circle_line_intersections() {
let mut p1 = Point { x: -10.0, y: 11.0 };
let mut p2 = Point { x: 10.0, y: -9.0 };
let mut line = Line { p1: p1, p2: p2 };

let result1 = line.circle_intersections(3.0, -5.0, 3.0, false);
assert_eq!(result1.len(), 2);
assert_approx_eq!(result1[0].x, 3.0);
assert_approx_eq!(result1[0].y, -2.0);
assert_approx_eq!(result1[1].x, 6.0);
assert_approx_eq!(result1[1].y, -5.0);

p1 = Point { x: -10.0, y: 11.0 };
p2 = Point { x: -11.0, y: -12.0 };
line = Line { p1: p1, p2: p2 };
let result2 = line.circle_intersections(3.0, -5.0, 3.0, true);
assert_eq!(result2.len(), 0);

p1 = Point { x: 3.0, y: -2.0 };
p2 = Point { x: 7.0, y: -2.0 };
line = Line { p1, p2 };
let result3 = line.circle_intersections(3.0, -5.0, 3.0, true);
assert_eq!(result3.len(), 1);
assert_approx_eq!(result3[0].x, 3.0);
assert_approx_eq!(result3[0].y, -2.0);

p1 = Point { x: 0.0, y: -3.0 };
p2 = Point { x: 0.0, y: 6.0 };
line = Line { p1, p2 };
let result4 = line.circle_intersections(0.0, 0.0, 4.0, false);
assert_eq!(result4.len(), 2);
assert_approx_eq!(result4[0].x, 0.0);
assert_approx_eq!(result4[1].x, 0.0);

let result5 = line.circle_intersections(0.0, 0.0, 4.0, true);
assert_eq!(result5.len(), 1);

p1 = Point { x: 6.0, y: 3.0 };
p2 = Point { x: 10.0, y: 7.0 };
line = Line { p1, p2 };
let result6 = line.circle_intersections(4.0, 2.0, 5.0, false);
assert_eq!(result6.len(), 2);
assert_approx_eq!(result6[0].x, 1.0);
assert_approx_eq!(result6[0].y, -2.0);
assert_approx_eq!(result6[1].x, 8.0);
assert_approx_eq!(result6[1].y, 5.0);

p1 = Point { x: 7.0, y: 4.0 };
p2 = Point { x: 11.0, y: 8.0 };
line = Line { p1, p2 };
let result7 = line.circle_intersections(4.0, 2.0, 5.0, true);
assert_eq!(result7.len(), 1);
assert_approx_eq!(result7[0].x, 8.0);
assert_approx_eq!(result7[0].y, 5.0);
}
}
</lang>

{{out}}
<pre>
running 1 test
test tests::test_circle_line_intersections ... ok
test result: ok. 1
</pre>



=={{header|Swift}}==
=={{header|Swift}}==