making a proper polygon type, finally. using it in a few places, not all

yet
This commit is contained in:
Dustin Carlino 2018-08-08 17:23:41 -07:00
parent 5d2a4f3ac4
commit 1b610aa708
9 changed files with 199 additions and 137 deletions

View File

@ -1,8 +1,7 @@
use ezgui::canvas::Canvas; use ezgui::canvas::Canvas;
use ezgui::input::UserInput; use ezgui::input::UserInput;
use ezgui::GfxCtx; use ezgui::GfxCtx;
use geom; use geom::{PolyLine, Polygon, Pt2D};
use geom::{PolyLine, Pt2D};
use graphics; use graphics;
use graphics::types::Color; use graphics::types::Color;
use gui; use gui;
@ -307,7 +306,7 @@ impl UI {
Pt2D::new(947.7612927256201, 765.1100512564725), // 5 Pt2D::new(947.7612927256201, 765.1100512564725), // 5
]; ];
//draw_polyline(g, &PolyLine::new(pts.clone()), 0.25, RED); //draw_polyline(g, &PolyLine::new(pts.clone()), 0.25, RED);
for tri in geom::triangulate(&pts).iter() { for tri in Polygon::new(&pts).for_drawing().iter() {
g.draw_polygon(BLUE, tri); g.draw_polygon(BLUE, tri);
} }

View File

@ -3,7 +3,7 @@ use ezgui::input::UserInput;
use generator; use generator;
use geo; use geo;
use geo::prelude::Intersects; use geo::prelude::Intersects;
use geom::Pt2D; use geom::{Polygon, Pt2D};
use graphics::math::Vec2d; use graphics::math::Vec2d;
use map_model::{geometry, BuildingID, IntersectionID, LaneID, Map, ParcelID}; use map_model::{geometry, BuildingID, IntersectionID, LaneID, Map, ParcelID};
use piston::input::Key; use piston::input::Key;
@ -42,13 +42,13 @@ impl Validator {
)); ));
} }
for i in &draw_map.intersections { for i in &draw_map.intersections {
objects.push((ID::Intersection(i.id), vec![make_poly(&i.polygon)])); objects.push((ID::Intersection(i.id), vec![make_new_poly(&i.polygon)]));
} }
for b in &draw_map.buildings { for b in &draw_map.buildings {
objects.push((ID::Building(b.id), vec![make_poly(&b.fill_polygon)])); objects.push((ID::Building(b.id), vec![make_new_poly(&b.fill_polygon)]));
} }
for p in &draw_map.parcels { for p in &draw_map.parcels {
objects.push((ID::Parcel(p.id), vec![make_poly(&p.fill_polygon)])); objects.push((ID::Parcel(p.id), vec![make_new_poly(&p.fill_polygon)]));
} }
println!( println!(
@ -139,6 +139,14 @@ fn make_poly(points: &Vec<Vec2d>) -> geo::Polygon<f64> {
geo::Polygon::new(exterior.into(), Vec::new()) geo::Polygon::new(exterior.into(), Vec::new())
} }
fn make_new_poly(p: &Polygon) -> geo::Polygon<f64> {
let exterior: Vec<geo::Point<f64>> = p.pts
.iter()
.map(|pt| geo::Point::new(pt.x(), pt.y()))
.collect();
geo::Polygon::new(exterior.into(), Vec::new())
}
// TODO duplicated with warp. generic handling of object types? // TODO duplicated with warp. generic handling of object types?
fn get_pt(map: &Map, id: ID) -> Pt2D { fn get_pt(map: &Map, id: ID) -> Pt2D {
match id { match id {

View File

@ -2,15 +2,12 @@
use aabb_quadtree::geom::Rect; use aabb_quadtree::geom::Rect;
use ezgui::GfxCtx; use ezgui::GfxCtx;
use geom; use geom::{PolyLine, Polygon, Pt2D};
use geom::PolyLine;
use graphics; use graphics;
use graphics::math::Vec2d; use graphics::math::Vec2d;
use graphics::types::Color; use graphics::types::Color;
use map_model; use map_model::{Building, BuildingID, Map};
use map_model::geometry; use render::{get_bbox, PARCEL_BOUNDARY_THICKNESS};
use map_model::{BuildingID, Map};
use render::PARCEL_BOUNDARY_THICKNESS;
use std::f64; use std::f64;
#[derive(Debug)] #[derive(Debug)]
@ -18,22 +15,19 @@ pub struct DrawBuilding {
pub id: BuildingID, pub id: BuildingID,
// TODO should just have one. use graphics::Line for now. // TODO should just have one. use graphics::Line for now.
boundary_polygons: Vec<Vec<Vec2d>>, boundary_polygons: Vec<Vec<Vec2d>>,
// TODO rm the other one pub fill_polygon: Polygon,
pub fill_polygon: Vec<Vec2d>,
fill_triangles: Vec<Vec<Vec2d>>,
front_path: Option<[f64; 4]>, front_path: Option<[f64; 4]>,
} }
impl DrawBuilding { impl DrawBuilding {
pub fn new(bldg: &map_model::Building) -> DrawBuilding { pub fn new(bldg: &Building) -> DrawBuilding {
DrawBuilding { DrawBuilding {
id: bldg.id, id: bldg.id,
fill_polygon: bldg.points.iter().map(|pt| [pt.x(), pt.y()]).collect(),
// TODO ideally start the path on a side of the building // TODO ideally start the path on a side of the building
front_path: bldg.front_path front_path: bldg.front_path
.as_ref() .as_ref()
.map(|l| [l.pt1().x(), l.pt1().y(), l.pt2().x(), l.pt2().y()]), .map(|l| [l.pt1().x(), l.pt1().y(), l.pt2().x(), l.pt2().y()]),
fill_triangles: geom::triangulate(&bldg.points), fill_polygon: Polygon::new(&bldg.points),
boundary_polygons: PolyLine::new(bldg.points.clone()) boundary_polygons: PolyLine::new(bldg.points.clone())
.make_polygons_blindly(PARCEL_BOUNDARY_THICKNESS), .make_polygons_blindly(PARCEL_BOUNDARY_THICKNESS),
} }
@ -54,13 +48,13 @@ impl DrawBuilding {
for p in &self.boundary_polygons { for p in &self.boundary_polygons {
g.draw_polygon(boundary_color, p); g.draw_polygon(boundary_color, p);
} }
for p in &self.fill_triangles { for p in &self.fill_polygon.for_drawing() {
g.draw_polygon(fill_color, p); g.draw_polygon(fill_color, p);
} }
} }
pub fn contains_pt(&self, x: f64, y: f64) -> bool { pub fn contains_pt(&self, x: f64, y: f64) -> bool {
geometry::point_in_polygon(x, y, &self.fill_polygon) self.fill_polygon.contains_pt(Pt2D::new(x, y))
} }
pub fn tooltip_lines(&self, map: &Map) -> Vec<String> { pub fn tooltip_lines(&self, map: &Map) -> Vec<String> {
@ -76,10 +70,11 @@ impl DrawBuilding {
} }
pub fn get_bbox(&self) -> Rect { pub fn get_bbox(&self) -> Rect {
let mut polygons = vec![self.fill_polygon.clone()]; let mut b = self.fill_polygon.get_bounds();
if let Some(line) = self.front_path { if let Some(line) = self.front_path {
polygons.push(vec![[line[0], line[1]], [line[2], line[3]]]); b.update(line[0], line[1]);
b.update(line[2], line[3]);
} }
geometry::get_bbox_for_polygons(&polygons) get_bbox(&b)
} }
} }

View File

@ -4,19 +4,19 @@ use aabb_quadtree::geom::Rect;
use colors::{ColorScheme, Colors}; use colors::{ColorScheme, Colors};
use dimensioned::si; use dimensioned::si;
use ezgui::GfxCtx; use ezgui::GfxCtx;
use geom::{Line, Pt2D}; use geom::{Line, Polygon, Pt2D};
use graphics; use graphics;
use graphics::math::Vec2d; use graphics::math::Vec2d;
use graphics::types::Color; use graphics::types::Color;
use map_model; use map_model;
use map_model::geometry; use map_model::geometry;
use render::DrawLane; use render::{get_bbox, DrawLane};
use std::f64; use std::f64;
#[derive(Debug)] #[derive(Debug)]
pub struct DrawIntersection { pub struct DrawIntersection {
pub id: map_model::IntersectionID, pub id: map_model::IntersectionID,
pub polygon: Vec<Vec2d>, pub polygon: Polygon,
crosswalks: Vec<Vec<(Vec2d, Vec2d)>>, crosswalks: Vec<Vec<(Vec2d, Vec2d)>>,
center: Pt2D, center: Pt2D,
has_traffic_signal: bool, has_traffic_signal: bool,
@ -28,39 +28,37 @@ impl DrawIntersection {
map: &map_model::Map, map: &map_model::Map,
lanes: &Vec<DrawLane>, lanes: &Vec<DrawLane>,
) -> DrawIntersection { ) -> DrawIntersection {
let mut pts: Vec<Vec2d> = Vec::new(); let mut pts: Vec<Pt2D> = Vec::new();
for l in &inter.incoming_lanes { for l in &inter.incoming_lanes {
let (pt1, pt2) = lanes[l.0].get_end_crossing(); let line = lanes[l.0].get_end_crossing();
pts.push(pt1); pts.push(line.pt1());
pts.push(pt2); pts.push(line.pt2());
} }
for l in &inter.outgoing_lanes { for l in &inter.outgoing_lanes {
let (pt1, pt2) = lanes[l.0].get_start_crossing(); let line = lanes[l.0].get_start_crossing();
pts.push(pt1); pts.push(line.pt1());
pts.push(pt2); pts.push(line.pt2());
} }
let center = geometry::center(&pts.iter().map(|pt| Pt2D::new(pt[0], pt[1])).collect()); let center = geometry::center(&pts);
// Sort points by angle from the center // Sort points by angle from the center
pts.sort_by_key(|pt| { pts.sort_by_key(|pt| center.angle_to(*pt).normalized_degrees() as i64);
center
.angle_to(Pt2D::new(pt[0], pt[1]))
.normalized_degrees() as i64
});
let first_pt = pts[0].clone(); let first_pt = pts[0].clone();
pts.push(first_pt); pts.push(first_pt);
DrawIntersection { DrawIntersection {
center, center,
id: inter.id, id: inter.id,
polygon: pts, polygon: Polygon::new(&pts),
crosswalks: calculate_crosswalks(inter, map), crosswalks: calculate_crosswalks(inter, map),
has_traffic_signal: inter.has_traffic_signal, has_traffic_signal: inter.has_traffic_signal,
} }
} }
pub fn draw(&self, g: &mut GfxCtx, color: Color, cs: &ColorScheme) { pub fn draw(&self, g: &mut GfxCtx, color: Color, cs: &ColorScheme) {
g.draw_polygon(color, &self.polygon); for p in &self.polygon.for_drawing() {
g.draw_polygon(color, p);
}
let crosswalk_marking = graphics::Line::new( let crosswalk_marking = graphics::Line::new(
cs.get(Colors::Crosswalk), cs.get(Colors::Crosswalk),
@ -84,11 +82,11 @@ impl DrawIntersection {
} }
pub fn contains_pt(&self, x: f64, y: f64) -> bool { pub fn contains_pt(&self, x: f64, y: f64) -> bool {
geometry::point_in_polygon(x, y, &self.polygon) self.polygon.contains_pt(Pt2D::new(x, y))
} }
pub fn get_bbox(&self) -> Rect { pub fn get_bbox(&self) -> Rect {
geometry::get_bbox_for_polygons(&[self.polygon.clone()]) get_bbox(&self.polygon.get_bounds())
} }
fn draw_stop_sign(&self, g: &mut GfxCtx, cs: &ColorScheme) { fn draw_stop_sign(&self, g: &mut GfxCtx, cs: &ColorScheme) {

View File

@ -25,8 +25,8 @@ struct Marking {
pub struct DrawLane { pub struct DrawLane {
pub id: LaneID, pub id: LaneID,
pub polygons: Vec<Vec<Vec2d>>, pub polygons: Vec<Vec<Vec2d>>,
start_crossing: (Vec2d, Vec2d), start_crossing: Line,
end_crossing: (Vec2d, Vec2d), end_crossing: Line,
markings: Vec<Marking>, markings: Vec<Marking>,
// TODO pretty temporary // TODO pretty temporary
@ -36,9 +36,8 @@ pub struct DrawLane {
impl DrawLane { impl DrawLane {
pub fn new(lane: &map_model::Lane, map: &map_model::Map) -> DrawLane { pub fn new(lane: &map_model::Lane, map: &map_model::Map) -> DrawLane {
let road = map.get_r(lane.parent); let road = map.get_r(lane.parent);
let start = perp_line(lane.first_line(), geometry::LANE_THICKNESS); let start = new_perp_line(lane.first_line(), geometry::LANE_THICKNESS);
let end = perp_line(lane.last_line().reverse(), geometry::LANE_THICKNESS); let end = new_perp_line(lane.last_line().reverse(), geometry::LANE_THICKNESS);
let polygons = lane.lane_center_pts let polygons = lane.lane_center_pts
.make_polygons_blindly(geometry::LANE_THICKNESS); .make_polygons_blindly(geometry::LANE_THICKNESS);
@ -76,8 +75,8 @@ impl DrawLane {
id: lane.id, id: lane.id,
polygons, polygons,
markings, markings,
start_crossing: ([start[0], start[1]], [start[2], start[3]]), start_crossing: start,
end_crossing: ([end[0], end[1]], [end[2], end[3]]), end_crossing: end,
draw_id_at: calculate_id_positions(lane).unwrap_or(Vec::new()), draw_id_at: calculate_id_positions(lane).unwrap_or(Vec::new()),
} }
} }
@ -161,12 +160,12 @@ impl DrawLane {
} }
// Get the line marking the end of the lane, perpendicular to the direction of the lane // Get the line marking the end of the lane, perpendicular to the direction of the lane
pub(crate) fn get_end_crossing(&self) -> (Vec2d, Vec2d) { pub(crate) fn get_end_crossing(&self) -> &Line {
self.end_crossing &self.end_crossing
} }
pub(crate) fn get_start_crossing(&self) -> (Vec2d, Vec2d) { pub(crate) fn get_start_crossing(&self) -> &Line {
self.start_crossing &self.start_crossing
} }
} }
@ -178,6 +177,12 @@ fn perp_line(l: Line, length: f64) -> [f64; 4] {
[pt1.x(), pt1.y(), pt2.x(), pt2.y()] [pt1.x(), pt1.y(), pt2.x(), pt2.y()]
} }
fn new_perp_line(l: Line, length: f64) -> Line {
let pt1 = l.shift(length / 2.0).pt1();
let pt2 = l.reverse().shift(length / 2.0).pt2();
Line::new(pt1, pt2)
}
fn calculate_sidewalk_lines(lane: &map_model::Lane) -> Marking { fn calculate_sidewalk_lines(lane: &map_model::Lane) -> Marking {
let tile_every = geometry::LANE_THICKNESS * si::M; let tile_every = geometry::LANE_THICKNESS * si::M;

View File

@ -7,6 +7,8 @@ mod map;
mod parcel; mod parcel;
mod turn; mod turn;
use aabb_quadtree::geom::{Point, Rect};
use geom::Bounds;
use map_model::geometry; use map_model::geometry;
pub use render::lane::DrawLane; pub use render::lane::DrawLane;
pub use render::map::DrawMap; pub use render::map::DrawMap;
@ -20,3 +22,16 @@ const TURN_ICON_ARROW_THICKNESS: f64 = geometry::BIG_ARROW_THICKNESS / 3.0;
const BIG_ARROW_TIP_LENGTH: f64 = 1.0; const BIG_ARROW_TIP_LENGTH: f64 = 1.0;
const TURN_ICON_ARROW_TIP_LENGTH: f64 = BIG_ARROW_TIP_LENGTH * 0.8; const TURN_ICON_ARROW_TIP_LENGTH: f64 = BIG_ARROW_TIP_LENGTH * 0.8;
const TURN_ICON_ARROW_LENGTH: f64 = 2.0; const TURN_ICON_ARROW_LENGTH: f64 = 2.0;
pub fn get_bbox(b: &Bounds) -> Rect {
Rect {
top_left: Point {
x: b.min_x as f32,
y: b.min_y as f32,
},
bottom_right: Point {
x: b.max_x as f32,
y: b.max_y as f32,
},
}
}

View File

@ -2,22 +2,18 @@
use aabb_quadtree::geom::Rect; use aabb_quadtree::geom::Rect;
use ezgui::GfxCtx; use ezgui::GfxCtx;
use geom; use geom::{PolyLine, Polygon};
use geom::PolyLine;
use graphics::math::Vec2d; use graphics::math::Vec2d;
use graphics::types::Color; use graphics::types::Color;
use map_model; use map_model;
use map_model::geometry; use render::{get_bbox, PARCEL_BOUNDARY_THICKNESS};
use render::PARCEL_BOUNDARY_THICKNESS;
#[derive(Debug)] #[derive(Debug)]
pub struct DrawParcel { pub struct DrawParcel {
pub id: map_model::ParcelID, pub id: map_model::ParcelID,
// TODO should just have one. use graphics::Line for now. // TODO should just have one. use graphics::Line for now.
boundary_polygons: Vec<Vec<Vec2d>>, boundary_polygons: Vec<Vec<Vec2d>>,
// TODO clean this up pub fill_polygon: Polygon,
pub fill_polygon: Vec<Vec2d>,
fill_triangles: Vec<Vec<Vec2d>>,
} }
impl DrawParcel { impl DrawParcel {
@ -26,8 +22,7 @@ impl DrawParcel {
id: p.id, id: p.id,
boundary_polygons: PolyLine::new(p.points.clone()) boundary_polygons: PolyLine::new(p.points.clone())
.make_polygons_blindly(PARCEL_BOUNDARY_THICKNESS), .make_polygons_blindly(PARCEL_BOUNDARY_THICKNESS),
fill_polygon: p.points.iter().map(|pt| [pt.x(), pt.y()]).collect(), fill_polygon: Polygon::new(&p.points),
fill_triangles: geom::triangulate(&p.points),
} }
} }
@ -35,7 +30,7 @@ impl DrawParcel {
for p in &self.boundary_polygons { for p in &self.boundary_polygons {
g.draw_polygon(boundary_color, p); g.draw_polygon(boundary_color, p);
} }
for p in &self.fill_triangles { for p in &self.fill_polygon.for_drawing() {
g.draw_polygon(fill_color, p); g.draw_polygon(fill_color, p);
} }
} }
@ -43,6 +38,6 @@ impl DrawParcel {
//pub fn contains_pt(&self, x: f64, y: f64) -> bool {} //pub fn contains_pt(&self, x: f64, y: f64) -> bool {}
pub fn get_bbox(&self) -> Rect { pub fn get_bbox(&self) -> Rect {
geometry::get_bbox_for_polygons(&vec![self.fill_polygon.clone()]) get_bbox(&self.fill_polygon.get_bounds())
} }
} }

View File

@ -19,7 +19,7 @@ pub use bounds::Bounds;
use dimensioned::si; use dimensioned::si;
pub use gps::LonLat; pub use gps::LonLat;
pub use line::Line; pub use line::Line;
pub use polygon::triangulate; pub use polygon::Polygon;
pub use polyline::PolyLine; pub use polyline::PolyLine;
pub use pt::{HashablePt2D, Pt2D}; pub use pt::{HashablePt2D, Pt2D};
use std::marker; use std::marker;

View File

@ -1,97 +1,144 @@
use graphics::math::Vec2d; use graphics::math::Vec2d;
use Pt2D; use {Bounds, Pt2D};
// Adapted from https://crates.io/crates/polygon2; couldn't use the crate directly because it #[derive(Debug)]
// depends on nightly. pub struct Polygon {
// TODO urgh, just storing for geom validation. :P
pub pts: Vec<Pt2D>,
pub fn triangulate(pts: &Vec<Pt2D>) -> Vec<Vec<Vec2d>> { // This could be stored more efficiently, but worry about it later when switching to gfx-rs.
assert!(pts.len() >= 3); triangles: Vec<Triangle>,
}
let mut tgs = Vec::new(); impl Polygon {
let mut avl = Vec::with_capacity(pts.len()); // Adapted from https://crates.io/crates/polygon2; couldn't use the crate directly because it
for i in 0..pts.len() { // depends on nightly.
avl.push(i); pub fn new(pts: &Vec<Pt2D>) -> Polygon {
} assert!(pts.len() >= 3);
let mut i = 0; let mut tgs = Vec::new();
let mut al = pts.len(); let mut avl = Vec::with_capacity(pts.len());
while al > 3 { for i in 0..pts.len() {
let i0 = avl[i % al]; avl.push(i);
let i1 = avl[(i + 1) % al]; }
let i2 = avl[(i + 2) % al];
let a = pts[i0]; let mut i = 0;
let b = pts[i1]; let mut al = pts.len();
let c = pts[i2]; while al > 3 {
let i0 = avl[i % al];
let i1 = avl[(i + 1) % al];
let i2 = avl[(i + 2) % al];
let mut ear_found = false; let a = pts[i0];
if is_triangle_convex(a, b, c) { let b = pts[i1];
ear_found = true; let c = pts[i2];
let tri = Triangle::new(a, b, c);
for j in 0..al { let mut ear_found = false;
let vi = avl[j]; if tri.is_convex() {
ear_found = true;
if vi != i0 && vi != i1 && vi != i2 { for j in 0..al {
if point_in_triangle(pts[vi], a, b, c) { let vi = avl[j];
ear_found = false;
break; if vi != i0 && vi != i1 && vi != i2 {
if tri.contains_pt(pts[vi]) {
ear_found = false;
break;
}
} }
} }
} }
if ear_found {
tgs.push(i0);
tgs.push(i1);
tgs.push(i2);
avl.remove((i + 1) % al);
al -= 1;
i = 0;
} else if i > 3 * al {
break;
} else {
i += 1;
}
} }
if ear_found { tgs.push(avl[0]);
tgs.push(i0); tgs.push(avl[1]);
tgs.push(i1); tgs.push(avl[2]);
tgs.push(i2);
avl.remove((i + 1) % al); let mut triangles = Vec::new();
al -= 1; assert!(tgs.len() % 3 == 0);
i = 0; for tri in tgs.chunks(3) {
} else if i > 3 * al { triangles.push(Triangle::new(pts[tri[0]], pts[tri[1]], pts[tri[2]]));
break; }
} else { Polygon {
i += 1; pts: pts.clone(),
triangles,
} }
} }
tgs.push(avl[0]); pub fn for_drawing(&self) -> Vec<Vec<Vec2d>> {
tgs.push(avl[1]); self.triangles
tgs.push(avl[2]); .iter()
.map(|tri| vec![tri.pt1.to_vec(), tri.pt2.to_vec(), tri.pt3.to_vec()])
let mut result = Vec::new(); .collect()
assert!(tgs.len() % 3 == 0);
for tri in tgs.chunks(3) {
result.push(vec![
pts[tri[0]].to_vec(),
pts[tri[1]].to_vec(),
pts[tri[2]].to_vec(),
]);
} }
result pub fn contains_pt(&self, pt: Pt2D) -> bool {
self.triangles
.iter()
.find(|tri| tri.contains_pt(pt))
.is_some()
}
pub fn get_bounds(&self) -> Bounds {
let mut b = Bounds::new();
for tri in &self.triangles {
b.update_pt(&tri.pt1);
b.update_pt(&tri.pt2);
b.update_pt(&tri.pt3);
}
b
}
} }
fn is_triangle_convex(a: Pt2D, b: Pt2D, c: Pt2D) -> bool { #[derive(Debug)]
((a.y() - b.y()) * (c.x() - b.x()) + (b.x() - a.x()) * (c.y() - b.y())) >= 0.0 struct Triangle {
pt1: Pt2D,
pt2: Pt2D,
pt3: Pt2D,
} }
fn point_in_triangle(p: Pt2D, a: Pt2D, b: Pt2D, c: Pt2D) -> bool { impl Triangle {
let v0x = c.x() - a.x(); fn new(pt1: Pt2D, pt2: Pt2D, pt3: Pt2D) -> Triangle {
let v0y = c.y() - a.y(); Triangle { pt1, pt2, pt3 }
let v1x = b.x() - a.x(); }
let v1y = b.y() - a.y();
let v2x = p.x() - a.x();
let v2y = p.y() - a.y();
let dot00 = v0x * v0x + v0y * v0y; fn is_convex(&self) -> bool {
let dot01 = v0x * v1x + v0y * v1y; ((self.pt1.y() - self.pt2.y()) * (self.pt3.x() - self.pt2.x())
let dot02 = v0x * v2x + v0y * v2y; + (self.pt2.x() - self.pt1.x()) * (self.pt3.y() - self.pt2.y())) >= 0.0
let dot11 = v1x * v1x + v1y * v1y; }
let dot12 = v1x * v2x + v1y * v2y;
let denom = dot00 * dot11 - dot01 * dot01; fn contains_pt(&self, pt: Pt2D) -> bool {
let u = (dot11 * dot02 - dot01 * dot12) / denom; let v0x = self.pt3.x() - self.pt1.x();
let v = (dot00 * dot12 - dot01 * dot02) / denom; let v0y = self.pt3.y() - self.pt1.y();
let v1x = self.pt2.x() - self.pt1.x();
let v1y = self.pt2.y() - self.pt1.y();
let v2x = pt.x() - self.pt1.x();
let v2y = pt.y() - self.pt1.y();
(u >= 0.0) && (v >= 0.0) && (u + v < 1.0) let dot00 = v0x * v0x + v0y * v0y;
let dot01 = v0x * v1x + v0y * v1y;
let dot02 = v0x * v2x + v0y * v2y;
let dot11 = v1x * v1x + v1y * v1y;
let dot12 = v1x * v2x + v1y * v2y;
let denom = dot00 * dot11 - dot01 * dot01;
let u = (dot11 * dot02 - dot01 * dot12) / denom;
let v = (dot00 * dot12 - dot01 * dot02) / denom;
(u >= 0.0) && (v >= 0.0) && (u + v < 1.0)
}
} }