Skip to content

Instantly share code, notes, and snippets.

@abonander
Last active August 29, 2015 14:06
Show Gist options
  • Save abonander/902235199a3770806ccd to your computer and use it in GitHub Desktop.
Save abonander/902235199a3770806ccd to your computer and use it in GitHub Desktop.
Triangle Solver in Rust
use std::num::FloatMath;
struct Tri {
sides: [Option<f32>, ..3],
angles: [Option<f32>, ..3],
ambiguous: Option<Box<Tri>>,
}
impl Tri {
fn from_vec(vals: &[Option<f32>]) -> Tri {
Tri {
sides: [vals[0], vals[1], vals[2]],
angles: [vals[3], vals[4], vals[5]],
ambiguous: None,
}
}
fn side(&mut self, i: int) -> &mut Option<f32> {
let len = self.sides.len();
self.sides.get_mut(wrap(i, len)).unwrap()
}
fn ang(&mut self, i: int) -> &mut Option<f32> {
let len = self.angles.len();
self.angles.get_mut(wrap(i, len)).unwrap()
}
fn solved(&self) -> bool {
count_knowns(&self.angles) == 3 && count_knowns(&self.sides) == 3
}
fn has_all_angles(&mut self) -> bool {
match self.angles {
[Some(_), Some(_), Some(_)] => return true,
[Some(a), Some(b), None] => self.angles[2] = Some(180f32 - (a + b)),
[Some(a), None, Some(c)] => self.angles[1] = Some(180f32 - (a + c)),
[None, Some(b), Some(c)] => self.angles[0] = Some(180f32 - (b + c)),
_ => return false,
}
self.has_all_angles()
}
fn solve_angles(&mut self) -> bool {
self.has_all_angles() ||
(match self.angles {
[None, _, _] => self.solve_angle(0),
[_, None, _] => self.solve_angle(1),
[_, _, None] => self.solve_angle(2),
_ => false,
}) && self.solve_angles()
}
fn solve_angle(&mut self, i: int) -> bool {
match (*self.side(i), *self.side(i - 1), *self.side(i + 1), *self.ang(i - 1), *self.ang(i + 1)) {
// Side - Side - Side
(Some(opp), Some(left), Some(right), _, _) => *self.ang(i) = Some(sss(opp, left, right)),
// Side - Side - Angle with the given angle opposite the short side (ambiguous case)
(Some(opp), Some(left), _, Some(_), _) if opp < left => self.ambiguous_case(i, -1),
// Ambiguous SSA right variant
(Some(opp) , _, Some(right), _, Some(_)) if opp < right => self.ambiguous_case(i, 1),
// Side - Side - Angle with the given angle opposite the long side (not ambiguous)
(Some(opp), Some(left), _, Some(ang_left), _) => *self.ang(i) = Some(no_ambi_ssa(opp, left, ang_left)),
// Non-ambiguous SSA right variant
(Some(opp), _, Some(right), _, Some(ang_right)) => *self.ang(i) = Some(no_ambi_ssa(opp, right, ang_right)),
// Cannot solve angles with info given
_ => return false,
}
true
}
fn has_all_sides(&self) -> bool {
match self.sides {
[Some(_), Some(_), Some(_)] => true,
_ => false,
}
}
fn solve_sides(&mut self) -> bool {
self.has_all_sides() ||
(match self.sides {
[None, _, _] => self.solve_side(0),
[_, None, _] => self.solve_side(1),
[_, _, None] => self.solve_side(2),
_ => false,
}) && self.solve_sides()
}
fn solve_side(&mut self, i: int) -> bool {
match (*self.side(i - 1), *self.side(i + 1), *self.ang(i), *self.ang(i - 1), *self.ang(i + 1)) {
// Side - Angle - Side
(Some(left), Some(right), Some(opp), _, _) =>
*self.side(i) = Some(sas(left, right, opp)),
// Angle - Angle - Side left
(Some(left), _, _, Some(ang_left), Some(ang_right)) =>
*self.side(i) = Some(asa(left, ang_left, ang_right)),
// Angle -Angle - Side right
(_, Some(right), _, Some(ang_left), Some(ang_right)) =>
*self.side(i) = Some(asa(right, ang_right, ang_left)),
// Angle - Side - Angle would have been worked out with solve_angles()
// All other cases are unsolvable
_ => return false,
}
true
}
fn ambiguous_case(&mut self, i: int, which: int) {
let ambi_ang = no_ambi_ssa(
self.side(i + which).unwrap(),
self.side(i).unwrap(),
self.ang(i + which).unwrap()
);
let other_ambi_ang = 180.0 - ambi_ang - self.ang(i + which).unwrap();
let mut other = Tri {
sides: [self.sides[0], self.sides[1], self.sides[2]],
angles: [self.angles[0], self.sides[1], self.sides[2]],
ambiguous: None,
};
*self.ang(i) = Some(ambi_ang);
*other.ang(i) = Some(other_ambi_ang);
self.ambiguous = Some(box other);
}
fn solve(&mut self) -> bool {
if self.solved() { return true; }
let solved = if !self.solve_angles() {
self.solve_sides() && self.solve_angles()
} else {
self.solve_sides()
};
let ambi_solved = self.ambiguous.as_mut().map(|ambi| ambi.solve()).unwrap_or(true);
solved && ambi_solved
}
fn print(&self) {
println!("{:.2f} {:.2f} {:.2f} {:.2f} {:.2f} {:.2f}",
self.sides[0].unwrap_or(0.0),
self.sides[1].unwrap_or(0.0),
self.sides[2].unwrap_or(0.0),
self.angles[0].unwrap_or(0.0),
self.angles[1].unwrap_or(0.0),
self.angles[2].unwrap_or(0.0),
);
self.ambiguous.as_ref().map(|tri| { println!("Ambiguous case:"); tri.print() });
}
}
#[inline]
fn deg_sin(x: f32) -> f32 { x.to_radians().sin() }
#[inline]
fn deg_cos(x: f32) -> f32 { x.to_radians().cos() }
#[inline]
fn deg_asin(x: f32) -> f32 { x.asin().to_degrees() }
#[inline]
fn deg_acos(x: f32) -> f32 { x.acos().to_degrees() }
fn sss(opp: f32, adj_1: f32, adj_2: f32) -> f32 {
// A = acos((b^2 + c^2 - a^2) / 2bc)
deg_acos((sq(adj_1) + sq(adj_2) - sq(opp)) / (adj_1 * adj_2 * 2.0))
}
fn sas(left: f32, right: f32, opp: f32) -> f32 {
// a = sqrt(b^2 + c^2 - 2*b*c*cos(A))
(sq(left) + sq(right) - left * right * deg_cos(opp) * 2.0).sqrt()
}
fn asa(s1: f32, a1: f32, a2: f32) -> f32 {
s1 * deg_sin(a2) / deg_sin(a1)
}
fn no_ambi_ssa(side_1: f32, side_2: f32, ang_side_1: f32) -> f32 {
// asin(c / b * sin(A))
deg_asin((side_2 / side_1) * deg_sin(ang_side_1))
}
#[inline]
fn sq(x: f32) -> f32 { x.powi(2) }
#[inline]
fn wrap(i: int, max: uint) -> uint { (max as int + i) as uint % max }
fn count_knowns(vec: &[Option<f32>]) -> uint {
vec.iter().filter(|x| x.is_some()).count()
}
fn main() {
println!("Enter triangles in the following format: a b c A B C Using underscores for unknowns.");
print!("> ");
for line in std::io::stdio::stdin().lines() {
let mut tri = parse_tri(line.unwrap().as_slice());
if !tri.solve() {
println!("Could not solve triangle!");
} else {
tri.print()
}
print!("> ");
}
}
fn parse_tri(line: &str) -> Tri {
let vals: Vec<Option<f32>> = line.words().map(|st| from_str::<f32>(st)).collect();
if vals.len() != 6{
println!("Missing or extra values in triangle!");
}
Tri::from_vec(vals.as_slice())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment