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