Пример #1
0
void makeEdgePlanes(const carve::poly::Polyhedron *poly, EPMap &edge_planes) {
#if defined(UNORDERED_COLLECTIONS_SUPPORT_RESIZE)
  edge_planes.resize(poly->edges.size());
#endif

  for (size_t i = 0; i < poly->edges.size(); ++i) {
    EdgePlane &ep(edge_planes[&poly->edges[i]]);

    CARVE_ASSERT(poly->edges[i].faces.size() == 2);

    const carve::poly::Face *f1 = poly->edges[i].faces[0];
    const carve::poly::Face *f2 = poly->edges[i].faces[1];

    CARVE_ASSERT(f1 && f2);

    ep.base = poly->edges[i].v2->v - poly->edges[i].v1->v;
    ep.base.normalize();
    carve::geom3d::Vector d_1 = f1->plane_eqn.N + f2->plane_eqn.N;
    carve::geom3d::Vector d_2 = cross(f1->plane_eqn.N, ep.base) - cross(f2->plane_eqn.N, ep.base);
    ep.dir = d_2.length2() > d_1.length2() ? d_2 : d_1;
    ep.dir.normalize();
    ep.norm = cross(ep.dir, ep.base);

          if (true) {
            carve::geom3d::Vector v1 = (poly->edges[i].v1->v + poly->edges[i].v2->v) / 2.0;
            carve::geom3d::Vector v2 = v1 + 0.075 * ep.dir;
            glBegin(GL_LINES);
            glColor4f(1.0, 1.0, 1.0, 1.0);
            glVertex3f(v1.x, v1.y, v1.z);
            glColor4f(1.0, 1.0, 1.0, 0.4);
            glVertex3f(v2.x, v2.y, v2.z);
            glEnd();
          }
  }
}
      static void processOneEdge(const V2 &edge,
                                 const carve::csg::detail::LoopEdges &a_edge_map,
                                 const carve::csg::detail::LoopEdges &b_edge_map,
                                 Classification &a_classification,
                                 Classification &b_classification,
								 double EPSILON) {
        GrpEdgeSurfMap a_edge_surfaces;
        GrpEdgeSurfMap b_edge_surfaces;

        carve::geom3d::Vector edge_vector = (edge.second->v - edge.first->v).normalized();
        carve::geom3d::Vector base_vector = perpendicular(edge_vector);

        carve::csg::detail::LoopEdges::const_iterator ae_f = a_edge_map.find(edge);
        carve::csg::detail::LoopEdges::const_iterator ae_r = a_edge_map.find(flip(edge));
        CARVE_ASSERT(ae_f != a_edge_map.end() || ae_r != a_edge_map.end());

        carve::csg::detail::LoopEdges::const_iterator be_f = b_edge_map.find(edge);
        carve::csg::detail::LoopEdges::const_iterator be_r = b_edge_map.find(flip(edge));
        CARVE_ASSERT(be_f != b_edge_map.end() || be_r != b_edge_map.end());

        if (ae_f != a_edge_map.end() && !processForwardEdgeSurfaces(a_edge_surfaces, (*ae_f).second, edge_vector, base_vector,EPSILON)) return;
        if (ae_r != a_edge_map.end() && !processReverseEdgeSurfaces(a_edge_surfaces, (*ae_r).second, edge_vector, base_vector,EPSILON)) return;
        if (be_f != b_edge_map.end() && !processForwardEdgeSurfaces(b_edge_surfaces, (*be_f).second, edge_vector, base_vector,EPSILON)) return;
        if (be_r != b_edge_map.end() && !processReverseEdgeSurfaces(b_edge_surfaces, (*be_r).second, edge_vector, base_vector,EPSILON)) return;

        classifyAB(a_edge_surfaces, b_edge_surfaces, b_classification);
        classifyAB(b_edge_surfaces, a_edge_surfaces, a_classification);
      }
    bool triangle_intersection_simple(const vec2 tri_a[3], const vec2 tri_b[3]) {
      // triangles must be anticlockwise, or colinear.
      CARVE_ASSERT(orient2d_exact(tri_a[0], tri_a[1], tri_a[2]) >= 0.0);
      CARVE_ASSERT(orient2d_exact(tri_b[0], tri_b[1], tri_b[2]) >= 0.0);

      for (size_t i = 0; i < 3; ++i) {
        if (sat_edge(tri_a, tri_b, i) < 0) return false;
      }
      for (size_t i = 0; i < 3; ++i) {
        if (sat_edge(tri_b, tri_a, i) < 0) return false;
      }
      return true;
    }
    TriangleInt triangle_intersection(const vec2 tri_a[3], const vec2 tri_b[3]) {
      // triangles must be anticlockwise, or colinear.
      CARVE_ASSERT(carve::geom2d::orient2d(tri_a[0], tri_a[1], tri_a[2]) >= 0.0);
      CARVE_ASSERT(carve::geom2d::orient2d(tri_b[0], tri_b[1], tri_b[2]) >= 0.0);

      size_t ia, ib;
      int sharing = test_sharing<2>(tri_a, tri_b, ia, ib);

      switch (std::abs(sharing)) {
      case 0: {
        // no shared vertices.
        if (sat_edge(tri_a, tri_b, 0) < 0) return TR_INT_NONE;
        if (sat_edge(tri_a, tri_b, 1) < 0) return TR_INT_NONE;
        if (sat_edge(tri_a, tri_b, 2) < 0) return TR_INT_NONE;
        if (sat_edge(tri_b, tri_a, 0) < 0) return TR_INT_NONE;
        if (sat_edge(tri_b, tri_a, 1) < 0) return TR_INT_NONE;
        if (sat_edge(tri_b, tri_a, 2) < 0) return TR_INT_NONE;

        return TR_INT_INT;
      }
      case 1: {
        // shared vertex (ia, ib) [but not shared edge]
        if (sat_edge(tri_a, tri_b, (ia+2)%3, ib) < 0) return TR_INT_VERT;
        if (sat_edge(tri_a, tri_b, ia, ib)       < 0) return TR_INT_VERT;
        if (sat_edge(tri_b, tri_a, (ib+2)%3, ia) < 0) return TR_INT_VERT;
        if (sat_edge(tri_b, tri_a, ib, ia)       < 0) return TR_INT_VERT;

        return TR_INT_INT;
      }
      case 2: {
        // shared edge (ia, ib) -> (ia + 1, ib + (sharing > 0) ? +1 : -1)
        size_t pa = (ia + 2) % 3;
        size_t pb = (ib + (sharing == +1 ? 2 : 1)) % 3;

        double sa = orient2d_exact(tri_a[ia], tri_a[(ia+1)%3], tri_a[pa]);
        double sb = orient2d_exact(tri_a[ia], tri_a[(ia+1)%3], tri_b[pb]);

        if (sa * sb < 0.0) {
          return TR_INT_EDGE;
        }

        return TR_INT_INT;
      }
      case 3: {
        return TR_INT_TRI;
      }
      default: {
        CARVE_FAIL("should not happen.");
      }
      }
    }
Пример #5
0
 virtual bool key(unsigned char k, int x, int y) {
   const char *t;
   static const char *l = "1234567890!@#$%^&*()";
   if (k == '\\') {
     for (unsigned i = 1; i < draw_flags.size(); i += 2) {
       draw_flags[i] = !draw_flags[i];
     }
   } else if (k == 'n') {
     bool n = true;
     for (unsigned i = 0; i < draw_flags.size(); ++i)
       if (is_wireframe[i] && draw_flags[i]) n = false;
     for (unsigned i = 0; i < draw_flags.size(); ++i)
       if (is_wireframe[i]) draw_flags[i] = n;
   } else if (k == 'm') {
     bool n = true;
     for (unsigned i = 0; i < draw_flags.size(); ++i)
       if (!is_wireframe[i] && draw_flags[i]) n = false;
     for (unsigned i = 0; i < draw_flags.size(); ++i)
       if (!is_wireframe[i]) draw_flags[i] = n;
   } else {
     t = strchr(l, k);
     if (t != NULL) {
       CARVE_ASSERT(t >= l);
       unsigned layer = t - l;
       if (layer < draw_flags.size()) {
         draw_flags[layer] = !draw_flags[layer];
       }
     }
   }
   return true;
 }
Пример #6
0
vector<ndim>& vector<ndim>::normalize() {
#if defined(CARVE_DEBUG)
  CARVE_ASSERT(length() > 0.0);
#endif
  *this /= length();
  return *this;
}
Пример #7
0
    Face<ndim> *Face<ndim>::init(const Face<ndim> *base, iter_t vbegin, iter_t vend, bool flipped) {
      CARVE_ASSERT(vbegin < vend);

      vertices.reserve((size_t)std::distance(vbegin, vend));

      if (flipped) {
        std::reverse_copy(vbegin, vend, std::back_inserter(vertices));
        plane_eqn = -base->plane_eqn;
      } else {
        std::copy(vbegin, vend, std::back_inserter(vertices));
        plane_eqn = base->plane_eqn;
      }

      edges.clear();
      edges.resize(nVertices(), NULL);

      aabb.fit(vertices.begin(), vertices.end(), vec_adapt_vertex_ptr());
      untag();

      int da = carve::geom::largestAxis(plane_eqn.N);

      project = getProjector(plane_eqn.N.v[da] > 0, da);
      unproject = getUnprojector(plane_eqn.N.v[da] > 0, da);

      return this;
    }
    TriangleIntType triangle_intersection_exact(const vec2 tri_a[3], const vec2 tri_b[3]) {
      // triangles must be anticlockwise, or colinear.
      CARVE_ASSERT(orient2d_exact(tri_a[0], tri_a[1], tri_a[2]) >= 0.0);
      CARVE_ASSERT(orient2d_exact(tri_b[0], tri_b[1], tri_b[2]) >= 0.0);

      int curr = +1;
      for (size_t i = 0; curr != -1 && i < 3; ++i) {
        curr = std::min(curr, sat_edge(tri_a, tri_b, i));
      }
      for (size_t i = 0; curr != -1 && i < 3; ++i) {
        curr = std::min(curr, sat_edge(tri_b, tri_a, i));
      }
      switch (curr) {
      case -1: return TR_TYPE_NONE;
      case  0: return TR_TYPE_TOUCH;
      case +1: return TR_TYPE_INT;
      default: CARVE_FAIL("should not happen.");
      }
    }
    TriangleIntType triangle_point_intersection_exact(const vec2 tri_a[3], const vec2 &pt_b) {
      CARVE_ASSERT(orient2d_exact(tri_a[0], tri_a[1], tri_a[2]) >= 0.0);

      int m = min3(dbl_sign(orient2d_exact(tri_a[0], tri_a[1], pt_b)),
                   dbl_sign(orient2d_exact(tri_a[1], tri_a[2], pt_b)),
                   dbl_sign(orient2d_exact(tri_a[2], tri_a[0], pt_b)));

      switch (m) {
      case -1: return TR_TYPE_NONE;
      case  0: return TR_TYPE_TOUCH;
      default: return TR_TYPE_INT;
      }
    }
    TriangleIntType triangle_linesegment_intersection_exact(const vec2 tri_a[3], const vec2 line_b[2]) {
      CARVE_ASSERT(orient2d_exact(tri_a[0], tri_a[1], tri_a[2]) >= 0.0);

      int l1 = dbl_sign(orient2d_exact(line_b[0], line_b[1], tri_a[0]));
      int l2 = dbl_sign(orient2d_exact(line_b[0], line_b[1], tri_a[1]));
      int l3 = dbl_sign(orient2d_exact(line_b[0], line_b[1], tri_a[2]));

      if (min3(l1,l2,l3) == +1) return TR_TYPE_NONE;
      if (max3(l1,l2,l3) == -1) return TR_TYPE_NONE;

      int m = +1;
      for (size_t i = 0; m != -1 && i < 3; ++i) {
        int n = std::max(dbl_sign(orient2d_exact(tri_a[i], tri_a[(i+1)%3], line_b[0])),
                         dbl_sign(orient2d_exact(tri_a[i], tri_a[(i+1)%3], line_b[1])));
        m = std::min(m, n);
      }

      switch (m) {
      case -1: return TR_TYPE_NONE;
      case  0: return TR_TYPE_TOUCH;
      default: return TR_TYPE_INT;
      }
    }
Пример #11
0
    bool pointInPolySimple(const std::vector<T> &points, adapt_t adapt, const P2 &p, double EPSILON) {
      CARVE_ASSERT(points.size() > 0);
      P2Vector::size_type l = points.size();
      double s = 0.0;
      double rp, r0, d;

      rp = r0 = atan2(adapt(points[0]) - p);

      for (P2Vector::size_type i = 1; i < l; i++) {
        double r = atan2(adapt(points[i]) - p);
        d = r - rp;
        if (d > M_PI) d -= M_TWOPI;
        if (d < -M_PI) d += M_TWOPI;
        s = s + d;
        rp = r;
      }

      d = r0 - rp;
      if (d > M_PI) d -= M_TWOPI;
      if (d < -M_PI) d += M_TWOPI;
      s = s + d;

      return !carve::math::ZERO(s,EPSILON);
    }
Пример #12
0
vector<ndim> vector<ndim>::normalized() const {
#if defined(CARVE_DEBUG)
  CARVE_ASSERT(length() > 0.0);
#endif
  return *this / length();
}
Пример #13
0
int main(int argc, char **argv) {
  options.parse(argc, argv);
  carve::mesh::MeshSet<3> *poly;

  if (options.axis == Options::ERR) {
    std::cerr << "need to specify a closure plane." << std::endl;
    exit(1);
  }

  if (options.file == "-") {
    poly = readPLYasMesh(std::cin);
  } else if (endswith(options.file, ".ply")) {
    poly = readPLYasMesh(options.file);
  } else if (endswith(options.file, ".vtk")) {
    poly = readVTKasMesh(options.file);
  } else if (endswith(options.file, ".obj")) {
    poly = readOBJasMesh(options.file);
  }

  if (poly == NULL) {
    std::cerr << "failed to load polyhedron" << std::endl;
    exit(1);
  }

  std::cerr << "poly aabb = " << poly->getAABB() << std::endl;

  if (poly->getAABB().compareAxis(carve::geom::axis_pos(options.axis, options.pos)) == 0) {
    std::cerr << "poly aabb intersects closure plane." << std::endl;
    exit(1);
  }


  for (size_t i = 0; i < poly->meshes.size(); ++i) {
    carve::mesh::MeshSet<3>::mesh_t *mesh = poly->meshes[i];
    const size_t N = mesh->open_edges.size();
    if (N == 0) continue;

    mesh->faces.reserve(N + 1);

    carve::mesh::MeshSet<3>::edge_t *start = mesh->open_edges[0];

    std::vector<carve::mesh::MeshSet<3>::edge_t *> edges_to_close;
    edges_to_close.resize(N);
    carve::mesh::MeshSet<3>::edge_t *edge = start;
    size_t j = 0;
    do {
      edges_to_close[j++] = edge;
      edge = edge->perimNext();
    } while (edge != start);

    CARVE_ASSERT(j == N);

    std::vector<carve::mesh::MeshSet<3>::vertex_t> projected;
    projected.resize(N);

    for (j = 0; j < N; ++j) {
      edge = edges_to_close[j];
      projected[j].v = edge->vert->v;
      projected[j].v.v[options.axis] = options.pos;
    }

    for (j = 0; j < N; ++j) {
      edge = edges_to_close[j];
      carve::mesh::MeshSet<3>::face_t *quad =
        new carve::mesh::MeshSet<3>::face_t(edge->v2(), edge->v1(), &projected[j], &projected[(j+1)%N]);
      quad->mesh = mesh;
      edge->rev = quad->edge;
      quad->edge->rev = edge;
      mesh->faces.push_back(quad);
    }

    for (j = 0; j < N; ++j) {
      carve::mesh::MeshSet<3>::edge_t *e1 = edges_to_close[j]->rev->prev;
      carve::mesh::MeshSet<3>::edge_t *e2 = edges_to_close[(j+1)%N]->rev->next;
      e1->rev = e2;
      e2->rev = e1;
    }

    for (j = 0; j < N; ++j) {
      edge = edges_to_close[j]->rev;
      edge->validateLoop();
    }

    carve::mesh::MeshSet<3>::face_t *loop =
      carve::mesh::MeshSet<3>::face_t::closeLoop(edges_to_close[0]->rev->next->next);

    loop->mesh = mesh;
    mesh->faces.push_back(loop);

    poly->collectVertices();
  }

  if (options.obj) {
    writeOBJ(std::cout, poly);
  } else if (options.vtk) {
    writeVTK(std::cout, poly);
  } else {
    writePLY(std::cout, poly, options.ascii);
  }

  return 0;
}
Пример #14
0
 const Vertex *operator*() const {
   CARVE_ASSERT(idx >= 0 && idx < base->vertexCount());
   return base->vertex((size_t)idx);
 }
Пример #15
0
 const PolylineEdge *operator*() const {
   CARVE_ASSERT(idx >= 0 && idx < base->edgeCount());
   return base->edge((size_t)idx);
 }
Пример #16
0
void carve::csg::CSG::findSharedEdges(const detail::LoopEdges &edge_map_a,
                                      const detail::LoopEdges &edge_map_b,
                                      V2Set &shared_edges) {
  for (detail::LoopEdges::const_iterator
         i = edge_map_a.begin(), e = edge_map_a.end();
       i != e;
       ++i) {
    detail::LoopEdges::const_iterator j = edge_map_b.find((*i).first);
    if (j != edge_map_b.end()) {
      shared_edges.insert((*i).first);
    }
  }

#if defined(CARVE_DEBUG)
  detail::VVSMap edge_graph;

  for (V2Set::const_iterator i = shared_edges.begin(); i != shared_edges.end(); ++i) {
    edge_graph[(*i).first].insert((*i).second);
    edge_graph[(*i).second].insert((*i).first);
  }

  std::cerr << "*** testing consistency of edge graph" << std::endl;
  for (detail::VVSMap::const_iterator i = edge_graph.begin(); i != edge_graph.end(); ++i) {
    if ((*i).second.size() > 2) {
      std::cerr << "branch at: " << (*i).first << std::endl;
    }
    if ((*i).second.size() == 1) {
      std::cerr << "endpoint at: " << (*i).first << std::endl;
      std::cerr << "coordinate: " << (*i).first->v << std::endl;
    }
  }

  {
    carve::line::PolylineSet intersection_graph;
    intersection_graph.vertices.resize(edge_graph.size());
    std::map<const carve::mesh::MeshSet<3>::vertex_t *, size_t> vmap;

    size_t j = 0;
    for (detail::VVSMap::const_iterator i = edge_graph.begin(); i != edge_graph.end(); ++i) {
      intersection_graph.vertices[j].v = (*i).first->v;
      vmap[(*i).first] = j++;
    }

    while (edge_graph.size()) {
      detail::VVSMap::iterator prior_i = edge_graph.begin();
      carve::mesh::MeshSet<3>::vertex_t *prior = (*prior_i).first;
      std::vector<size_t> connected;
      connected.push_back(vmap[prior]);
      while (prior_i != edge_graph.end() && (*prior_i).second.size()) {
        carve::mesh::MeshSet<3>::vertex_t *next = *(*prior_i).second.begin();
        detail::VVSMap::iterator next_i = edge_graph.find(next);
        CARVE_ASSERT(next_i != edge_graph.end());
        connected.push_back(vmap[next]);
        (*prior_i).second.erase(next);
        (*next_i).second.erase(prior);
        if (!(*prior_i).second.size()) { edge_graph.erase(prior_i); prior_i = edge_graph.end(); }
        if (!(*next_i).second.size()) { edge_graph.erase(next_i); next_i = edge_graph.end(); }
        prior_i = next_i;
        prior = next;
      }
      bool closed = connected.front() == connected.back();
      for (size_t k = 0; k < connected.size(); ++k) {
        std::cerr << " " << connected[k];
      }
      std::cerr << std::endl;
      intersection_graph.addPolyline(closed, connected.begin(), connected.end());
    }

#if defined(CARVE_DEBUG_WRITE_PLY_DATA)
    std::string out("/tmp/intersection.ply");
    ::writePLY(out, &intersection_graph, true);
#endif
  }

  std::cerr << "*** edge graph consistency test done" << std::endl;
#endif
}
Пример #17
0
int main(int argc, char **argv) {
  carve::mesh::MeshSet<3> *a = makeTorus(30, 30, 2.0, 0.8, carve::math::Matrix::ROT(0.5, 1.0, 1.0, 1.0));
  

  carve::input::PolyhedronData data;

  for (int i = 0; i < DIM; i++) {
    double x = -3.0 + 6.0 * i / double(DIM - 1);
    for (int j = 0; j < DIM; j++) {
      double y = -3.0 + 6.0 * j / double(DIM - 1);
      double z = -1.0 + 2.0 * cos(sqrt(x * x + y * y) * 2.0) / sqrt(1.0 + x * x + y * y);
      size_t n = data.addVertex(carve::geom::VECTOR(x, y, z));
      if (i && j) {
        data.addFace(n - DIM - 1, n - 1, n - DIM);
        data.addFace(n - 1, n, n - DIM);
      }
    }
  }

  for (int i = 0; i < DIM; i++) {
    double x = -3.0 + 6.0 * i / double(DIM - 1);
    for (int j = 0; j < DIM; j++) {
      double y = -3.0 + 6.0 * j / double(DIM - 1);
      double z = 1.0 + 2.0 * cos(sqrt(x * x + y * y) * 2.0) / sqrt(1.0 + x * x + y * y);
      size_t n = data.addVertex(carve::geom::VECTOR(x, y, z));
      if (i && j) {
        data.addFace(n - DIM - 1, n - 1, n - DIM);
        data.addFace(n - 1, n, n - DIM);
      }
    }
  }

  carve::mesh::MeshSet<3> *b = data.createMesh(carve::input::opts());
  CARVE_ASSERT(b->meshes.size() == 2);

  Between between_collector(a, b);
  carve::mesh::MeshSet<3> *c = carve::csg::CSG().compute(a, b, between_collector, NULL, carve::csg::CSG::CLASSIFY_EDGE);

  TestScene *scene = new TestScene(argc, argv, 3);

  glNewList(scene->draw_list_base + 0, GL_COMPILE);
  drawMeshSet(a, .4, .6, .8, 1.0);
  glEndList();

  glNewList(scene->draw_list_base + 1, GL_COMPILE);
  drawMeshSet(b, .8, .6, .4, 1.0);
  glEndList();

  glNewList(scene->draw_list_base + 2, GL_COMPILE);
  drawMeshSet(c, .2, .2, .8, 1.0);
  drawMeshSetWireframe(c, -1, false, false);
  glEndList();

  scene->run();

  delete scene;

  delete a;
  delete b;
  delete c;

  return 0;
}
      static void walkGraphSegment(carve::csg::detail::VVSMap &shared_edge_graph,
                                   const carve::csg::detail::VSet &branch_points,
                                   V2 initial,
                                   const carve::csg::detail::LoopEdges & /* a_edge_map */,
                                   const carve::csg::detail::LoopEdges & /* b_edge_map */,
                                   std::list<V2> &out) {
        V2 curr;
        curr = initial;
        bool closed = false;

        out.clear();
        for (;;) {
          // walk forward.
          out.push_back(curr);
          remove(curr, shared_edge_graph);

          if (curr.second == initial.first) { closed = true; break; }
          if (branch_points.find(curr.second) != branch_points.end()) break;
          carve::csg::detail::VVSMap::const_iterator o = shared_edge_graph.find(curr.second);
          if (o == shared_edge_graph.end()) break;
          CARVE_ASSERT((*o).second.size() == 1);
          curr.first = curr.second;
          curr.second = *((*o).second.begin());
          // test here that the set of incident groups hasn't changed.
        }

        if (!closed) {
          // walk backward.
          curr = initial;
          for (;;) {
            if (branch_points.find(curr.first) != branch_points.end()) break;
            carve::csg::detail::VVSMap::const_iterator o = shared_edge_graph.find(curr.first);
            if (o == shared_edge_graph.end()) break;
            curr.second = curr.first;
            curr.first = *((*o).second.begin());
            // test here that the set of incident groups hasn't changed.

            out.push_front(curr);
            remove(curr, shared_edge_graph);
          }
        }

#if defined(CARVE_DEBUG)
        std::cerr << "intersection segment: " << out.size() << " edges." << std::endl;
#if defined(DEBUG_DRAW_INTERSECTION_LINE)
        {
          static float H = 0.0, S = 1.0, V = 1.0;
          float r, g, b;

          H = fmod((H + .37), 1.0);
          S = 0.5 + fmod((S - 0.37), 0.5);
          carve::colour::HSV2RGB(H, S, V, r, g, b);

          if (out.size() > 1) {
            drawEdges(out.begin(), ++out.begin(),
                      0.0, 0.0, 0.0, 1.0,
                      r, g, b, 1.0,
                      3.0);
            drawEdges(++out.begin(), --out.end(),
                      r, g, b, 1.0,
                      r, g, b, 1.0,
                      3.0);
            drawEdges(--out.end(), out.end(),
                      r, g, b, 1.0,
                      1.0, 1.0, 1.0, 1.0,
                      3.0);
          } else {
            drawEdges(out.begin(), out.end(),
                      r, g, b, 1.0,
                      r, g, b, 1.0,
                      3.0);
          }
        }
#endif
#endif
      }