Skip to content

Commit

Permalink
Strongly simplified trimming strokes #19
Browse files Browse the repository at this point in the history
  • Loading branch information
aeplay committed Dec 4, 2016
1 parent 560d6c4 commit fe1e03a
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 108 deletions.
101 changes: 40 additions & 61 deletions src/game/lanes_and_cars/planning/plan_result_steps.rs
Expand Up @@ -46,70 +46,49 @@ fn find_intersection_points(strokes: &CVec<RoadStroke>) -> Vec<P2> {

#[inline(never)]
pub fn trim_strokes_and_add_incoming_outgoing(strokes: &CVec<RoadStroke>, intersections: &mut CVec<Intersection>) -> CVec<RoadStroke> {
let mut strokes_todo = strokes.iter().cloned().enumerate().map(|(i, stroke)| (RoadStrokeRef(i), stroke)).collect::<Vec<_>>();
let mut trimming_ongoing = true;
let mut iters = 0;

while trimming_ongoing {
trimming_ongoing = false;
let new_strokes = strokes_todo.iter().flat_map(|&(stroke_ref, ref stroke)| {
let stroke_path = stroke.path();
let mut maybe_trimmed_strokes = intersections.iter_mut().filter_map(|intersection| {
let intersection_points = (stroke_path, &intersection.shape).intersect();
if intersection_points.len() >= 2 {
let entry_distance = intersection_points.iter().map(|p| OrderedFloat(p.along_a)).min().unwrap();
let exit_distance = intersection_points.iter().map(|p| OrderedFloat(p.along_a)).max().unwrap();
intersection.incoming.insert(stroke_ref, RoadStrokeNode{
position: stroke_path.along(*entry_distance),
direction: stroke_path.direction_along(*entry_distance)
});
intersection.outgoing.insert(stroke_ref, RoadStrokeNode{
position: stroke_path.along(*exit_distance),
direction: stroke_path.direction_along(*exit_distance)
});
match (stroke.cut_before(*entry_distance - 0.05), stroke.cut_after(*exit_distance + 0.05)) {
(Some(before_intersection), Some(after_intersection)) =>
Some(vec![(stroke_ref, before_intersection), (stroke_ref, after_intersection)]),
(Some(before_intersection), None) =>
Some(vec![(stroke_ref, before_intersection)]),
(None, Some(after_intersection)) =>
Some(vec![(stroke_ref, after_intersection)]),
(None, None) => None
}
} else if intersection_points.len() == 1 {
if intersection.shape.contains(stroke.nodes()[0].position) {
let exit_distance = intersection_points[0].along_a;
if let Some(after_intersection) = stroke.cut_after(exit_distance + 0.05) {
intersection.outgoing.insert(stroke_ref, after_intersection.nodes()[0]);
Some(vec![(stroke_ref, after_intersection)])
} else {None}
} else if intersection.shape.contains(stroke.nodes().last().unwrap().position) {
let entry_distance = intersection_points[0].along_a;
if let Some(before_intersection) = stroke.cut_before(entry_distance - 0.05) {
intersection.incoming.insert(stroke_ref, *before_intersection.nodes().last().unwrap());
Some(vec![(stroke_ref, before_intersection)])
} else {None}
} else {None}
} else {None}
});

match maybe_trimmed_strokes.next() {
Some(trimmed_strokes) => {
trimming_ongoing = true;
trimmed_strokes
},
None => vec![(stroke_ref, stroke.clone())]
let strokes = strokes.iter().cloned().enumerate().map(|(i, stroke)| (RoadStrokeRef(i), stroke)).collect::<Vec<_>>();

strokes.iter().flat_map(|&(stroke_ref, ref stroke)| {
let path = stroke.path();
let mut start_trim = 0.0f32;
let mut end_trim = path.length();
let mut cuts = Vec::new();

for ref mut intersection in intersections.iter_mut() {
let intersection_points = (path, &intersection.shape).intersect();
if intersection_points.len() >= 2 {
let entry_distance = intersection_points.iter().map(|p| OrderedFloat(p.along_a)).min().unwrap();
let exit_distance = intersection_points.iter().map(|p| OrderedFloat(p.along_a)).max().unwrap();
intersection.incoming.insert(stroke_ref, RoadStrokeNode{
position: path.along(*entry_distance),
direction: path.direction_along(*entry_distance)
});
intersection.outgoing.insert(stroke_ref, RoadStrokeNode{
position: path.along(*exit_distance),
direction: path.direction_along(*exit_distance)
});
cuts.push((*entry_distance, *exit_distance));
} else if intersection_points.len() == 1 {
if intersection.shape.contains(stroke.nodes()[0].position) {
let exit_distance = intersection_points[0].along_a;
start_trim = start_trim.max(exit_distance);
} else if intersection.shape.contains(stroke.nodes().last().unwrap().position) {
let entry_distance = intersection_points[0].along_a;
end_trim = end_trim.min(entry_distance);
}
}
}).collect::<Vec<_>>();

strokes_todo = new_strokes;
iters += 1;
if iters > 20 {
panic!("Stuck!!!")
}
}

strokes_todo.into_iter().map(|(_, stroke)| stroke).collect()
cuts.sort_by(|a, b| OrderedFloat(a.0).cmp(&OrderedFloat(b.0)));

cuts.insert(0, (-1.0, start_trim));
cuts.push((end_trim, path.length() + 1.0));

cuts.windows(2).filter_map(|two_cuts| {
let ((_, exit_distance), (entry_distance, _)) = (two_cuts[0], two_cuts[1]);
stroke.subsection(exit_distance, entry_distance)
}).collect::<Vec<_>>()
}).collect()
}

#[inline(never)]
Expand Down
60 changes: 13 additions & 47 deletions src/game/lanes_and_cars/planning/road_stroke.rs
Expand Up @@ -64,56 +64,22 @@ impl RoadStroke {
})
}

// TODO: this is really ugly
pub fn cut_before(&self, offset: N) -> Option<Self> {
// TODO: this is slightly ugly
pub fn subsection(&self, start: N, end: N) -> Option<Self> {
let path = self.path();
if let Some(cut_path) = path.subsection(0.0, offset) {
let mut new_nodes = self.nodes.iter().filter(|node|
cut_path.segments().iter().any(|segment|
segment.start().is_roughly_within(node.position, 0.1) || segment.end().is_roughly_within(node.position, 0.1)
)
).cloned().collect::<CVec<_>>();

if new_nodes.is_empty() {
None
} else {
let new_position = path.along(offset);
if new_nodes.last().unwrap().position.is_roughly_within(new_position, MIN_NODE_DISTANCE) {
if new_nodes.len() == 1 {None}
else {Some(RoadStroke::new(new_nodes))}
} else {
new_nodes.push(RoadStrokeNode{
position: new_position, direction: path.direction_along(offset)
});
Some(RoadStroke::new(new_nodes))
if let Some(cut_path) = path.subsection(start, end) {
let nodes = cut_path.segments().iter().map(|segment|
RoadStrokeNode{
position: segment.start(),
direction: segment.start_direction()
}
}
} else {None}
}

pub fn cut_after(&self, offset: N) -> Option<Self> {
let path = self.path();
if let Some(cut_path) = path.subsection(offset, path.length()) {
let mut new_nodes = self.nodes.iter().filter(|node|
cut_path.segments().iter().any(|segment|
segment.start().is_roughly_within(node.position, 0.1) || segment.end().is_roughly_within(node.position, 0.1)
)
).cloned().collect::<CVec<_>>();

if new_nodes.is_empty() {
None
} else {
let new_position = path.along(offset);
if new_nodes[0].position.is_roughly_within(new_position, MIN_NODE_DISTANCE) {
if new_nodes.len() == 1 {None}
else {Some(RoadStroke::new(new_nodes))}
} else {
new_nodes.insert(0, RoadStrokeNode{
position: new_position, direction: path.direction_along(offset)
});
Some(RoadStroke::new(new_nodes))
).chain(cut_path.segments().last().map(|last_segment|
RoadStrokeNode{
position: last_segment.end(),
direction: last_segment.end_direction()
}
}
).into_iter()).collect();
Some(RoadStroke::new(nodes))
} else {None}
}

Expand Down

0 comments on commit fe1e03a

Please sign in to comment.