Решение на (Floating) Points and Vectors от Милен Дончев

Обратно към всички решения

Към профила на Милен Дончев

Резултати

  • 16 точки от тестове
  • 0 бонус точки
  • 16 точки общо
  • 12 успешни тест(а)
  • 3 неуспешни тест(а)

Код

use std::ops::*;
pub fn are_equal(a: f64, b: f64) -> bool {
let res = a.abs() - b.abs();
res.abs() < std::f64::EPSILON
}
pub fn distance_between_points(a: Point, b: Point) -> f64 {
let first = (a.x - b.x) * (a.x - b.x);
let second = (a.y - b.y) * (a.y - b.y);
let third = (a.z - b.z) * (a.z - b.z);
let sum = first + second + third;
sum.sqrt()
}
#[derive(Debug, Clone, Copy)]
pub struct Point {
pub x: f64,
pub y: f64,
pub z: f64
}
impl Point {
pub fn new(x: f64, y: f64, z: f64) -> Self {
Point { x: x, y: y, z: z }
}
}
impl PartialEq for Point {
fn eq(&self, other: &Point) -> bool {
let a = are_equal(self.x, other.x) && are_equal(other.x, self.x);
let b = are_equal(self.y, other.y) && are_equal(other.y, self.y);
let c = are_equal(self.z, other.z) && are_equal(other.z, self.z);
a && b && c
}
}
impl PartialEq for Vector {
fn eq(&self, other: &Vector) -> bool {
self.point == other.point
}
}
// Добавяне на вектор към точка
impl Add<Vector> for Point {
type Output = Point;
fn add(self, other: Vector) -> Point {
Point {
x: self.x + other.point.x,
y: self.y + other.point.y,
z: self.z + other.point.z
}
}
}
// Добавяне на точка към вектор
impl Add<Point> for Vector {
type Output = Point;
fn add(self, other: Point) -> Point {
Point {
x: self.point.x + other.x,
y: self.point.y + other.y,
z: self.point.z + other.z
}
}
}
// Изваждане на точка от точка дава вектор
impl Sub<Point> for Point {
type Output = Vector;
fn sub(self, other: Point) -> Vector {
let r_x = self.x - other.x;
let r_y = self.y - other.y;
let r_z = self.z - other.z;
Vector {
point: Point::new(r_x, r_y, r_z)
}
}
}
// Добавяне на вектор към вектор
impl Add<Vector> for Vector {
type Output = Vector;
fn add(self, other: Vector) -> Vector {
Vector {
point: self + other.point
}
}
}
// Умножаване на вектор с вектор дава число
impl Mul<Vector> for f64 {
type Output = Vector;
fn mul(self, other: Vector) -> Vector {
let r_x = other.point.x * self;
let r_y = other.point.y * self;
let r_z = other.point.z * self;
Vector {
point: Point::new(r_x, r_y, r_z)
}
}
}
// (Скаларно) умножение на вектор по вектор дава число
impl Mul<Vector> for Vector {
type Output = f64;
fn mul(self, other: Vector) -> f64 {
let r_x = other.point.x * self.point.x;
let r_y = other.point.y * self.point.y;
let r_z = other.point.z * self.point.z;
r_x + r_y + r_z
}
}
// Векторно умножение на вектор по вектор дава вектор
impl BitXor<Vector> for Vector {
type Output = Vector;
fn bitxor(self, other: Vector) -> Vector {
let p1 = self.point;
let p2 = other.point;
let r_x = p1.y * p2.z - p1.z * p2.y;
let r_y = p1.z * p2.x - p1.x * p2.z;
let r_z = p1.x * p2.y - p1.y * p2.x;
Vector::new(r_x, r_y, r_z)
}
}
#[derive(Debug, Clone, Copy)]
pub struct Vector {
pub point: Point
}
impl Vector {
pub fn new(x: f64, y: f64, z: f64) -> Self {
Vector { point: Point::new(x, y, z) }
}
}
#[derive(Debug)]
pub struct Line {
pub start: Point,
pub end: Point
}
impl Line {
/// Конструиране на линия през две точки, които минават през нея. Две различни точки са
/// достатъчни, за да дефинират еднозначно линия.
///
/// Можете да получите точка и вектор, като извадите едната от другата точка.
///
/// Ако точките са една и съща, очакваме да върнете None.
///
pub fn from_pp(p1: Point, p2: Point) -> Option<Self> {
if p1 == p2 {
return None;
}
Some(Line { start: p1, end: p2 })
}
/// Конструиране на линия през точка за начало, и вектор, който определя посоката. Стига
/// вектора да е ненулев, това е достатъчно, за да дефинира еднозначно линия.
///
/// Може да получите две точки, като съберете дадената с вектора.
///
/// Ако вектора е нулев, очакваме да върнете None.
///
pub fn from_pv(p: Point, v: Vector) -> Option<Self> {
if v == Vector::new(0.0, 0.0, 0.0) {
return None;
}
let res:Point = p + v;
Some(Line { start: p, end: res })
}
// https://math.stackexchange.com/questions/1905533/find-perpendicular-distance-from-point-to-line-in-3d#answer-1905718
pub fn distance(&self, target: Point) -> f64 {
let a = target;
let b = self.start;
let c = self.end;
let ab: f64 = distance_between_points(a, b);
let bc: f64 = distance_between_points(b, c);
let ac: f64 = distance_between_points(a, c);
let diff: f64 = ab * ab - ac * ac;
let hb: f64 = (bc + diff/bc) / 2.0;
let ah_square = ab * ab - hb * hb;
let distance = ah_square.sqrt();
distance
}

Имаш три теста, които fail-ват, и ми е малко трудно да дебъгна защо, но имам някакво обещаващо предположение. Виж този тест:

let p1 = p!(0.0, 0.0, 0.0);
let p3 = p!(-0.1, 0.3, 0.25);

assert_eq!(Line::from_pp(p1, p2), Line::from_pv(p1, p2 - p1));
assert_eq!(Line::from_pp(p1, p2), Line::from_pv(p1, p1 - p2));

Първия минава, втория не. Мисля, че е понеже в това решение, което си link-нал се казва "hence ABC is an acute triangle (since AB2<AC2+BC2 and so on)". Тоест, това е решение, което работи за определени assumption-и. Няма да задълбавам в него, защото ме мързи, но ако искаш експериментирай с конкретни числа и мисля, че ще откриеш проблем.

}
impl PartialEq for Line {
fn eq(&self, other: &Line) -> bool {
let first_distance = self.distance(other.start);
let second_distance = self.distance(other.end);
let is_first_zero = are_equal(first_distance, 0.0);
let is_second_zero = are_equal(second_distance, 0.0);
is_first_zero && is_second_zero
}
}

Лог от изпълнението

Compiling solution v0.1.0 (/tmp/d20190123-22631-1nsad4a/solution)
    Finished dev [unoptimized + debuginfo] target(s) in 4.34s
     Running target/debug/deps/solution-2e785d603b538f71

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

     Running target/debug/deps/solution_test-29808948fb50ed3a

running 15 tests
test solution_test::test_equailty_symmetry ... ok
test solution_test::test_equality_basic ... ok
test solution_test::test_equality_floating ... ok
test solution_test::test_line_constructors ... ok
test solution_test::test_line_equality_by_points ... FAILED
test solution_test::test_line_equality_by_points_and_vectors ... FAILED
test solution_test::test_line_equality_by_vectors ... FAILED
test solution_test::test_line_validity ... ok
test solution_test::test_number_by_vector ... ok
test solution_test::test_number_vector_multiplication_with_precision ... ok
test solution_test::test_point_distance ... ok
test solution_test::test_points_minus_points ... ok
test solution_test::test_points_plus_vectors ... ok
test solution_test::test_vector_by_vector ... ok
test solution_test::test_vector_by_vector_cross ... ok

failures:

---- solution_test::test_line_equality_by_points stdout ----
thread 'solution_test::test_line_equality_by_points' panicked at 'assertion failed: `(left == right)`
  left: `Some(Line { start: Point { x: 0.0, y: 0.0, z: 0.0 }, end: Point { x: 0.1, y: 0.1, z: 0.1 } })`,
 right: `Some(Line { start: Point { x: -0.1, y: -0.1, z: -0.1 }, end: Point { x: 0.0, y: 0.0, z: 0.0 } })`', tests/solution_test.rs:212:5
note: Run with `RUST_BACKTRACE=1` for a backtrace.

---- solution_test::test_line_equality_by_points_and_vectors stdout ----
thread 'solution_test::test_line_equality_by_points_and_vectors' panicked at 'assertion failed: `(left == right)`
  left: `Some(Line { start: Point { x: 0.0, y: 0.0, z: 0.0 }, end: Point { x: 0.3, y: -0.1, z: 0.25 } })`,
 right: `Some(Line { start: Point { x: 0.0, y: 0.0, z: 0.0 }, end: Point { x: -0.3, y: 0.1, z: -0.25 } })`', tests/solution_test.rs:244:5

---- solution_test::test_line_equality_by_vectors stdout ----
thread 'solution_test::test_line_equality_by_vectors' panicked at 'assertion failed: `(left == right)`
  left: `Some(Line { start: Point { x: 0.0, y: 0.4, z: 0.0 }, end: Point { x: 0.1, y: 0.2, z: 0.5 } })`,
 right: `Some(Line { start: Point { x: 0.0, y: 0.4, z: 0.0 }, end: Point { x: 0.03333333333333333, y: 0.33333333333333337, z: 0.16666666666666666 } })`', tests/solution_test.rs:227:5


failures:
    solution_test::test_line_equality_by_points
    solution_test::test_line_equality_by_points_and_vectors
    solution_test::test_line_equality_by_vectors

test result: FAILED. 12 passed; 3 failed; 0 ignored; 0 measured; 0 filtered out

error: test failed, to rerun pass '--test solution_test'

История (2 версии и 2 коментара)

Милен качи първо решение на 24.11.2018 00:49 (преди почти 7 години)

Милен качи решение на 26.11.2018 16:17 (преди почти 7 години)

use std::ops::*;
pub fn are_equal(a: f64, b: f64) -> bool {
- a.abs() - b < std::f64::EPSILON
+ let res = a.abs() - b.abs();
+ res.abs() < std::f64::EPSILON
}
pub fn distance_between_points(a: Point, b: Point) -> f64 {
let first = (a.x - b.x) * (a.x - b.x);
let second = (a.y - b.y) * (a.y - b.y);
let third = (a.z - b.z) * (a.z - b.z);
let sum = first + second + third;
sum.sqrt()
}
#[derive(Debug, Clone, Copy)]
pub struct Point {
pub x: f64,
pub y: f64,
pub z: f64
}
impl Point {
pub fn new(x: f64, y: f64, z: f64) -> Self {
Point { x: x, y: y, z: z }
}
}
impl PartialEq for Point {
fn eq(&self, other: &Point) -> bool {
let a = are_equal(self.x, other.x) && are_equal(other.x, self.x);
let b = are_equal(self.y, other.y) && are_equal(other.y, self.y);
let c = are_equal(self.z, other.z) && are_equal(other.z, self.z);
a && b && c
}
}
impl PartialEq for Vector {
fn eq(&self, other: &Vector) -> bool {
self.point == other.point
}
}
// Добавяне на вектор към точка
impl Add<Vector> for Point {
type Output = Point;
fn add(self, other: Vector) -> Point {
Point {
x: self.x + other.point.x,
y: self.y + other.point.y,
z: self.z + other.point.z
}
}
}
// Добавяне на точка към вектор
impl Add<Point> for Vector {
type Output = Point;
fn add(self, other: Point) -> Point {
Point {
x: self.point.x + other.x,
y: self.point.y + other.y,
z: self.point.z + other.z
}
}
}
// Изваждане на точка от точка дава вектор
impl Sub<Point> for Point {
type Output = Vector;
fn sub(self, other: Point) -> Vector {
let r_x = self.x - other.x;
let r_y = self.y - other.y;
let r_z = self.z - other.z;
Vector {
point: Point::new(r_x, r_y, r_z)
}
}
}
// Добавяне на вектор към вектор
impl Add<Vector> for Vector {
type Output = Vector;
fn add(self, other: Vector) -> Vector {
Vector {
point: self + other.point
}
}
}
-// Добавяне на вектор към вектор дава число
+// Умножаване на вектор с вектор дава число
impl Mul<Vector> for f64 {
type Output = Vector;
fn mul(self, other: Vector) -> Vector {
let r_x = other.point.x * self;
let r_y = other.point.y * self;
let r_z = other.point.z * self;
Vector {
point: Point::new(r_x, r_y, r_z)
}
}
}
// (Скаларно) умножение на вектор по вектор дава число
impl Mul<Vector> for Vector {
type Output = f64;
fn mul(self, other: Vector) -> f64 {
let r_x = other.point.x * self.point.x;
let r_y = other.point.y * self.point.y;
let r_z = other.point.z * self.point.z;
r_x + r_y + r_z
}
}
// Векторно умножение на вектор по вектор дава вектор
impl BitXor<Vector> for Vector {
type Output = Vector;
fn bitxor(self, other: Vector) -> Vector {
let p1 = self.point;
let p2 = other.point;
let r_x = p1.y * p2.z - p1.z * p2.y;
let r_y = p1.z * p2.x - p1.x * p2.z;
let r_z = p1.x * p2.y - p1.y * p2.x;
Vector::new(r_x, r_y, r_z)
}
}
#[derive(Debug, Clone, Copy)]
pub struct Vector {
pub point: Point
}
impl Vector {
pub fn new(x: f64, y: f64, z: f64) -> Self {
Vector { point: Point::new(x, y, z) }
}
}
#[derive(Debug)]
pub struct Line {
pub start: Point,
pub end: Point
}
impl Line {
/// Конструиране на линия през две точки, които минават през нея. Две различни точки са
/// достатъчни, за да дефинират еднозначно линия.
///
/// Можете да получите точка и вектор, като извадите едната от другата точка.
///
/// Ако точките са една и съща, очакваме да върнете None.
///
pub fn from_pp(p1: Point, p2: Point) -> Option<Self> {
if p1 == p2 {
return None;
}
Some(Line { start: p1, end: p2 })
}
/// Конструиране на линия през точка за начало, и вектор, който определя посоката. Стига
/// вектора да е ненулев, това е достатъчно, за да дефинира еднозначно линия.
///
/// Може да получите две точки, като съберете дадената с вектора.
///
/// Ако вектора е нулев, очакваме да върнете None.
///
pub fn from_pv(p: Point, v: Vector) -> Option<Self> {
if v == Vector::new(0.0, 0.0, 0.0) {
return None;
}
let res:Point = p + v;
Some(Line { start: p, end: res })
}
// https://math.stackexchange.com/questions/1905533/find-perpendicular-distance-from-point-to-line-in-3d#answer-1905718
pub fn distance(&self, target: Point) -> f64 {
let a = target;
let b = self.start;
let c = self.end;
let ab: f64 = distance_between_points(a, b);
let bc: f64 = distance_between_points(b, c);
let ac: f64 = distance_between_points(a, c);
let diff: f64 = ab * ab - ac * ac;
let hb: f64 = (bc + diff/bc) / 2.0;
let ah_square = ab * ab - hb * hb;
let distance = ah_square.sqrt();
distance
}

Имаш три теста, които fail-ват, и ми е малко трудно да дебъгна защо, но имам някакво обещаващо предположение. Виж този тест:

let p1 = p!(0.0, 0.0, 0.0);
let p3 = p!(-0.1, 0.3, 0.25);

assert_eq!(Line::from_pp(p1, p2), Line::from_pv(p1, p2 - p1));
assert_eq!(Line::from_pp(p1, p2), Line::from_pv(p1, p1 - p2));

Първия минава, втория не. Мисля, че е понеже в това решение, което си link-нал се казва "hence ABC is an acute triangle (since AB2<AC2+BC2 and so on)". Тоест, това е решение, което работи за определени assumption-и. Няма да задълбавам в него, защото ме мързи, но ако искаш експериментирай с конкретни числа и мисля, че ще откриеш проблем.

}
impl PartialEq for Line {
fn eq(&self, other: &Line) -> bool {
let first_distance = self.distance(other.start);
let second_distance = self.distance(other.end);
let is_first_zero = are_equal(first_distance, 0.0);
- let is_second_zero = are_equal(first_distance, 0.0);
+ let is_second_zero = are_equal(second_distance, 0.0);
is_first_zero && is_second_zero
}
}