int pick_major_axis( vector<pair<Pmwx::Halfedge_handle, Pmwx::Halfedge_handle> >& sides, // Per side: inclusive range of half-edges "consolidated" into the sides. Polygon2& bounds, // Inset boundary in metric, first side matched to the list. Vector2& v_x, Vector2& v_y) { // special case: if we find a block with exactly ONE right angle, the longer of the two // sides going into the right angle is hte major axis, full stop, we're done. int right_angle = -1; for(int i = 0; i < sides.size(); ++i) { int j = (i + 1) % sides.size(); int k = (i + 2) % sides.size(); Vector2 vx(Vector2(bounds[i],bounds[j])); Vector2 vy(Vector2(bounds[j],bounds[k])); vx.normalize(); vy.normalize(); double dot = fabs(vx.dot(vy)); if(dot < 0.087155742747658) { if(right_angle == -1) right_angle = j; else right_angle = -2; // "more than one right angle" flag - causes us to NOT try this algo. } } if(right_angle >= 0) { int prev = (right_angle + sides.size() - 1) % sides.size(); int next = (right_angle + 1) % sides.size(); Vector2 vp(Vector2(bounds[prev],bounds[right_angle])); Vector2 vn(Vector2(bounds[right_angle],bounds[next])); double pl = vp.normalize(); double nl = vn.normalize(); if(pl > nl) v_x = vp; else v_x = vn; v_y = v_x.perpendicular_ccw(); return right_angle; } // THIS is the algo we shipped with - it tries to minimize the short side axis of the block. This works // okay but tends to make the diagonal of diagonal cuts (like Broadway) the main axis since (by the pythag // theorem) that slightly reduces the block depth. #if 0 int shortest = -1; double thinnest_so_far = 0.0; for(int i = 0; i < sides.size(); ++i) { Vector2 vx = Vector2(bounds.side(i).p1,bounds.side(i).p2); vx.normalize(); Vector2 vy = vx.perpendicular_ccw(); double bbox[4]; bbox[0] = bbox[2] = vx.dot(Vector2(bounds[0])); bbox[1] = bbox[3] = vy.dot(Vector2(bounds[0])); for(int j = 0; j < sides.size(); ++j) { double x = vx.dot(Vector2(bounds[j])); double y = vy.dot(Vector2(bounds[j])); bbox[0] = dobmin2(bbox[0], x); bbox[1] = dobmin2(bbox[1], y); bbox[2] = dobmax2(bbox[2], x); bbox[3] = dobmax2(bbox[3], y); } double xdist = fabs(bbox[2]-bbox[0]); double ydist = fabs(bbox[3]-bbox[1]); double my_dist = dobmin2(xdist,ydist); if(shortest == -1 || my_dist < thinnest_so_far) { shortest = i; thinnest_so_far = my_dist; if(xdist < ydist) { v_x = vx.perpendicular_ccw(); v_y = vy.perpendicular_ccw(); } else { v_x = vx; v_y = vy; } } } DebugAssert(shortest >= 0); return shortest; #endif #if 1 // #error This algo works 95% of the time, but 5% of the time it picks a slashed short end as the // #error long axis, which gives a long thin block a wrong axis alignment and a huge AABB. Bad! // The basic idea: we want to pick the grid axis MOST aligned with the block such that // the major axis supports roads. double best_corr = 0; double best = -1; Vector2 best_vec; bool elev_ok = false; int i, j, tries; for(tries = 0; tries < 2; ++tries) { for(i = 0; i < sides.size(); ++i) if(elev_ok || ground_road_access_for_he(sides[i].first)) { double score = 0.0; Vector2 si = Vector2(bounds.side(i).p1,bounds.side(i).p2); si.normalize(); Vector2 si_n = si.perpendicular_ccw(); for(int j = 0; j < sides.size(); ++j) if(elev_ok || ground_road_access_for_he(sides[j].first)) { Vector2 sj(bounds.side(j).p1,bounds.side(j).p2); double my_corr = fltmax2(fabs(si.dot(sj)),fabs(si_n.dot(sj))); score += my_corr; } if(score > best_corr){ best = i; best_corr = score; best_vec = si; } } if(best >= 0) break; elev_ok = true; } if(best >= 0) { Vector2 best_vec_n = best_vec.perpendicular_ccw(); int longest = -1; double corr_len = -1; for(int i = 0; i < sides.size(); ++i) if(/*elev_ok ||*/ ground_road_access_for_he(sides[i].first)) { Vector2 this_side(bounds.side(i).p1,bounds.side(i).p2); double len = this_side.normalize(); double my_corr = fltmax2(fabs(best_vec.dot(this_side)), fabs(best_vec_n.dot(this_side))); if(my_corr > 0.996194698091746) { my_corr *= len; if(my_corr > corr_len) { longest = i; corr_len = my_corr; } } } if(longest >= 0) best = longest; } v_x = Vector2(bounds.side(best).p1,bounds.side(best).p2); v_x.normalize(); v_y = v_x.perpendicular_ccw(); //printf("So far our best is %d, with axes %lf, %lf to %lf, %lf\n", best, v_x.dx,v_x.dy,v_y.dx,v_y.dy); double bbox[4]; bbox[0] = bbox[2] = v_x.dot(Vector2(bounds[0])); bbox[1] = bbox[3] = v_y.dot(Vector2(bounds[0])); for(i = 1; i < sides.size(); ++i) { double va = v_x.dot(Vector2(bounds[i])); double vb = v_y.dot(Vector2(bounds[i])); bbox[0]=dobmin2(bbox[0], va); bbox[1]=dobmin2(bbox[1], vb); bbox[2]=dobmax2(bbox[2], va); bbox[3]=dobmax2(bbox[3], vb); } //printf("Our bbox is %lf,%lf to %lf,%lf\n", bbox[0],bbox[1],bbox[2],bbox[3]); if(0) if((bbox[2] - bbox[0]) < (bbox[3] - bbox[1])) { v_x = v_x.perpendicular_ccw(); v_y = v_y.perpendicular_ccw(); //printf("Must rotate, the winner was a SHORT side.\n"); } // best = 0; // double best_dot = 0; // for(i = 0; i < sides.size(); ++i) // if(ground_road_access_for_he(sides[i].first)) // { // Vector2 side_vec(bounds.side(i).p1,bounds.side(i).p2); // double slen = side_vec.normalize(); // double corr = fabs(v_x.dot(side_vec)); // if(corr > best_dot) // { // best = i; // corr = best_dot; // } // } // DebugAssert(best >= 0.0); // v_x = Vector2(bounds.side(best).p1,bounds.side(best).p2); // v_x.normalize(); // v_y = v_x.perpendicular_ccw(); return best; #endif }
bool build_convex_polygon( Pmwx::Ccb_halfedge_circulator ccb, vector<pair<Pmwx::Halfedge_handle, Pmwx::Halfedge_handle> >& sides, const CoordTranslator2& trans, Polygon2& metric_bounds, double max_err_mtrs, double min_side_len) { double e_sq = max_err_mtrs*max_err_mtrs; sides.clear(); metric_bounds.clear(); Pmwx::Ccb_halfedge_circulator circ(ccb); // Bbox2 bounds; // // do { // bounds += cgal2ben(circ->source()->point()); // } while (++circ != ccb); Pmwx::Ccb_halfedge_circulator start,next; start = ccb; do { --start; if(!sides_can_merge(start,ccb)) break; if(!within_err_metric(start,ccb,trans,e_sq)) break; } while(start != ccb); ++start; // now we can go around. circ = start; //int ne = count_circulator(start); //printf("Poly has %d sides.\n", ne); do { Pmwx::Ccb_halfedge_circulator stop(circ); do { ++stop; } while(sides_can_merge(circ,stop) && within_err_metric(circ,stop,trans,e_sq) && stop != start); --stop; //printf("Pushing side of %d, %d\n", circulator_distance_to(start, circ),circulator_distance_to(start,stop)); sides.push_back(pair<Pmwx::Halfedge_handle,Pmwx::Halfedge_handle>(circ, stop)); ++stop; circ = stop; } while(circ != start); if(sides.size() < 3) { //debug_mesh_point(bounds.centroid(),1,1,1); return false; } int i, j, k; vector<Segment2> msides; for(i = 0; i < sides.size(); ++i) { j = (i + 1) % sides.size(); DebugAssert(sides[i].second->target() == sides[j].first->source()); msides.push_back(Segment2( trans.Forward(cgal2ben(sides[i].first->source()->point())), trans.Forward(cgal2ben(sides[i].second->target()->point())))); } vector<Segment2> debug(msides); for(i = 0; i < sides.size(); ++i) { j = (i + 1) % sides.size(); Vector2 v1(msides[i].p1,msides[i].p2); Vector2 v2(msides[j].p1,msides[j].p2); v1.normalize(); v2.normalize(); if(v1.dot(v2) > 0.9998 || !v1.left_turn(v2)) { //debug_mesh_point(trans.Reverse(msides[i].p2),1,0,0); return false; } double w = width_for_he(sides[i].first); if(w) { v1 = v1.perpendicular_ccw(); v1 *= w; msides[i].p1 += v1; msides[i].p2 += v1; } } for(j = 0; j < sides.size(); ++j) { i = (j + sides.size() - 1) % sides.size(); Line2 li(msides[i]), lj(msides[j]); Point2 p; if(!li.intersect(lj,p)) { Assert(!"Failure to intersect.\n"); return false; } metric_bounds.push_back(p); } for(i = 0; i < metric_bounds.size(); ++i) { j = (i + 1) % metric_bounds.size(); k = (i + 2) % metric_bounds.size(); if(metric_bounds.side(i).squared_length() < (min_side_len*min_side_len)) { //debug_mesh_line(trans.Reverse(metric_bounds.side(i).p1),trans.Reverse(metric_bounds.side(i).p2),1,1,0,1,1,0); return false; } if(!left_turn(metric_bounds[i],metric_bounds[j],metric_bounds[k])) { //debug_mesh_point(trans.Reverse(metric_bounds[j]),1,1,0); return false; } if(Vector2(msides[i].p1,msides[i].p2).dot(Vector2(metric_bounds[i],metric_bounds[j])) < 0.0) { //debug_mesh_line(trans.Reverse(msides[i].p1),trans.Reverse(msides[i].p2),1,0,0,1,0,0); return false; } } DebugAssert(metric_bounds.size() == msides.size()); DebugAssert(msides.size() == sides.size()); return true; }