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::input::UserInput;
use ezgui::GfxCtx;
use geom;
use geom::{PolyLine, Pt2D};
use geom::{PolyLine, Polygon, Pt2D};
use graphics;
use graphics::types::Color;
use gui;
@ -307,7 +306,7 @@ impl UI {
Pt2D::new(947.7612927256201, 765.1100512564725), // 5
];
//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);
}

View File

@ -3,7 +3,7 @@ use ezgui::input::UserInput;
use generator;
use geo;
use geo::prelude::Intersects;
use geom::Pt2D;
use geom::{Polygon, Pt2D};
use graphics::math::Vec2d;
use map_model::{geometry, BuildingID, IntersectionID, LaneID, Map, ParcelID};
use piston::input::Key;
@ -42,13 +42,13 @@ impl Validator {
));
}
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 {
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 {
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!(
@ -139,6 +139,14 @@ fn make_poly(points: &Vec<Vec2d>) -> geo::Polygon<f64> {
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?
fn get_pt(map: &Map, id: ID) -> Pt2D {
match id {

View File

@ -2,15 +2,12 @@
use aabb_quadtree::geom::Rect;
use ezgui::GfxCtx;
use geom;
use geom::PolyLine;
use geom::{PolyLine, Polygon, Pt2D};
use graphics;
use graphics::math::Vec2d;
use graphics::types::Color;
use map_model;
use map_model::geometry;
use map_model::{BuildingID, Map};
use render::PARCEL_BOUNDARY_THICKNESS;
use map_model::{Building, BuildingID, Map};
use render::{get_bbox, PARCEL_BOUNDARY_THICKNESS};
use std::f64;
#[derive(Debug)]
@ -18,22 +15,19 @@ pub struct DrawBuilding {
pub id: BuildingID,
// TODO should just have one. use graphics::Line for now.
boundary_polygons: Vec<Vec<Vec2d>>,
// TODO rm the other one
pub fill_polygon: Vec<Vec2d>,
fill_triangles: Vec<Vec<Vec2d>>,
pub fill_polygon: Polygon,
front_path: Option<[f64; 4]>,
}
impl DrawBuilding {
pub fn new(bldg: &map_model::Building) -> DrawBuilding {
pub fn new(bldg: &Building) -> DrawBuilding {
DrawBuilding {
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
front_path: bldg.front_path
.as_ref()
.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())
.make_polygons_blindly(PARCEL_BOUNDARY_THICKNESS),
}
@ -54,13 +48,13 @@ impl DrawBuilding {
for p in &self.boundary_polygons {
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);
}
}
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> {
@ -76,10 +70,11 @@ impl DrawBuilding {
}
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 {
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 dimensioned::si;
use ezgui::GfxCtx;
use geom::{Line, Pt2D};
use geom::{Line, Polygon, Pt2D};
use graphics;
use graphics::math::Vec2d;
use graphics::types::Color;
use map_model;
use map_model::geometry;
use render::DrawLane;
use render::{get_bbox, DrawLane};
use std::f64;
#[derive(Debug)]
pub struct DrawIntersection {
pub id: map_model::IntersectionID,
pub polygon: Vec<Vec2d>,
pub polygon: Polygon,
crosswalks: Vec<Vec<(Vec2d, Vec2d)>>,
center: Pt2D,
has_traffic_signal: bool,
@ -28,39 +28,37 @@ impl DrawIntersection {
map: &map_model::Map,
lanes: &Vec<DrawLane>,
) -> DrawIntersection {
let mut pts: Vec<Vec2d> = Vec::new();
let mut pts: Vec<Pt2D> = Vec::new();
for l in &inter.incoming_lanes {
let (pt1, pt2) = lanes[l.0].get_end_crossing();
pts.push(pt1);
pts.push(pt2);
let line = lanes[l.0].get_end_crossing();
pts.push(line.pt1());
pts.push(line.pt2());
}
for l in &inter.outgoing_lanes {
let (pt1, pt2) = lanes[l.0].get_start_crossing();
pts.push(pt1);
pts.push(pt2);
let line = lanes[l.0].get_start_crossing();
pts.push(line.pt1());
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
pts.sort_by_key(|pt| {
center
.angle_to(Pt2D::new(pt[0], pt[1]))
.normalized_degrees() as i64
});
pts.sort_by_key(|pt| center.angle_to(*pt).normalized_degrees() as i64);
let first_pt = pts[0].clone();
pts.push(first_pt);
DrawIntersection {
center,
id: inter.id,
polygon: pts,
polygon: Polygon::new(&pts),
crosswalks: calculate_crosswalks(inter, map),
has_traffic_signal: inter.has_traffic_signal,
}
}
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(
cs.get(Colors::Crosswalk),
@ -84,11 +82,11 @@ impl DrawIntersection {
}
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 {
geometry::get_bbox_for_polygons(&[self.polygon.clone()])
get_bbox(&self.polygon.get_bounds())
}
fn draw_stop_sign(&self, g: &mut GfxCtx, cs: &ColorScheme) {

View File

@ -25,8 +25,8 @@ struct Marking {
pub struct DrawLane {
pub id: LaneID,
pub polygons: Vec<Vec<Vec2d>>,
start_crossing: (Vec2d, Vec2d),
end_crossing: (Vec2d, Vec2d),
start_crossing: Line,
end_crossing: Line,
markings: Vec<Marking>,
// TODO pretty temporary
@ -36,9 +36,8 @@ pub struct DrawLane {
impl DrawLane {
pub fn new(lane: &map_model::Lane, map: &map_model::Map) -> DrawLane {
let road = map.get_r(lane.parent);
let start = perp_line(lane.first_line(), geometry::LANE_THICKNESS);
let end = perp_line(lane.last_line().reverse(), geometry::LANE_THICKNESS);
let start = new_perp_line(lane.first_line(), geometry::LANE_THICKNESS);
let end = new_perp_line(lane.last_line().reverse(), geometry::LANE_THICKNESS);
let polygons = lane.lane_center_pts
.make_polygons_blindly(geometry::LANE_THICKNESS);
@ -76,8 +75,8 @@ impl DrawLane {
id: lane.id,
polygons,
markings,
start_crossing: ([start[0], start[1]], [start[2], start[3]]),
end_crossing: ([end[0], end[1]], [end[2], end[3]]),
start_crossing: start,
end_crossing: end,
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
pub(crate) fn get_end_crossing(&self) -> (Vec2d, Vec2d) {
self.end_crossing
pub(crate) fn get_end_crossing(&self) -> &Line {
&self.end_crossing
}
pub(crate) fn get_start_crossing(&self) -> (Vec2d, Vec2d) {
self.start_crossing
pub(crate) fn get_start_crossing(&self) -> &Line {
&self.start_crossing
}
}
@ -178,6 +177,12 @@ fn perp_line(l: Line, length: f64) -> [f64; 4] {
[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 {
let tile_every = geometry::LANE_THICKNESS * si::M;

View File

@ -7,6 +7,8 @@ mod map;
mod parcel;
mod turn;
use aabb_quadtree::geom::{Point, Rect};
use geom::Bounds;
use map_model::geometry;
pub use render::lane::DrawLane;
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 TURN_ICON_ARROW_TIP_LENGTH: f64 = BIG_ARROW_TIP_LENGTH * 0.8;
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 ezgui::GfxCtx;
use geom;
use geom::PolyLine;
use geom::{PolyLine, Polygon};
use graphics::math::Vec2d;
use graphics::types::Color;
use map_model;
use map_model::geometry;
use render::PARCEL_BOUNDARY_THICKNESS;
use render::{get_bbox, PARCEL_BOUNDARY_THICKNESS};
#[derive(Debug)]
pub struct DrawParcel {
pub id: map_model::ParcelID,
// TODO should just have one. use graphics::Line for now.
boundary_polygons: Vec<Vec<Vec2d>>,
// TODO clean this up
pub fill_polygon: Vec<Vec2d>,
fill_triangles: Vec<Vec<Vec2d>>,
pub fill_polygon: Polygon,
}
impl DrawParcel {
@ -26,8 +22,7 @@ impl DrawParcel {
id: p.id,
boundary_polygons: PolyLine::new(p.points.clone())
.make_polygons_blindly(PARCEL_BOUNDARY_THICKNESS),
fill_polygon: p.points.iter().map(|pt| [pt.x(), pt.y()]).collect(),
fill_triangles: geom::triangulate(&p.points),
fill_polygon: Polygon::new(&p.points),
}
}
@ -35,7 +30,7 @@ impl DrawParcel {
for p in &self.boundary_polygons {
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);
}
}
@ -43,6 +38,6 @@ impl DrawParcel {
//pub fn contains_pt(&self, x: f64, y: f64) -> bool {}
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;
pub use gps::LonLat;
pub use line::Line;
pub use polygon::triangulate;
pub use polygon::Polygon;
pub use polyline::PolyLine;
pub use pt::{HashablePt2D, Pt2D};
use std::marker;

View File

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