00001
00002 #include <unistd.h>
00003 #include <float.h>
00004 #include <vector>
00005 #include <map>
00006
00007 #include <art/epsilon.h>
00008
00009 #include <art_map/ZoneOps.h>
00010 #include <art_map/RNDF.h>
00011 #include <art_map/types.h>
00012 #include <art_map/RNDF.h>
00013 #include <art_map/coordinates.h>
00014 #include <art_map/euclidean_distance.h>
00015 #include <art_map/PolyOps.h>
00016 #include <art_map/Graph.h>
00017
00018 ZoneManager::ZoneManager(const ZonePerimeter &_zone,
00019 float _safety_radius,
00020 float _scale,
00021 int _max_cells,
00022 bool _write_graph,
00023 ElementID _starting_id,
00024 MapXY _lower_left,
00025 MapXY _upper_right) :
00026 starting_id(_starting_id),
00027 write_graph(_write_graph),
00028 zone(_zone),
00029 safety_radius(_safety_radius),
00030 ll(_lower_left),
00031 ur(_upper_right),
00032 scale(fmax(_scale, sqrt((fabs(ur.x-ll.x)*fabs(ur.y-ll.y))/(_max_cells))))
00033 {
00034
00035 perimeter_points.clear();
00036
00037 for(unsigned i = 0; i < zone.perimeter_points.size(); i++) {
00038 MapXY before(zone.perimeter_points.at(i).map);
00039 MapXY point(zone.perimeter_points.at((i+1)
00040 %zone.perimeter_points.size()).map);
00041 ZoneOps::add_densely(perimeter_points, before, point, scale/3);
00042 }
00043 }
00044
00045 #if 0
00046 void ZoneManager::build_graph(const ObstacleList &obstacles,
00047 const MapXY &start)
00048 {
00049
00050
00051 for(unsigned i = 0; i < perimeter_points.size(); i++)
00052 gmanager.mark_point(perimeter_points[i]);
00053
00054 for(unsigned i = 0; i < zone.perimeter_points.size(); i++) {
00055 if(zone.perimeter_points.at(i).is_entry ||
00056 zone.perimeter_points.at(i).is_exit)
00057 gmanager.unmark_box(zone.perimeter_points.at(i).map,
00058 (int)ceil(2.0/scale));
00059 }
00060
00061 for(unsigned i = 0; i < obstacles.size(); i++) {
00062 gmanager.mark_point(obstacles[i]);
00063 }
00064
00065 int start_x, start_y = 0;
00066 gmanager.transform(start_x, start_y, start);
00067
00068 evg_thin thin(gmanager.grid,
00069 1, FLT_MAX,
00070 false, true,
00071 start_x, start_y);
00072
00073 thin.reset();
00074 thin.set_location(start_x, start_y);
00075 skeleton_type skel=thin.generate_skeleton();
00076
00077 nodes.clear();
00078 edges.clear();
00079
00080 for(unsigned i = 0; i < skel.size(); i++) {
00081 WayPointNode node;
00082 node.map = gmanager.reverse_transform(skel[i].x, skel[i].y);
00083 node.index = i;
00084 node.lane_width = (skel[i].radius*scale);
00085 node.id.seg = 0;
00086 node.id.lane = 0;
00087 node.id.pt = i + 1;
00088 nodes.push_back(node);
00089 }
00090
00091 for(unsigned i = 0; i < skel.size(); i++) {
00092 for(unsigned j = 0; j < skel.at(i).children.size(); j++) {
00093 WayPointEdge edge(nodes.at(i),
00094 nodes.at(skel.at(i).children.at(j)),
00095 UNDEFINED,
00096 UNDEFINED,
00097 false);
00098 edge.speed_max = edge.speed_min = 0;
00099
00100 edge.distance = (skel.at(i).radius*scale);
00101 edges.push_back(edge);
00102 edges.push_back(edge.reverse());
00103 }
00104 }
00105 }
00106
00107 WayPointNodeList ZoneManager::path_through_zone(const ObstacleList &obstacles,
00108 MapXY start,
00109 MapXY end) {
00110 WayPointNodeList path_nodes;
00111
00112 ObstacleList filtered_obstacles = ZoneOps::filter_obstacles(obstacles, zone);
00113
00114 build_graph(filtered_obstacles, start);
00115 ZoneOps::filter_nodes_and_edges(nodes, edges, safety_radius);
00116 unsigned num_nodes = nodes.size();
00117 unsigned num_edges = edges.size();
00118
00119 WayPointNode c_nodes[num_nodes];
00120 WayPointEdge c_edges[num_edges];
00121
00122 for(unsigned i = 0; i < num_nodes; i++)
00123 c_nodes[i] = nodes[i];
00124
00125 for(unsigned i = 0; i < num_edges; i++)
00126 c_edges[i] = edges[i];
00127
00128 Graph *g = new Graph(num_nodes, num_edges, c_nodes, c_edges);
00129
00130 ZoneOps::correct_edge_distances(*g);
00131
00132 if(write_graph)
00133 ZoneOps::print_graph_as_voronoi(*g);
00134
00135 bool can_go_straight = false;
00136 for(unsigned i = 0; i < g->nodes_size; i++) {
00137 if((Euclidean::DistanceTo(g->nodes[i].map, start) <
00138 g->nodes[i].lane_width) &&
00139 (Euclidean::DistanceTo(g->nodes[i].map, end) <
00140 g->nodes[i].lane_width))
00141 can_go_straight = true;
00142 }
00143
00144 if(!can_go_straight) {
00145 WayPointNode *start_node = g->get_closest_node_within_radius(start);
00146 WayPointNode *end_node = g->get_closest_node_within_radius(end);
00147
00148 if(start_node == NULL)
00149 start_node = g->get_closest_node(start);
00150
00151 if(end_node == NULL)
00152 end_node = g->get_closest_node(end);
00153
00154 if(start_node != NULL && end_node != NULL) {
00155 int start_index = start_node->index;
00156 int end_index = end_node->index;
00157
00158 WayPointEdgeList path = GraphSearch::astar_search(*g,
00159 start_index,
00160 end_index);
00161
00162 path_nodes = GraphSearch::edge_list_to_node_list(*g, path);
00163 }
00164 }
00165 delete g;
00166 return path_nodes;
00167 }
00168
00169 #define free_and_set_to_null(ptr) { free(ptr); (ptr) = NULL; }
00170
00171
00172 int ZoneOps::filter_nodes_and_edges(WayPointNodeList &nodes,
00173 WayPointEdgeList &edges,
00174 float safety_radius) {
00175 WayPointNodeList::iterator node = nodes.begin();
00176 while(node < nodes.end()) {
00177 if(node->lane_width < safety_radius) {
00178 WayPointEdgeList::iterator edge = edges.begin();
00179 while(edge < edges.end()) {
00180 if(edge->distance < safety_radius ||
00181 edge->startnode_index == node->index ||
00182 edge->endnode_index == node->index)
00183 edges.erase(edge);
00184 else
00185 edge++;
00186 }
00187 nodes.erase(node);
00188 }
00189 else
00190 node++;
00191 }
00192 return 0;
00193 }
00194
00195 void ZoneOps::print_graph_as_voronoi(Graph &graph) {
00196 std::map<int,int> index_map;
00197 FILE *fh = fopen("/tmp/graph.v.node","w");
00198 fprintf(fh, "%d 2 1 0\n", graph.nodes_size);
00199 for(int i = 0; i < (int) graph.nodes_size; i++) {
00200 fprintf(fh, "%d %.8f %.8f %.8f\n",
00201 i,
00202 graph.nodes[i].map.x,
00203 graph.nodes[i].map.y,
00204 graph.nodes[i].lane_width);
00205 index_map[graph.nodes[i].index] = i;
00206 }
00207 fclose(fh);
00208
00209 fh = fopen("/tmp/graph.v.edge","w");
00210 fprintf(fh, "%d 1\n", graph.edges_size);
00211 for(int i = 0; i < (int) graph.edges_size; i++) {
00212 fprintf(fh, "%d %d %d %.8f\n",
00213 i,
00214 index_map[graph.edges[i].startnode_index],
00215 index_map[graph.edges[i].endnode_index],
00216 graph.edges[i].distance);
00217 }
00218 fclose(fh);
00219 }
00220
00221
00222 int ZoneOps::voronoi_from_evg_thin(WayPointNodeList &nodes,
00223 WayPointEdgeList &edges,
00224 const ZonePerimeter &zone,
00225 float safety_radius,
00226 const ObstacleList &obstacles,
00227 const MapXY &start,
00228 bool write_graph,
00229 bool write_image,
00230 float scale,
00231 int max_cells) {
00232 if(zone.perimeter_points.size() < 3)
00233 return 1;
00234
00235 MapXY ll = zone.perimeter_points[0].map;
00236 MapXY ur = zone.perimeter_points[0].map;
00237
00238 std::vector<MapXY> points;
00239
00240 for(unsigned i = 0; i < zone.perimeter_points.size(); i++) {
00241 MapXY before(zone.perimeter_points.at(i).map);
00242 MapXY point(zone.perimeter_points.at((i+1)%zone.perimeter_points.size()).map);
00243
00244
00245 add_densely(points, before, point, scale/3);
00246 }
00247
00248 expand_bounding_box(points, ll, ur);
00249
00250 float width = fabs(ur.x-ll.x);
00251 float height = fabs(ur.y-ll.y);
00252
00253 scale = fmax(scale, sqrt((width*height)/(max_cells)));
00254
00255 grid_manager grid(scale, ll, ur);
00256
00257 for(unsigned i = 0; i < points.size(); i++)
00258 grid.mark_point(points[i]);
00259
00260 for(unsigned i = 0; i < zone.perimeter_points.size(); i++) {
00261 if(zone.perimeter_points.at(i).is_entry ||
00262 zone.perimeter_points.at(i).is_exit)
00263 grid.unmark_box(zone.perimeter_points.at(i).map, (int)ceil(2.0/scale));
00264 }
00265
00266 for(unsigned i = 0; i < obstacles.size(); i++)
00267 grid.mark_point(obstacles[i]);
00268
00269 int start_x, start_y = 0;
00270 grid.transform(start_x, start_y, start);
00271
00272 evg_thin thin(grid.grid,
00273 1,
00274 FLT_MAX,
00275 false,
00276 true,
00277 start_x, start_y);
00278
00279 skeleton_type skel=thin.generate_skeleton();
00280
00281
00282
00283
00284
00285
00286
00287
00288 for(unsigned i = 0; i < skel.size(); i++) {
00289 WayPointNode node;
00290 node.map = grid.reverse_transform(skel[i].x, skel[i].y);
00291 node.index = i;
00292 node.lane_width = (skel[i].radius*scale);
00293 node.id.seg = 0;
00294 node.id.lane = 0;
00295 node.id.pt = i + 1;
00296 nodes.push_back(node);
00297 }
00298
00299 for(unsigned i = 0; i < skel.size(); i++) {
00300 for(unsigned j = 0; j < skel.at(i).children.size(); j++) {
00301 WayPointEdge edge(nodes.at(i),
00302 nodes.at(skel.at(i).children.at(j)),
00303 UNDEFINED,
00304 UNDEFINED,
00305 false);
00306 edge.speed_max = edge.speed_min = 0;
00307
00308 edge.distance = (skel.at(i).radius*scale);
00309 edges.push_back(edge);
00310 edges.push_back(edge.reverse());
00311 }
00312 }
00313
00314 return 0;
00315 }
00316
00317
00318 WayPointNodeList ZoneOps::path_through_zone(const ZonePerimeter &zone,
00319 float perimeter_sample,
00320 float safety_radius,
00321 const ObstacleList &obstacles,
00322 MapXY start,
00323 MapXY end,
00324 bool write_graph,
00325 bool write_poly,
00326 bool write_obstacles,
00327 float scale,
00328 int max_cells) {
00329 WayPointNodeList path_nodes;
00330 WayPointNodeList nodes;
00331 WayPointEdgeList edges;
00332
00333 int rv = -1;
00334
00335 ObstacleList filtered_obstacles = filter_obstacles(obstacles, zone);
00336
00337 if(0) {
00338 rv = voronoi_from_trilibrary(nodes,
00339 edges,
00340 zone,
00341 perimeter_sample,
00342 safety_radius,
00343 filtered_obstacles,
00344 write_graph,
00345 write_poly,
00346 write_obstacles);
00347 } else {
00348 rv = voronoi_from_evg_thin(nodes,
00349 edges,
00350 zone,
00351 safety_radius,
00352 filtered_obstacles,
00353 start,
00354 write_graph,
00355 write_poly,
00356 scale,
00357 max_cells);
00358 }
00359 if(rv == 0) {
00360 filter_nodes_and_edges(nodes, edges, safety_radius);
00361
00362 unsigned num_nodes = nodes.size();
00363 unsigned num_edges = edges.size();
00364
00365 WayPointNode c_nodes[num_nodes];
00366 WayPointEdge c_edges[num_edges];
00367
00368 for(unsigned i = 0; i < num_nodes; i++)
00369 c_nodes[i] = nodes[i];
00370
00371 for(unsigned i = 0; i < num_edges; i++)
00372 c_edges[i] = edges[i];
00373
00374 Graph *g = new Graph(num_nodes, num_edges, c_nodes, c_edges);
00375
00376 build_new_graph(nodes, edges, *g);
00377
00378 if(write_graph)
00379 print_graph_as_voronoi(*g);
00380
00381 bool can_go_straight = false;
00382 for(unsigned i = 0; i < g->nodes_size; i++) {
00383 if((Euclidean::DistanceTo(g->nodes[i].map, start) <
00384 g->nodes[i].lane_width) &&
00385 (Euclidean::DistanceTo(g->nodes[i].map, end) <
00386 g->nodes[i].lane_width))
00387 can_go_straight = true;
00388 }
00389
00390 if(!can_go_straight) {
00391 WayPointNode *start_node = g->get_closest_node_within_radius(start);
00392 WayPointNode *end_node = g->get_closest_node_within_radius(end);
00393
00394 if(start_node == NULL)
00395 start_node = g->get_closest_node(start);
00396
00397 if(end_node == NULL)
00398 end_node = g->get_closest_node(end);
00399
00400 if(start_node != NULL && end_node != NULL) {
00401 int start_index = start_node->index;
00402 int end_index = end_node->index;
00403
00404 WayPointEdgeList path = GraphSearch::astar_search(*g,
00405 start_index,
00406 end_index);
00407
00408 path_nodes = GraphSearch::edge_list_to_node_list(*g, path);
00409 }
00410 }
00411 delete g;
00412 }
00413 return path_nodes;
00414 }
00415 #endif
00416
00417 void ZoneOps::add_densely(std::vector<MapXY> &points,
00418 const MapXY &p1,
00419 const MapXY &p2,
00420 const float &max_spacing) {
00421
00422 for(float i = 0; i < 1; i += max_spacing/Euclidean::DistanceTo(p1, p2)) {
00423 MapXY n = p1;
00424 n.x += i * (p2.x - p1.x);
00425 n.y += i * (p2.y - p1.y);
00426 points.push_back(n);
00427 }
00428
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438 }
00439
00440 #if 0 // maybe later
00441 ObstacleList ZoneOps::filter_obstacles(const ObstacleList &obstacles,
00442 const ZonePerimeter &zone)
00443 {
00444 ObstacleList f;
00445 for(unsigned i = 0; i < obstacles.size(); i++) {
00446 if(point_in_zone(zone, obstacles[i]))
00447 f.push_back(obstacles[i]);
00448 }
00449 return f;
00450 }
00451 #endif
00452
00453 void ZoneOps::build_new_graph(const WayPointNodeList &nodes,
00454 const WayPointEdgeList &edges,
00455 Graph& g)
00456 {
00457
00458 unsigned num_edges = edges.size();
00459
00460 for(uint i = 0; i < num_edges; i++) {
00461 WayPointNode* start=g.get_node_by_index(g.edges[i].startnode_index);
00462 WayPointNode* end=g.get_node_by_index(g.edges[i].endnode_index);
00463
00464 if(start != NULL && end != NULL)
00465 g.edges[i].distance=Euclidean::DistanceTo(start->map,end->map);
00466 }
00467
00468 }
00469
00470 #if 0 // maybe later
00471 void ZoneOps::correct_edge_distances(Graph& g)
00472 {
00473 for(uint i = 0; i < g.edges.size(); i++) {
00474 WayPointNode* start=g.get_node_by_index(g.edges[i].startnode_index);
00475 WayPointNode* end=g.get_node_by_index(g.edges[i].endnode_index);
00476
00477 if(start != NULL && end != NULL)
00478 g.edges[i].distance=Euclidean::DistanceTo(start->map,end->map);
00479 }
00480 }
00481
00482 ZonePerimeter ZoneOps::build_fake_zone(const std::vector<MapXY>
00483 &points_to_include,
00484 float border_width)
00485 {
00486 ZonePerimeter zone;
00487 zone.zone_id = 0;
00488
00489 if(points_to_include.size() < 1)
00490 return zone;
00491
00492 MapXY upper_right = points_to_include[0];
00493 MapXY lower_left = points_to_include[0];
00494
00495 expand_bounding_box(points_to_include, lower_left, upper_right);
00496
00497
00498 lower_left.x -= border_width;
00499 lower_left.y -= border_width;
00500 upper_right.x += border_width;
00501 upper_right.y += border_width;
00502
00503 zone.perimeter_points.push_back(WayPointNode(MapXY(upper_right.x,
00504 lower_left.y)));
00505 zone.perimeter_points.push_back(WayPointNode(upper_right));
00506 zone.perimeter_points.push_back(WayPointNode(MapXY(lower_left.x,
00507 upper_right.y)));
00508 zone.perimeter_points.push_back(WayPointNode(lower_left));
00509
00510 return zone;
00511 }
00512
00513 void ZoneOps::expand_bounding_box(const std::vector<MapXY>
00514 &points_to_include,
00515 MapXY &lower_left,
00516 MapXY &upper_right)
00517 {
00518
00519 for(unsigned i = 0; i < points_to_include.size(); i++) {
00520 lower_left.x = fmin(points_to_include[i].x, lower_left.x);
00521 lower_left.y = fmin(points_to_include[i].y, lower_left.y);
00522 upper_right.x = fmax(points_to_include[i].x, upper_right.x);
00523 upper_right.y = fmax(points_to_include[i].y, upper_right.y);
00524 }
00525 }
00526
00527 void ZoneOps::expand_bounding_box_of_waypoints(const std::vector<WayPointNode>
00528 &points_to_include,
00529 MapXY &lower_left,
00530 MapXY &upper_right)
00531 {
00532
00533 for(unsigned i = 0; i < points_to_include.size(); i++) {
00534 lower_left.x = fmin(points_to_include[i].map.x, lower_left.x);
00535 lower_left.y = fmin(points_to_include[i].map.y, lower_left.y);
00536 upper_right.x = fmax(points_to_include[i].map.x, upper_right.x);
00537 upper_right.y = fmax(points_to_include[i].map.y, upper_right.y);
00538 }
00539 }
00540 #endif
00541
00542 WayPointNode ZoneOps::starting_node_for_zone(const ZonePerimeter &zone)
00543 {
00544
00545 for(unsigned i = 0; i < zone.perimeter_points.size(); i++) {
00546 if(zone.perimeter_points[i].is_entry && !zone.perimeter_points[i].is_exit)
00547 return zone.perimeter_points[i];
00548 }
00549
00550 return WayPointNode();
00551 }
00552
00553 ZonePerimeter ZoneOps::get_zone_by_id(const ZonePerimeterList &zones,
00554 const segment_id_t &zone_id)
00555 {
00556 for(unsigned i = 0; i < zones.size(); i++) {
00557 if(zones[i].zone_id == zone_id)
00558 return zones[i];
00559 }
00560 ZonePerimeter empty;
00561 return empty;
00562 }
00563
00564 bool ZoneOps::is_a_zone_id(const ZonePerimeterList &zones,
00565 const segment_id_t &zone_id)
00566 {
00567 for(unsigned i = 0; i < zones.size(); i++) {
00568 if(zones[i].zone_id == zone_id)
00569 return true;
00570 }
00571 return false;
00572 }
00573
00574 ZonePerimeterList ZoneOps::build_zone_list_from_rndf(RNDF &rndf,
00575 Graph &graph)
00576 {
00577 ZonePerimeterList zl;
00578 std::vector<Zone>::iterator zi;
00579 for(zi = rndf.zones.begin(); zi != rndf.zones.end(); zi++)
00580 {
00581 ZonePerimeter z=build_zone_perimeter_from_zone(graph, *zi);
00582 bool found=false;
00583 for (uint i = 0; i < graph.edges_size; i++)
00584 {
00585 int index1 = graph.edges[i].startnode_index;
00586 int index2 = graph.edges[i].endnode_index;
00587 if (graph.nodes[index1].id.seg==z.zone_id &&
00588 graph.nodes[index2].id.seg==z.zone_id)
00589 {
00590 z.speed_limit = graph.edges[i].speed_max;
00591 found=true;
00592 break;
00593 }
00594 }
00595 if (!found)
00596 z.speed_limit=DEFAULT_ZONE_SPEED;
00597
00598 zl.push_back(z);
00599 }
00600 return zl;
00601 }
00602
00603 ZonePerimeter ZoneOps::build_zone_perimeter_from_zone(Graph &graph,
00604 Zone &zone)
00605 {
00606 ZonePerimeter zp;
00607 zp.zone_id = (segment_id_t)zone.zone_id;
00608 std::vector<Perimeter_Point>::iterator pp;
00609 for(pp = zone.perimeter.perimeterpoints.begin();
00610 pp != zone.perimeter.perimeterpoints.end();
00611 pp++) {
00612 WayPointNode* wpn =
00613 graph.get_node_by_id(ElementID(zp.zone_id, 0, pp->waypoint_id));
00614 if(wpn != NULL) {
00615 zp.perimeter_points.push_back(*wpn);
00616 }
00617 }
00618
00619 return zp;
00620 }
00621
00622 void ZoneOps::print_zone_list(const ZonePerimeterList &zones)
00623 {
00624 ROS_DEBUG_STREAM("Number of Zones: " << zones.size());
00625 for(unsigned i = 0; i < zones.size(); i++)
00626 print_zone(zones[i]);
00627 }
00628
00629 void ZoneOps::print_zone(const ZonePerimeter &zone)
00630 {
00631 printf("Zone ID: %d Perimeter: ", zone.zone_id);
00632 for(unsigned i = 0; i < zone.perimeter_points.size(); i++) {
00633 printf("(%.6f, %.6f), ",
00634 zone.perimeter_points[i].map.x,
00635 zone.perimeter_points[i].map.y);
00636 }
00637 printf("\n");
00638 }
00639
00640
00641 segment_id_t ZoneOps::containing_zone(const ZonePerimeterList &zones,
00642 const MapXY &point)
00643 {
00644 for(unsigned i = 0; i < zones.size(); i++) {
00645 if(point_in_zone(zones[i], point))
00646 return zones[i].zone_id;
00647 }
00648 return -1;
00649 }
00650
00651 bool ZoneOps::point_in_zone(const ZonePerimeter &zone,
00652 const MapXY &point)
00653 {
00654 if(zone.perimeter_points.size() < 3)
00655 return false;
00656
00657 int intersections = 0;
00658
00659 for(unsigned i = 0; i < zone.perimeter_points.size(); i++) {
00660 intersections += intersections_of_segment_and_ray_to_right
00661 (zone.perimeter_points[i].map,
00662 zone.perimeter_points[(i+1) % zone.perimeter_points.size()].map,
00663 zone.perimeter_points[(i+2) % zone.perimeter_points.size()].map,
00664 point);
00665 }
00666
00667
00668 return bool(intersections % 2);
00669 }
00670
00671 int ZoneOps::intersections_of_segment_and_ray_to_right(const MapXY &p1,
00672 const MapXY &p2,
00673 const MapXY &p3,
00674 const MapXY &r)
00675 {
00676 if(Epsilon::equal(p2.y, r.y) && p2.x >= r.x) {
00677
00678 if ((p1.y > r.y && p3.y > r.y) || (p1.y < r.y && p3.y < r.y))
00679 return 0;
00680 return 1;
00681 }
00682
00683
00684 if((p1.x < r.x) && (p2.x < r.x)) return 0;
00685 if((p1.y < r.y) == (p2.y < r.y)) return 0;
00686 if((p1.x > r.x) && (p2.x > r.x)) return 1;
00687
00688
00689 if((p1.x > r.x) && (p1.y < r.y))
00690 return ((p2.x - r.x)*(p1.y - r.y) < (p1.x - r.x)*(p2.y - r.y));
00691
00692 if((p1.x < r.x) && (p1.y < r.y))
00693 return ((p2.x - r.x)*(p1.y - r.y) < (p1.x - r.x)*(p2.y - r.y));
00694
00695 return ((p2.x - r.x)*(p1.y - r.y) > (p1.x - r.x)*(p2.y - r.y));
00696 }