00001
00002 #include "ShapeLine.h"
00003 #include "ShapeRoot.h"
00004 #include "ShapeSpace.h"
00005 #include "Sketch.h"
00006 #include "SketchSpace.h"
00007 #include "visops.h"
00008
00009 #include "PolygonData.h"
00010 #include "ShapePolygon.h"
00011
00012 namespace DualCoding {
00013
00014 DATASTUFF_CC(PolygonData);
00015
00016 PolygonData::PolygonData(const LineData& side)
00017 : BaseData(*side.space,polygonDataType), edges(), vertices()
00018 {
00019 edges.push_back(side);
00020 updateVertices();
00021 mobile = POLYGON_DATA_MOBILE;
00022 }
00023
00024 PolygonData::PolygonData(ShapeSpace& _space, const vector<Point>& pts,
00025 bool closed, bool end1Valid, bool end2Valid)
00026 : BaseData(_space, polygonDataType), edges(), vertices(pts.size()>1 ? pts : vector<Point>())
00027 {
00028 mobile = POLYGON_DATA_MOBILE;
00029 if (pts.empty()) return;
00030 for(vector<Point>::const_iterator vtx_it = pts.begin();
00031 vtx_it < pts.end()-1; vtx_it++)
00032 edges.push_back(LineData(_space, *vtx_it, *(vtx_it+1)));
00033 edges.front().end1Pt().setValid(end1Valid);
00034 edges.back().end2Pt().setValid(end2Valid);
00035 if (closed)
00036 tryClosePolygon();
00037 }
00038
00039
00040 vector<Shape<LineData> > PolygonData::extractPolygonEdges(Sketch<bool> const& sketch,
00041 Sketch<bool> const& occluder) {
00042 vector<Shape<LineData> > lines(LineData::extractLines(sketch, occluder,4));
00043 return lines;
00044 }
00045
00046 void PolygonData::updateVertices() {
00047 vertices.clear();
00048 if (edges.empty()) return;
00049
00050 if (isClosed())
00051 vertices.push_back(end1Ln().intersectionWithLine(end2Ln()));
00052 else
00053 vertices.push_back(end1Pt());
00054 if (edges.size() > 1)
00055 for (vector<LineData>::const_iterator it = edges.begin();
00056 it != edges.end()-1; it++)
00057 vertices.push_back(it->intersectionWithLine(*(it+1)));
00058 if (isClosed())
00059 vertices.push_back(vertices.front());
00060 else
00061 vertices.push_back(end2Pt());
00062 }
00063
00064 vector<ShapeRoot> PolygonData::formPolygons(const vector<LineData>& lines) {
00065 if (lines.empty()) return vector<ShapeRoot>();
00066
00067 vector<LineData> allEdges = lines;
00068 sort(allEdges.begin(),allEdges.end(), ptr_fun(isFirstLineLonger));
00069
00070 vector<ShapeRoot> newPolygons;
00071 for (vector<LineData>::iterator line_it = allEdges.begin();
00072 line_it != allEdges.end(); line_it++) {
00073 Shape<PolygonData> temp_polygon(*line_it);
00074 for (vector<LineData>::iterator line_it2 = line_it+1;
00075 line_it2 < allEdges.end(); line_it2++) {
00076 if (line_it2->getColor() == line_it->getColor()
00077 && (temp_polygon->tryUpdateEdge(Shape<LineData>(*line_it2))
00078 || temp_polygon->tryImportNewEndline(*line_it2))) {
00079 allEdges.erase(line_it2);
00080 line_it2 = line_it;
00081 }
00082 }
00083 temp_polygon->setColor(line_it->getColor());
00084 temp_polygon->updateVertices();
00085 temp_polygon->printParams();
00086 newPolygons.push_back(temp_polygon);
00087 }
00088 return newPolygons;
00089 }
00090
00091 vector<ShapeRoot> PolygonData::formPolygons(const vector<LineData>& lines,
00092 vector<Shape<PolygonData> >& existingPolygons,
00093 vector<ShapeRoot>& deletedPolygons) {
00094 if (lines.empty()) return vector<ShapeRoot>();
00095
00096 vector<LineData> allEdges = lines;
00097 for (vector<Shape<PolygonData> >::const_iterator pol_it = existingPolygons.begin();
00098 pol_it < existingPolygons.end(); pol_it++) {
00099 vector<LineData> existingEdges = (*pol_it)->getEdges();
00100 const int existingParentID = (*pol_it)->getId();
00101 for (vector<LineData>::iterator edge_it = existingEdges.begin();
00102 edge_it != existingEdges.end(); edge_it++)
00103 edge_it->setParentId(existingParentID);
00104 allEdges.insert(allEdges.end(), existingEdges.begin(),existingEdges.end());
00105 }
00106
00107 vector<ShapeRoot> newPolygons = formPolygons(allEdges);
00108
00109 for (vector<Shape<PolygonData> >::iterator existing_it = existingPolygons.begin();
00110 existing_it < existingPolygons.end(); existing_it++) {
00111 vector<ShapeRoot>::iterator new_it = newPolygons.begin();
00112 for (; new_it < newPolygons.end(); new_it++) {
00113 const Shape<PolygonData>& newPolygon = ShapeRootTypeConst(*new_it,PolygonData);
00114 if ((*existing_it)->getEdges().size() == newPolygon->getEdges().size()) {
00115 vector<LineData>::const_iterator edge_it = newPolygon->getEdges().begin();
00116 const int parentPolygonID = (*existing_it)->getId();
00117 for (;edge_it != newPolygon->getEdges().end(); edge_it++)
00118 if (parentPolygonID != edge_it->getParentId())
00119 break;
00120 if (edge_it == newPolygon->getEdges().end()) {
00121 (*existing_it)->edges = newPolygon->edges;
00122 (*existing_it)->vertices = newPolygon->vertices;
00123 new_it->deleteShape();
00124 newPolygons.erase(new_it--);
00125 cout << " => match for existing polygon " << parentPolygonID << endl;
00126 break;
00127 }
00128 }
00129 }
00130 if (new_it == newPolygons.end()) {
00131 deletedPolygons.push_back(*existing_it);
00132 existingPolygons.erase(existing_it--);
00133 }
00134 }
00135 return newPolygons;
00136 }
00137
00138
00139 BoundingBox PolygonData::getBoundingBox() const {
00140 BoundingBox result(vertices[0].coordX(), vertices[0].coordX(),
00141 vertices[0].coordY(), vertices[0].coordY());
00142 for (vector<Point>::const_iterator it = vertices.begin();
00143 it != vertices.end();
00144 it++) {
00145 if ( (*it).coordX() < result.xmin )
00146 result.xmin = (*it).coordX();
00147 else if ( (*it).coordX() > result.xmax )
00148 result.xmax = (*it).coordX();
00149 if ( (*it).coordY() < result.ymin )
00150 result.ymin = (*it).coordY();
00151 else if ( (*it).coordY() > result.ymax )
00152 result.ymax = (*it).coordY();
00153 }
00154 return result;
00155 }
00156
00157 bool PolygonData::formsNewEndline(const LineData& ln, bool useEnd1Pt, bool useEnd2Pt) const {
00158 if (edges.empty()) return true;
00159
00160 useEnd1Pt &= ln.end1Pt().isValid();
00161 useEnd2Pt &= ln.end2Pt().isValid();
00162
00163 if (!(useEnd1Pt || useEnd2Pt))
00164 return false;
00165
00166 if (end1Pt().isValid())
00167 if((useEnd1Pt && end1Pt().distanceFrom(ln.end1Pt()) < THRESH_DIST_VERTEX)
00168 || (useEnd2Pt && end1Pt().distanceFrom(ln.end2Pt()) < THRESH_DIST_VERTEX))
00169 return true;
00170 if (end2Pt().isValid())
00171 if((useEnd1Pt && end2Pt().distanceFrom(ln.end1Pt()) < THRESH_DIST_VERTEX)
00172 || (useEnd2Pt && end2Pt().distanceFrom(ln.end2Pt()) < THRESH_DIST_VERTEX))
00173 return true;
00174
00175 return false;
00176 }
00177
00178 bool PolygonData::tryImportNewEndline(const LineData& ln, bool useEnd1Pt, bool useEnd2Pt) {
00179 if (edges.empty()) {
00180 edges.push_back(ln);
00181 return true;
00182 }
00183
00184 useEnd1Pt &= ln.end1Pt().isValid();
00185 useEnd2Pt &= ln.end2Pt().isValid();
00186 if (!(useEnd1Pt || useEnd2Pt))
00187 return false;
00188
00189 if (end1Pt().isValid()) {
00190 if (useEnd1Pt &&
00191 end1Pt().distanceFrom(ln.end1Pt()) < THRESH_DIST_VERTEX) {
00192 LineData end1ln = ln;
00193 end1ln.setEndPts(ln.end2Pt(),ln.end1Pt());
00194 edges.insert(edges.begin(), end1ln);
00195 return true;
00196 }
00197 else if (useEnd2Pt &&
00198 end1Pt().distanceFrom(ln.end2Pt()) < THRESH_DIST_VERTEX) {
00199 edges.insert(edges.begin(), ln);
00200 return true;
00201 }
00202 }
00203
00204 if (end2Pt().isValid()) {
00205 if (useEnd1Pt &&
00206 end2Pt().distanceFrom(ln.end1Pt()) < THRESH_DIST_VERTEX) {
00207 edges.push_back(ln);
00208 return true;
00209 }
00210 else if (useEnd2Pt &&
00211 end2Pt().distanceFrom(ln.end2Pt()) < THRESH_DIST_VERTEX) {
00212 LineData end2ln = ln;
00213 end2ln.setEndPts(ln.end2Pt(),ln.end1Pt());
00214 edges.push_back(end2ln);
00215 return true;
00216 }
00217 }
00218 return false;
00219 }
00220
00221 bool PolygonData::tryClosePolygon() {
00222 if (vertices.size()<3 ||
00223 !end1Pt().isValid() || !end2Pt().isValid())
00224 return false;
00225 if (end1Ln().isMatchFor(end2Ln())) {
00226 cout << "end1 match for end2\n";
00227 end1Ln().printParams();
00228 end2Ln().printParams();
00229 edges.erase(edges.end());
00230 }
00231 edges.push_back(LineData(*space,end2Pt(),end1Pt()));
00232 edges.front().end1Pt().setValid(true);
00233 updateVertices();
00234 return true;
00235 }
00236
00237 bool PolygonData::isClosed() const {
00238 return (edges.size() > 2 &&
00239 (end1Ln().isMatchFor(end2Ln()) ||
00240 ((end1Pt().distanceFrom(end2Pt()) < THRESH_DIST_VERTEX)
00241 && end1Pt().isValid() && end2Pt().isValid())));
00242 }
00243
00244
00245 bool PolygonData::isMatchForEdge(const LineData& other) const {
00246 for(vector<LineData>::const_iterator this_it = edges.begin();
00247 this_it != edges.end(); this_it++)
00248 if (this_it->isMatchFor(other))
00249 return true;
00250 return false;
00251 }
00252
00253
00254
00255 vector<ShapeRoot> PolygonData::updateState() {
00256 bool breakPolygon = false;
00257 for (vector<LineData>::iterator it = edges.begin();
00258 it != edges.end(); it++) {
00259 if (it->getConfidence() < 0) {
00260 if (! (it == edges.begin() || it == edges.end()-1))
00261 breakPolygon = true;
00262 edges.erase(it--);
00263 }
00264 }
00265 if (breakPolygon && !edges.empty()) {
00266 vector<LineData> lines(edges);
00267 edges.clear();
00268 return formPolygons(lines);
00269 }
00270 else
00271 return vector<ShapeRoot>();
00272 }
00273
00274
00275 bool PolygonData::tryUpdateEdge(const ShapeRoot& ln) {
00276 for(vector<LineData>::iterator it = edges.begin();
00277 it != edges.end(); it++)
00278 if (it->isMatchFor(ln) && it->updateParams(ln)) {
00279 it->increaseConfidence(ln);
00280 if (it->getParentId() == 0 && ln->getParentId() != 0)
00281 it->setParentId(ln->getParentId());
00282 return true;
00283 }
00284 return false;
00285 }
00286
00287
00288
00289
00290
00291
00292
00293 bool PolygonData::isMatchFor(const ShapeRoot& other) const {
00294 if (other->isType(lineDataType) && isSameColorAs(other)) {
00295 const LineData& other_ln = ShapeRootTypeConst(other,LineData).getData();
00296 return (isMatchForEdge(other_ln));
00297 }
00298 if (other->isType(polygonDataType) && isSameColorAs(other)) {
00299 const PolygonData& other_polygon = ShapeRootTypeConst(other,PolygonData).getData();
00300 if (other_polygon.getEdges().size() == edges.size()) {
00301 vector<LineData>::const_iterator other_it = other_polygon.getEdges().begin();
00302 vector<LineData>::const_iterator this_it = getEdges().begin();
00303 for (; other_it != other_polygon.getEdges().end(); other_it++, this_it++)
00304 if (!this_it->isMatchFor(*other_it)) break;
00305 if (this_it == edges.end())
00306 return true;
00307 vector<LineData>::const_reverse_iterator other_rit = other_polygon.getEdges().rbegin();
00308 this_it = edges.begin();
00309 for (; other_rit != other_polygon.getEdges().rend(); other_it++, this_it++)
00310 if (!this_it->isMatchFor(*other_rit)) break;
00311 return (this_it == edges.end());
00312 }
00313 }
00314 return false;
00315 }
00316
00317 int PolygonData::getConfidence() const {
00318 switch (edges.size()) {
00319 case 0:
00320 return -1;
00321 case 1:
00322 return edges.front().getConfidence();
00323 default:
00324 vector<LineData>::const_iterator it = edges.begin();
00325 int conf = (it++)->getConfidence();
00326 for (; it != edges.end(); it++)
00327 if (conf > it->getConfidence())
00328 conf = it->getConfidence();
00329 return conf;
00330 };
00331 }
00332
00333 bool PolygonData::updateParams(const ShapeRoot& other, bool) {
00334 if (other->isType(lineDataType) && tryUpdateEdge(other)) {
00335 updateVertices();
00336 return true;
00337 }
00338 return false;
00339 }
00340
00341
00342
00343
00344
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355 bool PolygonData::isAdmissible() const {
00356 for (vector<LineData>::const_iterator it = edges.begin();
00357 it != edges.end(); it++) {
00358 if (it->isAdmissible())
00359 return true;
00360 }
00361 return false;
00362 }
00363
00364
00365
00366 bool PolygonData::isInside(const Point& pt) const {
00367 if (!isClosed()) return false;
00368 float mostDist = -1;
00369 unsigned int mostDistIndex = -1U;
00370 const unsigned int numVertices = vertices.size()-1;
00371 for (unsigned int i = 0; i < numVertices; i++) {
00372 float dist = pt.distanceFrom(vertices[i]);
00373 if (dist > mostDist) {
00374 mostDist = dist;
00375 mostDistIndex = i;
00376 }
00377 }
00378
00379 LineData ln(this->getSpace(),pt,(vertices[mostDistIndex]));
00380 unsigned int intersectionCount = 0;
00381
00382
00383 {
00384 float theta0 = (mostDistIndex == 0) ?
00385 ((vertices.front() == vertices.back()) ?
00386 atan2(vertices[vertices.size()-2].coordY()-vertices[mostDistIndex].coordY(),
00387 vertices[vertices.size()-2].coordX()-vertices[mostDistIndex].coordX()) :
00388 atan2(vertices[vertices.size()-1].coordY()-vertices[mostDistIndex].coordY(),
00389 vertices[vertices.size()-1].coordX()-vertices[mostDistIndex].coordX())) :
00390 atan2(vertices[mostDistIndex-1].coordY()-vertices[mostDistIndex].coordY(),
00391 vertices[mostDistIndex-1].coordX()-vertices[mostDistIndex].coordX());
00392 float theta1 = atan2(pt.coordY()-vertices[mostDistIndex].coordY(),pt.coordX()-vertices[mostDistIndex].coordX());
00393 float theta2 = (mostDistIndex == vertices.size()-1) ?
00394 ((vertices.front() == vertices.back()) ?
00395 atan2(vertices[1].coordY()-vertices[mostDistIndex].coordY(),
00396 vertices[1].coordX()-vertices[mostDistIndex].coordX()) :
00397 atan2(vertices[0].coordY()-vertices[mostDistIndex].coordY(),
00398 vertices[0].coordX()-vertices[mostDistIndex].coordX())) :
00399 atan2(vertices[mostDistIndex+1].coordY()-vertices[mostDistIndex].coordY(),
00400 vertices[mostDistIndex+1].coordX()-vertices[mostDistIndex].coordX());
00401 if ((theta2>theta0 && !((theta2-theta0<M_PI && theta2>theta1 && theta1>theta0) ||
00402 (2*M_PI-theta2+theta0<M_PI && (theta1>theta2 || theta0>theta1))))
00403 || (theta0>theta2 && !((theta0-theta2<M_PI && theta0>theta1 && theta1>theta2) ||
00404 (2*M_PI-theta0+theta2<M_PI && (theta1>theta0 || theta2>theta1))))) {
00405
00406 intersectionCount++;
00407 }
00408 }
00409
00410 for (unsigned int i = mostDistIndex+1; i < numVertices+mostDistIndex-1; i++) {
00411 LineData cleanEdge(*space,vertices[i%numVertices],vertices[(i+1)%numVertices]);
00412
00413 if (ln.getOrientation() != cleanEdge.getOrientation()
00414 && ln.intersectsLine(cleanEdge)) {
00415
00416 intersectionCount++;
00417 }
00418 }
00419 return (intersectionCount%2) == 0;
00420 }
00421
00422
00423 void PolygonData::printParams() const {
00424 cout << edges.size() << " edge(s)\n";
00425 for (vector<LineData>::const_iterator it = edges.begin();
00426 it != edges.end(); it++)
00427 cout << it->end1Pt() << " -> " << it->end2Pt() << endl;
00428
00429 cout << vertices.size () << " vertice(s):\n";
00430 for (vector<Point>::const_iterator it = vertices.begin();
00431 it != vertices.end(); it++)
00432 cout << (*it) << endl;
00433 }
00434
00435 void PolygonData::applyTransform(const NEWMAT::Matrix& Tmat) {
00436 for (vector<LineData>::iterator it = edges.begin();
00437 it != edges.end(); it++)
00438 it->applyTransform(Tmat);
00439 for (vector<Point>::iterator it = vertices.begin();
00440 it != vertices.end(); it++)
00441 it->applyTransform(Tmat);
00442 }
00443
00444 void PolygonData::projectToGround(const NEWMAT::Matrix& camToBase,
00445 const NEWMAT::ColumnVector& groundplane) {
00446 for (vector<LineData>::iterator it = edges.begin();
00447 it != edges.end(); it++)
00448 it->projectToGround(camToBase,groundplane);
00449 }
00450
00451 Point PolygonData::getCentroid() const {
00452 if (edges.empty())
00453 return Point(0,0,0,getRefFrameType());
00454 vector<LineData>::const_iterator it = edges.begin();
00455 Point centroid = (it++)->getCentroid();
00456 for (; it != edges.end(); it++)
00457 centroid += it->getCentroid();
00458 return centroid/edges.size();
00459 }
00460
00461
00462 void PolygonData::setColor(rgb new_color) {
00463 color_rgb = new_color;
00464 for (vector<LineData>::iterator it = edges.begin();
00465 it != edges.end(); it++)
00466 it->setColor(color_rgb);
00467 deleteRendering();
00468 }
00469
00470
00471 bool PolygonData::isFirstLineLonger(const Shape<LineData>& ln1,const Shape<LineData>& ln2) {
00472 return ln1->isLongerThan(ln2);
00473 }
00474
00475 Sketch<bool>* PolygonData::render() const {
00476 string const result_name = "render(" + getName() + ")";
00477 Sketch<bool> canvas(space->getDualSpace(), result_name);
00478 canvas = 0;
00479 const int width = space->getDualSpace().getWidth();
00480 const int height = space->getDualSpace().getHeight();
00481 float x1, y1, x2, y2;
00482 for (vector<LineData>::const_iterator it = edges.begin();
00483 it != edges.end(); it++) {
00484 it->setDrawCoords(x1, y1, x2, y2, width, height);
00485 it->drawline2d(canvas, (int)x1, (int)y1, (int)x2, (int)y2);
00486 }
00487 if (!isClosed())
00488 return new Sketch<bool>(canvas);
00489 NEW_SKETCH_N(borders, bool, visops::zeros(canvas));
00490 for (int i = 0; i < width; i++) {
00491 borders(i,0) = ! canvas(i,0);
00492 borders(i,height-1) = ! canvas(i,height-1);
00493 }
00494 for (int j = 1; j < height-1; j++) {
00495 borders(0,j) = ! canvas(0,j);
00496 borders(width-1,j) = ! canvas(width-1,j);
00497 }
00498 usint const not_reached = usint(-1);
00499 NEW_SKETCH_N(bd, usint, visops::bdist(borders, canvas, width*height));
00500 bd->inheritFrom(*this);
00501 bd->setColorMap(jetMap);
00502 NEW_SKETCH(result, bool, bd == not_reached);
00503 result->setName(result_name);
00504 return new Sketch<bool>(result);
00505 }
00506
00507
00508
00509 class convexHullPoint {
00510 public:
00511 int x, y;
00512 float angle;
00513
00514 convexHullPoint() : x(0), y(0), angle(0) {}
00515
00516 convexHullPoint(int _x, int _y, float _a) : x(_x), y(_y), angle(_a) {}
00517
00518 static int crossProduct(const convexHullPoint &p1, const convexHullPoint &p2, const convexHullPoint &p3) {
00519 return (p2.x - p1.x)*(p3.y - p1.y) - (p3.x - p1.x)*(p2.y - p1.y);
00520 }
00521
00522 class pointCompare : public binary_function<convexHullPoint, convexHullPoint, bool> {
00523 private:
00524 const convexHullPoint &pivot;
00525 public:
00526 pointCompare(const convexHullPoint &_pivot) : pivot(_pivot) {}
00527 bool operator() (const convexHullPoint &p1, const convexHullPoint &p2) {
00528 if ( p1.angle < p2.angle )
00529 return true;
00530 else if ( p1.angle > p2.angle )
00531 return false;
00532 else {
00533 int const d1x = pivot.x - p1.x;
00534 int const d1y = pivot.y - p1.y;
00535 int const d2x = pivot.x - p2.x;
00536 int const d2y = pivot.y - p2.y;
00537 return (d1x*d1x+d1y*d1y) < (d2x*d2x+d2y*d2y);
00538 }
00539 }
00540 }
00541 ;
00542 };
00543
00544 Shape<PolygonData> PolygonData::convexHull(const Sketch<bool> &sketch) {
00545 int const spts = sketch->sum();
00546 int const width = sketch.width;
00547 int const npix = sketch->getNumPixels();
00548
00549
00550
00551
00552 NEW_SKETCH_N(neighbs, uchar, visops::neighborSum(sketch));
00553 std::vector<convexHullPoint> points(spts);
00554 points.clear();
00555 int pivot_x = -1, pivot_y = width+1;
00556 for (int i=0; i<npix; i++) {
00557
00558 if ( sketch[i] && neighbs[i] < 8 ) {
00559 int const x = i % width;
00560 int const y = i / width;
00561 points.push_back(convexHullPoint(x,y,0));
00562 if ( y < pivot_y || (y == pivot_y && x > pivot_x) ) {
00563 pivot_x = x;
00564 pivot_y = y;
00565 };
00566 }
00567 }
00568 int const npts = points.size();
00569
00570
00571
00572 for (int i=0; i<npts; i++)
00573 points[i].angle = (float) -atan2((float) (pivot_y - points[i].y), (float) (pivot_x - points[i].x));
00574 std::sort(points.begin(),points.end(),convexHullPoint::pointCompare(convexHullPoint(pivot_x,pivot_y,0)));
00575
00576
00577 vector<convexHullPoint> hull(npts);
00578 hull.clear();
00579 hull.push_back(points[0]);
00580 hull.push_back(points[1]);
00581 for ( int i=2; i<npts; i++ ) {
00582 int last = hull.size() - 1;
00583 int o = convexHullPoint::crossProduct(hull[last-1],hull[last],points[i]);
00584 if ( o == 0 )
00585 hull[last] = points[i];
00586 else if ( o < 0 )
00587 hull.push_back(points[i]);
00588 else {
00589 while ( o >= 0 && hull.size() > 2 ) {
00590 hull.pop_back();
00591 last--;
00592 o = convexHullPoint::crossProduct(hull[last-1],hull[last],points[i]);
00593 }
00594 hull.push_back(points[i]);
00595 }
00596 }
00597 int const nhull = hull.size();
00598
00599 ShapeSpace &ShS = sketch->getDualSpace();
00600 vector<Point> vertices;
00601 for (int i=0; i<nhull; i++)
00602 vertices.push_back(Point(hull[i].x, hull[i].y));
00603 vertices.push_back(Point(hull[0].x, hull[0].y));
00604 return Shape<PolygonData>(new PolygonData(ShS,vertices,true));
00605 }
00606
00607
00608 }