Пример #1
0
/**
 * \brief Create a new ass_font_t according to "desc" argument
 */
ass_font_t* ass_font_new(ass_library_t* library, FT_Library ftlibrary, void* fc_priv, ass_font_desc_t* desc)
{
	int error;
	ass_font_t* fontp;
	ass_font_t font;

	fontp = ass_font_cache_find(desc);
	if (fontp)
		return fontp;
	
	font.library = library;
	font.ftlibrary = ftlibrary;
	font.n_faces = 0;
	font.desc.family = strdup(desc->family);
	font.desc.bold = desc->bold;
	font.desc.italic = desc->italic;

	font.scale_x = font.scale_y = 1.;
	font.v.x = font.v.y = 0;
	font.size = 0.;

	error = add_face(fc_priv, &font, 0);
	if (error == -1) {
		free(font.desc.family);
		return 0;
	} else
		return ass_font_cache_add(&font);
}
Пример #2
0
/**
 * \brief Create a new ASS_Font according to "desc" argument
 */
ASS_Font *ass_font_new(Cache *font_cache, ASS_Library *library,
                       FT_Library ftlibrary, ASS_FontSelector *fontsel,
                       ASS_FontDesc *desc)
{
    int error;
    ASS_Font *fontp;
    ASS_Font font;

    fontp = ass_cache_get(font_cache, desc);
    if (fontp)
        return fontp;

    font.library = library;
    font.ftlibrary = ftlibrary;
    font.shaper_priv = NULL;
    font.n_faces = 0;
    font.desc.family = strdup(desc->family);
    font.desc.bold = desc->bold;
    font.desc.italic = desc->italic;
    font.desc.vertical = desc->vertical;

    font.scale_x = font.scale_y = 1.;
    font.v.x = font.v.y = 0;
    font.size = 0.;

    error = add_face(fontsel, &font, 0);
    if (error == -1) {
        free(font.desc.family);
        return 0;
    } else
        return ass_cache_put(font_cache, &font.desc, &font);
}
Пример #3
0
int	get_coord(char *path, t_vertice **v, t_vertice_n **vn, t_face **f)
{
  int	fd;
  char	*buff;
  char	**sep;

  fd = open(path, O_RDONLY);
  if (fd == -1)
    exit(1);
  while ((buff = get_next_line(fd)))
    {
      sep = my_strtok(buff, " ", TOK_DELIM);
      if (sep && size_tab(sep) == 4)
	{
	  if (!my_strcmp("v", sep[0]))
	    add_vertice(v, atof(sep[1]), atof(sep[2]),
			atof(sep[3]));
	  else if (!my_strcmp("f", sep[0]))
	    add_face(f, sep[1], sep[2], sep[3]);
	  else if (!my_strcmp("vn", sep[0]))
	    add_vertice_norme(vn, atof(sep[1]), atof(sep[2]),
			      atof(sep[3]));
	}
    }
  close(fd);
 return (1);
}
Пример #4
0
obj_t::obj_t( const char * objname ) 
{
    // long name
    fullpath = realname( objname ); 

    // model handle
    const char * shortname = strip_path_and_extension( objname );
    name = copy_string( shortname );

    // memory map file
    mmfile_t mm( fullpath ); 
    index = 0;
    size = mm.size;
    data = mm.data;
    
    
    while ( index < size ) 
    {
        strip_white_space();

        switch( data[i] ) 
        {
        case 'm':
            if ( check_name( "mtllib" ) )
                consume_line();
            break;
        case 'u':
            if ( check_name( "usemtl" ) )
                consume_line();
            break;
        case 'g':
            consume_line();
            break;
        case '#':
            strip_white_space();
            break;
        case 'v':
            if ( check_name( "vn" ) )
                add_normal();
            else if ( check_name( "vt" ) )
                add_texcoord();
            else if ( data[index+1] == ' ' )
                add_vertex();
            else
                core.warn( "obj_t: straggling v in objfile: %s", name );
            break;
        case 'f':
            if ( data[index+1] == ' ' ) 
                add_face();
            break;
        case 's':
        default:
            ++index;
            break;
        }
    }
}
Пример #5
0
Файл: r.c Проект: wpwrak/subosm
static void triangulate(void)
{
	FILE *files[2];
	unsigned i, faces;
	unsigned a, b, c;
	int n;

	/* start qdelaunay */

	if (popen2("qdelaunay Qt i", files) < 0) {
		perror("popen2");
		exit(1);
	}

	/* send list of points */

	if (fprintf(files[1], "2\n%u\n", n_nodes) < 0) {
		perror("fprintf");
		exit(1);
	}
	for (i = 0; i != n_nodes; i++) {
		if (fprintf(files[1], "%d %d\n", node[i].x, node[i].y) < 0) {
			perror("fprintf");
			exit(1);
		}
	}
	if (fclose(files[1]) < 0) {
		perror("fclose");
		exit(1);
	}

	/* read list of faces (triangles) */

	n = fscanf(files[0], "%u", &faces);
	if (n < 0) {
		perror("fscanf");
		exit(1);
	}
	if (n != 1) {
		fprintf(stderr, "bad result (length)\n");
		exit(1);
	}
	for (i = 0; i != faces; i++) {
		n = fscanf(files[0], "%u %u %u", &a, &b, &c);
		if (n < 0) {
			perror("fscanf");
			exit(1);
		}
		if (n != 3) {
			fprintf(stderr, "bad result (face)\n");
			exit(1);
		}
		add_face(a, b, c);
	}
	fclose(files[0]);
}
Пример #6
0
Surface_mesh::Face
Surface_mesh::
add_triangle(Vertex v0, Vertex v1, Vertex v2)
{
    std::vector<Vertex> v(3);
    v[0] = v0;
    v[1] = v1;
    v[2] = v2;
    return add_face(v);
}
Пример #7
0
Surface_mesh::Face
Surface_mesh::
add_quad(Vertex v0, Vertex v1, Vertex v2, Vertex v3)
{
    std::vector<Vertex> v(4);
    v[0] = v0;
    v[1] = v1;
    v[2] = v2;
    v[3] = v3;
    return add_face(v);
}
Пример #8
0
voxel_model::voxel_model(const Layer& layer, const LayerType& layer_type)
  : name_(layer_type.name), invalidated_(false), model_(1.0f), proto_model_(1.0f)
{
	aabb_[0] = glm::vec3(std::numeric_limits<float>::max(),std::numeric_limits<float>::max(),std::numeric_limits<float>::max());
	aabb_[1] = glm::vec3(std::numeric_limits<float>::min(),std::numeric_limits<float>::min(),std::numeric_limits<float>::min());

	for(const std::pair<std::string, VoxelPos>& pivot : layer_type.pivots) {
		glm::vec3 point = glm::vec3(pivot.second) + glm::vec3(0.5f);

		pivots_.push_back(std::pair<std::string, glm::vec3>(pivot.first, point));
	}

	vbo_id_.reset(new GLuint, [](GLuint* id){glDeleteBuffers(1, id); delete id;});
	glGenBuffers(1, vbo_id_.get());

	std::vector<GLfloat> varray[6];
	std::vector<GLubyte> carray[6];

	for(auto p : layer.map) {
		for(int n = FACE_LEFT; n != MAX_FACES; ++n) {
			glm::ivec3 pos = glm::ivec3(normal_vectors()[n]) + p.first;
			if(layer.map.find(pos) == layer.map.end()) {
				add_face(n, p, varray[n], carray[n]);
			}
		}
	}

	size_t total_size = 0;
	for(int n = FACE_LEFT; n != MAX_FACES; ++n) {
		vattrib_offsets_[n] = total_size;
		total_size += varray[n].size() * sizeof(GLfloat);
		num_vertices_[n] = varray[n].size() / 3;
	}
	for(int n = FACE_LEFT; n != MAX_FACES; ++n) {
		cattrib_offsets_[n] = total_size;
		total_size += carray[n].size() * sizeof(uint8_t);
	}
	glBindBuffer(GL_ARRAY_BUFFER, *vbo_id_);
	glBufferData(GL_ARRAY_BUFFER, total_size, NULL, GL_STATIC_DRAW);
	for(int n = FACE_LEFT; n != MAX_FACES; ++n) {
		glBufferSubData(GL_ARRAY_BUFFER, vattrib_offsets_[n], varray[n].size()*sizeof(GLfloat), &varray[n][0]);
	}
	for(int n = FACE_LEFT; n != MAX_FACES; ++n) {
		glBufferSubData(GL_ARRAY_BUFFER, cattrib_offsets_[n], carray[n].size()*sizeof(uint8_t), &carray[n][0]);
	}
	glBindBuffer(GL_ARRAY_BUFFER, 0);
}
Пример #9
0
/**
 * \brief Create a new ASS_Font according to "desc" argument
 */
ASS_Font *ass_font_new(Cache *font_cache, ASS_Library *library,
                       FT_Library ftlibrary, ASS_FontSelector *fontsel,
                       ASS_FontDesc *desc)
{
    ASS_Font *font;
    if (ass_cache_get(font_cache, desc, &font)) {
        if (font->desc.family)
            return font;
        ass_cache_dec_ref(font);
        return NULL;
    }
    if (!font)
        return NULL;

    font->library = library;
    font->ftlibrary = ftlibrary;
    font->shaper_priv = NULL;
    font->n_faces = 0;
    ASS_FontDesc *new_desc = ass_cache_key(font);
    font->desc.family = new_desc->family;
    font->desc.bold = desc->bold;
    font->desc.italic = desc->italic;
    font->desc.vertical = desc->vertical;

    font->scale_x = font->scale_y = 1.;
    font->v.x = font->v.y = 0;
    font->size = 0.;

    int error = add_face(fontsel, font, 0);
    if (error == -1) {
        font->desc.family = NULL;
        ass_cache_commit(font, 1);
        ass_cache_dec_ref(font);
        return NULL;
    }
    ass_cache_commit(font, 1);
    return font;
}
Пример #10
0
void add_surface(const Chunk& chunk, meshmap_t& meshes, surface_t& surface, const Plane plane, const Side side)
{
	uint_fast8_t ix, iy, iz;
	if(plane == Plane::XY)
	{
		ix = 0;
		iy = 2;
		iz = 1;
	}
	else if(plane == Plane::XZ)
	{
		ix = 0;
		iy = 1;
		iz = 2;
	}
	else // must be YZ
	{
		ix = 1;
		iy = 0;
		iz = 2;
	}

	u8vec3 xyz;
	for(xyz[1] = 0; xyz[1] < CHUNK_SIZE; ++xyz[1])
	{
		generate_surface(chunk, surface, xyz, { ix, iy, iz }, static_cast<int_fast8_t>(side));

		while(true)
		{
			const Rectangle rekt = yield_rectangle(surface);
			if(std::get<0>(rekt.key) == BlockType::none)
			{
				break;
			}

			mesh_vertex_coord_t v1;
			mesh_vertex_coord_t v2;
			mesh_vertex_coord_t v3;
			mesh_vertex_coord_t v4;

			uint_fast8_t y1 = xyz[1];
			if(side == Side::top)
			{
				y1 += 1;
			}

			#define s(x) static_cast<mesh_vertex_coord_t::value_type>(x)
			v1[ix] = rekt.x;
			v1[iy] = y1;
			v1[iz] = rekt.z;

			v2[ix] = s(rekt.x + rekt.w);
			v2[iy] = y1;
			v2[iz] = rekt.z;

			v3[ix] = s(rekt.x + rekt.w);
			v3[iy] = y1;
			v3[iz] = s(rekt.z + rekt.h);

			v4[ix] = rekt.x;
			v4[iy] = y1;
			v4[iz] = s(rekt.z + rekt.h);
			#undef s

			mesh_t& mesh = meshes[rekt.key];
			if((plane == Plane::XZ && side == Side::top)
			|| (plane == Plane::XY && side == Side::bottom)
			|| (plane == Plane::YZ && side == Side::bottom))
			{
				add_face(mesh, v4, v3, v2, v1);
			}
			else
			{
				add_face(mesh, v1, v2, v3, v4);
			}
		}
	}
}
Пример #11
0
void
test_validity(Mesh& mesh)
{
  typedef typename boost::graph_traits<Mesh>::vertex_descriptor vertex_descriptor;
  typedef typename boost::graph_traits<Mesh>::edge_descriptor edge_descriptor;
  typedef typename boost::graph_traits<Mesh>::face_descriptor face_descriptor;
  typedef typename boost::property_map<Mesh, CGAL::vertex_point_t>::type VPMap;
  VPMap vpmap = get(CGAL::vertex_point, mesh);
  vertex_descriptor vertices[4];
  edge_descriptor edges[4];
  vertices[0] = add_vertex(mesh);
  vertices[1] = add_vertex(mesh);
  vertices[2] = add_vertex(mesh);
  vertices[3] = add_vertex(mesh);
  
  put(vpmap, vertices[0], Point_3(0,0,0));
  put(vpmap, vertices[1], Point_3(1,0,0));
  put(vpmap, vertices[2], Point_3(1,1,0));
  put(vpmap, vertices[3], Point_3(0,1,0));
  
  edges[0] = add_edge(mesh); 
  edges[1] = add_edge(mesh); 
  edges[2] = add_edge(mesh); 
  edges[3] = add_edge(mesh); 
  
  assert(!CGAL::is_valid_halfedge_graph(mesh));
  for(int i=0; i<4; ++i)
  {
    set_target(halfedge(edges[i], mesh), vertices[i], mesh);
    set_halfedge(vertices[i], halfedge(edges[i], mesh), mesh);
  }
  
  for(int i=0; i<4; ++i)
    set_target(opposite(halfedge(edges[i], mesh), mesh), vertices[(i+1)%4], mesh);
  for(int i=0; i<4; ++i)
  {
    set_next(halfedge(edges[(i+1)%4], mesh), halfedge(edges[i], mesh), mesh);
    set_next(opposite(halfedge(edges[i], mesh), mesh), 
        opposite(halfedge(edges[(i+1)%4], mesh), mesh), mesh);
  }
  
  assert(CGAL::is_valid_halfedge_graph(mesh));
  face_descriptor faces[1];
  faces[0] = add_face(mesh);
  assert(!CGAL::is_valid_face_graph(mesh));
  
  for(int i=0; i<4; ++i)
  {
    set_face(opposite(halfedge(edges[i], mesh), mesh), faces[0], mesh);
  }
  set_halfedge(faces[0], opposite(halfedge(edges[0], mesh), mesh), mesh);
  assert(CGAL::is_valid_face_graph(mesh));
  assert(CGAL::is_valid_polygon_mesh(mesh));
  
  Mesh dummy;
  vertices[0] = add_vertex(dummy);
  vertices[1] = add_vertex(dummy);
  edges[0] = add_edge(dummy);
  set_target(halfedge(edges[0], dummy), vertices[0], dummy);
  set_halfedge(vertices[0], halfedge(edges[0], dummy), dummy);
  set_target(opposite(halfedge(edges[0], dummy), dummy), vertices[1], dummy);
  set_halfedge(vertices[1], opposite(halfedge(edges[0], dummy), dummy), dummy);
  set_next(halfedge(edges[0], dummy), opposite(halfedge(edges[0], dummy), dummy), dummy);
  set_next(opposite(halfedge(edges[0], dummy), dummy), halfedge(edges[0], dummy), dummy);
  faces[0] = add_face(dummy);
  set_halfedge(faces[0], opposite(halfedge(edges[0], dummy), dummy), dummy);
  set_face(halfedge(edges[0], dummy), faces[0], dummy);
  set_face(opposite(halfedge(edges[0], dummy), dummy), faces[0], dummy);
  assert(CGAL::is_valid_face_graph(dummy));
  assert(!CGAL::is_valid_polygon_mesh(dummy));
  
}
Пример #12
0
/**
 * \brief Get a glyph
 * \param ch character code
 **/
FT_Glyph ass_font_get_glyph(void* fontconfig_priv, ass_font_t* font, uint32_t ch, ass_hinting_t hinting)
{
	int error;
	int index = 0;
	int i;
	FT_Glyph glyph;
	FT_Face face = 0;
	int flags = 0;

	if (ch < 0x20)
		return 0;
	if (font->n_faces == 0)
		return 0;

	for (i = 0; i < font->n_faces; ++i) {
		face = font->faces[i];
		index = FT_Get_Char_Index(face, ch);
		if (index)
			break;
	}

#ifdef HAVE_FONTCONFIG
	if (index == 0) {
		int face_idx;
		mp_msg(MSGT_ASS, MSGL_INFO, MSGTR_LIBASS_GlyphNotFoundReselectingFont,
		       ch, font->desc.family, font->desc.bold, font->desc.italic);
		face_idx = add_face(fontconfig_priv, font, ch);
		if (face_idx >= 0) {
			face = font->faces[face_idx];
			index = FT_Get_Char_Index(face, ch);
			if (index == 0) {
				mp_msg(MSGT_ASS, MSGL_ERR, MSGTR_LIBASS_GlyphNotFound,
				       ch, font->desc.family, font->desc.bold, font->desc.italic);
			}
		}
	}
#endif

	switch (hinting) {
	case ASS_HINTING_NONE: flags = FT_LOAD_NO_HINTING; break;
	case ASS_HINTING_LIGHT: flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT; break;
	case ASS_HINTING_NORMAL: flags = FT_LOAD_FORCE_AUTOHINT; break;
	case ASS_HINTING_NATIVE: flags = 0; break;
	}
	
	error = FT_Load_Glyph(face, index, FT_LOAD_NO_BITMAP | flags);
	if (error) {
		mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_ErrorLoadingGlyph);
		return 0;
	}
	
#if (FREETYPE_MAJOR > 2) || \
    ((FREETYPE_MAJOR == 2) && (FREETYPE_MINOR >= 2)) || \
    ((FREETYPE_MAJOR == 2) && (FREETYPE_MINOR == 1) && (FREETYPE_PATCH >= 10))
// FreeType >= 2.1.10 required
	if (!(face->style_flags & FT_STYLE_FLAG_ITALIC) && 
			(font->desc.italic > 55)) {
		FT_GlyphSlot_Oblique(face->glyph);
	}
#endif
	error = FT_Get_Glyph(face->glyph, &glyph);
	if (error) {
		mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_ErrorLoadingGlyph);
		return 0;
	}
	
	return glyph;
}
Пример #13
0
// This routine assumes the sentinel points have already been added, and processes points in order
GEODE_NEVER_INLINE static Ref<MutableTriangleTopology> deterministic_exact_delaunay(RawField<const Perturbed2,VertexId> X, const bool validate) {

  const int n = X.size()-3;
  const auto mesh = new_<MutableTriangleTopology>();
  IntervalScope scope;

  // Initialize the mesh to a Delaunay triangle containing the sentinels at infinity.
  mesh->add_vertices(n+3);
  mesh->add_face(vec(VertexId(n+0),VertexId(n+1),VertexId(n+2)));
  if (self_check) {
    mesh->assert_consistent();
    assert_delaunay("self check 0: ",mesh,X);
  }

  // The randomized incremental construction algorithm uses the history of the triangles
  // as the acceleration structure.  Specifically, we maintain a BSP tree where the nodes
  // are edge tests (are we only the left or right of an edge) and the leaves are triangles.
  // There are two operations that modify this tree:
  //
  // 1. Split: A face is split into three by the insertion of an interior vertex.
  // 2. Flip: An edge is flipped, turning two triangles into two different triangles.
  //
  // The three starts out empty, since one triangle needs zero tests.
  Array<Node> bsp; // All BSP nodes including leaves
  bsp.preallocate(3*n); // The minimum number of possible BSP nodes
  Field<Vector<int,2>,FaceId> face_to_bsp; // Map from FaceId to up to two BSP leaf points (2*node+(right?0:1))
  face_to_bsp.flat.preallocate(2*n+1); // The exact maximum number of faces
  face_to_bsp.flat.append_assuming_enough_space(vec(0,-1)); // By the time we call set_links, node 0 will be valid
  if (self_check)
    check_bsp(*mesh,bsp,face_to_bsp,X);

  // Allocate a stack to simulate recursion when flipping non-Delaunay edges.
  // Invariant: if edge is on the stack, the other edges of face(edge) are Delaunay.
  // Since halfedge ids change during edge flips in a corner mesh, we store half edges as directed vertex pairs.
  Array<Tuple<HalfedgeId,Vector<VertexId,2>>> stack;
  stack.preallocate(8);

  // Insert all vertices into the mesh in random order, maintaining the Delaunay property
  for (const auto i : range(n)) {
    const VertexId v(i);
    check_interrupts();

    // Search through the BSP tree to find the containing triangle
    const auto f0 = bsp_search(bsp,X,v);
    const auto vs = mesh->vertices(f0);

    // Split the face by inserting the new vertex and update the BSP tree accordingly.
    mesh->split_face(f0,v);
    const auto e0 = mesh->halfedge(v),
               e1 = mesh->left(e0),
               e2 = mesh->right(e0);
    assert(mesh->dst(e0)==vs.x);
    const auto f1 = mesh->face(e1),
               f2 = mesh->face(e2);
    const int base = bsp.extend(3,uninit);
    set_links(bsp,face_to_bsp[f0],base);
    bsp[base+0] = Node(vec(v,vs.x),base+2,base+1);
    bsp[base+1] = Node(vec(v,vs.y),~f0.id,~f1.id);
    bsp[base+2] = Node(vec(v,vs.z),~f1.id,~f2.id);
    face_to_bsp[f0] = vec(2*(base+1)+0,-1);
    face_to_bsp.flat.append_assuming_enough_space(vec(2*(base+1)+1,2*(base+2)+0));
    face_to_bsp.flat.append_assuming_enough_space(vec(2*(base+2)+1,-1));
    if (self_check) {
      mesh->assert_consistent();
      check_bsp(*mesh,bsp,face_to_bsp,X);
    }

    // Fix all non-Delaunay edges
    stack.copy(vec(tuple(mesh->next(e0),vec(vs.x,vs.y)),
                   tuple(mesh->next(e1),vec(vs.y,vs.z)),
                   tuple(mesh->next(e2),vec(vs.z,vs.x))));
    if (self_check)
      assert_delaunay("self check 1: ",mesh,X,Tuple<>(),true);
    while (stack.size()) {
      const auto evs = stack.pop();
      auto e = mesh->vertices(evs.x)==evs.y ? evs.x : mesh->halfedge(evs.y.x,evs.y.y);
      if (e.valid() && !is_delaunay(*mesh,X,e)) {
        // Our mesh is linearly embedded in the plane, so edge flips are always safe
        assert(mesh->is_flip_safe(e));
        e = mesh->unsafe_flip_edge(e);
        GEODE_ASSERT(is_delaunay(*mesh,X,e));
        // Update the BSP tree for the triangle flip
        const auto f0 = mesh->face(e),
                   f1 = mesh->face(mesh->reverse(e));
        const int node = bsp.append(Node(mesh->vertices(e),~f1.id,~f0.id));
        set_links(bsp,face_to_bsp[f0],node);
        set_links(bsp,face_to_bsp[f1],node);
        face_to_bsp[f0] = vec(2*node+1,-1);
        face_to_bsp[f1] = vec(2*node+0,-1);
        if (self_check) {
          mesh->assert_consistent();
          check_bsp(*mesh,bsp,face_to_bsp,X);
        }
        // Recurse to successor edges to e
        const auto e0 = mesh->next(e),
                   e1 = mesh->prev(mesh->reverse(e));
        stack.extend(vec(tuple(e0,mesh->vertices(e0)),
                         tuple(e1,mesh->vertices(e1))));
        if (self_check)
          assert_delaunay("self check 2: ",mesh,X,Tuple<>(),true);
      }
    }
    if (self_check) {
      mesh->assert_consistent();
      assert_delaunay("self check 3: ",mesh,X);
    }
  }

  // Remove sentinels
  for (int i=0;i<3;i++)
    mesh->erase_last_vertex_with_reordering();

  // If desired, check that the final mesh is Delaunay
  if (validate)
    assert_delaunay("delaunay validate: ",mesh,X);

  // Return the mesh with the sentinels removed
  return mesh;
}
Пример #14
0
int RunGLTest (void)
{

#ifdef GL
	int mm=1;
#else
	int mm = 0;
#endif
	int mode=0;
	cam.x=0;
	cam.y=0;
	int bpp;
	int w = 200;
	int h = 200;
	int done = 0;
	int shrink=0;
	int grow=0;
	int gofullscreen=0;
	int mustresize = 1;
	int justresized = 0;
	xy  ss = parsemodes(w,h,"mode",1,0,0);
	printf("wtf\n");
	if (ss.x!=-1){w=ss.x;h=ss.y;};
	SDL_Surface* s;
#ifdef GL
	s=initsdl(w,h,&bpp,SDL_OPENGL

#else
	gltextsdlsurface=s=initsdl(w,h,&bpp,
#endif

	+0);printf("inito\n");
	SDL_InitSubSystem( SDL_INIT_TIMER);
	SDL_EnableUNICODE(1);
	SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY/2, SDL_DEFAULT_REPEAT_INTERVAL*2);
#ifdef GL
	newtermmsg=GetFileIntoCharPointer1("newtermmsg");
	printf("pretty far\n");
	wm(w,h);
	int down=0;
	glEnable(GL_BLEND);
	glShadeModel(GL_FLAT);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE);
	glClearColor( 0.0, 0.0, 0.04, 0.0 );
	glLineWidth(lv);
#else
	gltextsdlsurface=s;
#endif
	roteface *face1;
	roteface *activeface;
	face1=add_face();
	activeface =face1 ;
	face1->next=add_face();
	printf("still?\n");
	add_terminal(face1);
	printf("2threaad\n");
	loadl2();
	struct state *nerv=0;
#ifdef nerve
	nerv=nerverot_init(w,h);
#endif

	int dirty=1;
	printf("mainloop descent commencing\n");
	while( !done )
	{	
	        lockterms(face1);


		if(dirty||faces_dirty(face1))
		{
			dirty=0;
			facesclean(face1);
#ifdef GL
			glClear(GL_COLOR_BUFFER_BIT);
#else
			SDL_FillRect	( s, NULL, 0 );

#endif

#ifndef GL

#else

				if(nerv)
				{
				    shownerv(nerv);
				    dirty=1;
				}
				glPushMatrix();
				glScalef(sx,sy,0.004);
				glTranslatef(cam.x,cam.y,0);
				

#endif
				Uint8 * k;
				int integer;

				k=SDL_GetKeyState(&integer);
				if(k[SDLK_RCTRL])
					focusline(activeface);
				int nf;
				switch(mode)
				{
				    case 0:
					showfaces(face1);
				    break;
#ifdef GL
				    case 1:
					krychlus(face1);
				    break;
#endif
				}
#ifdef GL
				glPopMatrix();
#endif


#ifndef GL
			SDL_UpdateRect(s,0,0,0,0);
#else
			SDL_GL_SwapBuffers( );
#endif
			facesclean(face1);
			
		}
#ifdef GL
		GLenum gl_error;
		gl_error = glGetError( );
		if( gl_error != GL_NO_ERROR )
		{
			if(gl_error==GL_STACK_OVERFLOW)
				printf("QUACK QUACK QUACK, OVERFLOVING STACK\n");
			else if(gl_error==GL_INVALID_OPERATION)
				printf("INVALID OPERATION, PATIENT EXPLODED\n");
			else	fprintf( stderr, "testgl: OpenGL error: %d\n", gl_error );
			
		}
#endif
		char* sdl_error;
		sdl_error = SDL_GetError( );
		if( sdl_error[0] != '\0' )
		{
		    fprintf(stderr, "testgl: SDL error '%s'\n", sdl_error);
		    SDL_ClearError();
		}

		SDL_TimerID x=0;
		if(dirty)
		    x= SDL_AddTimer(55, NewTimerCallback, 0);
		                     
		unlockterms(face1);
//		printf("---------unlocked wating\n");
		SDL_Event event;
		if(SDL_WaitEvent( &event ))
		{
	    	    lockterms(face1);
//		    printf("---------locked goooin %i\n", event.type);
		    if(x)SDL_RemoveTimer(x);x=0;	    	    
		    do {
			int mod=event.key.keysym.mod;
			int key=event.key.keysym.sym;
    			Uint8 *keystate = SDL_GetKeyState(NULL);

			switch( event.type )
			{
			
#ifdef GL
				case SDL_MOUSEMOTION:
				if((SDL_BUTTON(1)|SDL_BUTTON(2))&SDL_GetMouseState(0,0))
				{
					activeface->x+=event.motion.xrel;
					activeface->y+=event.motion.yrel;
				}
				if((SDL_BUTTON(3)|SDL_BUTTON(2))&SDL_GetMouseState(0,0))
				{
					cam.x-=event.motion.xrel;
					cam.y-=event.motion.yrel;
				}
				
				break;
#endif
				case SDL_KEYUP:
				{
					if ( (key == SDLK_RCTRL) )
					{
						dirty=1;
					}
				}
				break;
				case SDL_KEYDOWN:
				

					if(mod&KMOD_RSHIFT&&(key==SDLK_PAGEUP||key==SDLK_PAGEDOWN))
					{
						if(key==SDLK_PAGEUP)
							tscroll+=9;
						if(key==SDLK_PAGEDOWN)
							tscroll-=9;
						if(tscroll<0)tscroll=0;
//						printf("scroll:%i,logl:%i, log&%i, t:%i ,b:%i\n", tscroll,activeface->t->logl, activeface->t->log,activeface->t->scrolltop,activeface->t->scrollbottom);
					}
					else
					if(key==SDLK_RCTRL||mod&KMOD_RCTRL)
					{
						dirty=1;
						switch (key)
						{
							case SDLK_TAB:
							    cycle(face1, &activeface);
							break;
							case SDLK_F2:
							    gofullscreen=1;
							break;
							case SDLK_F3:
							    rastio+=0.05;
							    mustresize=1;
							    dirty=1;
							break;
							case SDLK_F4:
							    rastio-=0.05;
							    mustresize=1;
							    dirty=1;
							break;
							case SDLK_F5:
							    sx-=0.001;
							    sy+=0.001;
							break;
							case SDLK_F6:
							    sx+=0.001;
							    sy-=0.001;
							break;

							case SDLK_F7:
							    savemode(w,h);
							break;
							case SDLK_F8:
							    loadl2();
							break;
							case SDLK_p:
							    saveScreenshot();
							break;
#ifdef GL
							case SDLK_F9:
							    lv-=1;	glLineWidth(lv);
							break;
							case SDLK_F10:
							    lv+=1;	glLineWidth(lv);
							break;
#endif
							case SDLK_F11:
							    shrink=1;
							break;
							case SDLK_F12:
							    grow=1;
							break;
							case SDLK_PAGEUP:
							     mode++;
							     if(mode>mm)mode= mm;
							break;
							case SDLK_INSERT:
							     mode--;
							     if(mode<0)mode= 0;
							break;
							case SDLK_END:
							    resizooo(activeface, 0,1,keystate);
							break;
							case SDLK_HOME:
							    resizooo(activeface, 0,-1,keystate);
							break;
							case SDLK_DELETE:
							    resizooo(activeface, -1,0,keystate);
							break;
							case SDLK_PAGEDOWN:
							    resizooo(activeface, 1,0,keystate);
							break;
#ifdef nerve
							case SDLK_F1:
								if(nerv)
								{
									nerverot_free(nerv);
							        	dirty=1;
									nerv=0;
								}
								else
								{
							        	nerv=nerverot_init(w,h);
									
									dirty=1;
								}
							break;
#endif
						}
					}
					else
					{
					    if(activeface->t==0)
					    {
						printf("debug messages r fun\n");
						add_terminal(activeface);
						activeface->next=add_face();
					    }
					    if ( (key >= SDLK_F1) && (key <= SDLK_F15) )
					    {
						char *k;
						if(asprintf(&k ,"kf%i", key-SDLK_F1+1)!=-1)
						{
						    rote_vt_terminfo(activeface->t, k);
						    free(k);
						}
					    }
					    else
					    if ( (key == SDLK_SPACE) )
						keyp(activeface,32);
					    else
					    #define magic(x) rote_vt_terminfo(activeface->t, x )
					    if ( (key == SDLK_BACKSPACE) )
						magic( "kbs");
					    else
					    if ( (key == SDLK_ESCAPE) )
						keyp(activeface,27);
					    else
					    if ( (key == SDLK_LEFT) )
						magic("kcub1");
					    else
					    if ( (key == SDLK_RIGHT) )
						magic( "kcuf1");
					    else
					    if ( (key == SDLK_UP) )
						magic( "kcuu1");
					    else
					    if ( (key == SDLK_DOWN) )
						magic( "kcud1");
					    else
					    if ( (key == SDLK_END) )
						magic( "kend");
					    else
					    if ( (key == SDLK_HOME) )
						magic("khome");
					    else
					    if ( (key == SDLK_DELETE) )
						magic( "kdch1");
					    else
					    if ( (key == SDLK_PAGEDOWN) )
						magic( "knp");
					    else
					    if ( (key == SDLK_INSERT) )
					    	magic( "kich1");
					    else
					    if ( (key == SDLK_PAGEUP) )
						magic ( "kpp" );
					    else
					    if ( (key == SDLK_RETURN) )
						keyp(activeface,10);
					    else
					    if( event.key.keysym.unicode && ( (event.key.keysym.unicode & 0xFF80) == 0 ) )
						keyp(activeface, event.key.keysym.unicode);
					}
				break;
	 			case SDL_QUIT:
					done = 1;
					break;
#ifndef GL
	 			case SDL_MOUSEBUTTONDOWN:
					rote_vt_mousedown(activeface->t,event.button.x/13, event.button.y/26);
					break;
	 			case SDL_MOUSEBUTTONUP:
					rote_vt_mouseup  (activeface->t,event.button.x/13, event.button.y/26);
					break;
	 			case SDL_MOUSEMOTION:
					rote_vt_mousemove(activeface->t,event.button.x/13, event.button.y/26);
					break;
#endif
				case SDL_VIDEORESIZE:
				    {
					w=event.resize.w;h=event.resize.h;
//					printf("videoresize %i %i\n", w,h);
					dirty=1;
					if (s=SDL_SetVideoMode( w,h, bpp, s->flags ) ) 
//						printf("hmm\n");
					wm(w,h);
				    if(!justresized)

					mustresize=1;
				        justresized=0;
				        
				    }

					break;
				case SDL_USEREVENT:
					if(event.user.code==1)
					    RemoveTerm(&activeface,&face1, event.user.data1);
					break;
			}
		    }
		    while (SDL_PollEvent(&event));
		    if (shrink||grow)
		    { 
		        resize(&w,&h,&bpp,&s->flags,&shrink,&grow);
		        wm(w,h);
		    }
		    if (mustresize)
		    {
			mustresize=0;
			justresized=1;
//			if(activeface->t->cols!=event.resize.w/13/rastio||
//			    activeface->t->rows!=event.resize.h/26/rastio)
				//rote_vt_resize(activeface->t,event.resize.h/26/rastio ,event.resize.w/13/rastio);
		    }
		    if(gofullscreen)
			if(s->flags & SDL_FULLSCREEN )
			{
    			    s=SDL_SetVideoMode( w,h, bpp, (s->flags & ~SDL_FULLSCREEN ));
			
			}
			else
    			    s=SDL_SetVideoMode( w,h, bpp, (s->flags | SDL_FULLSCREEN ));
		    gofullscreen=0;
		    unlockterms(face1);
		}

	}
	SDL_Quit( );
	return(0);
}
Пример #15
0
/******************************************************************************
Write out a jot .sm file.
******************************************************************************/
void
write_sm()
{
   LMESHptr mesh = make_shared<LMESH>();
   
   int i=0;

   //******** Build the mesh ********

   err_adv(debug, "read ply file: %d vertices, %d faces\n", nverts, nfaces);

   err_adv(debug, "building mesh:");

   // Add vertices to mesh
   err_adv(debug, "  adding vertices...");
   for (i = 0; i < nverts; i++)
      mesh->add_vertex(Wpt(vlist[i]->x, vlist[i]->y, vlist[i]->z));
   err_adv(debug, "  done\n");

   // Add per-vertex colors if needed
   if (per_vertex_color) {
      err_adv(debug, "  adding colors...");
      for (i = 0; i < nverts; i++)
         mesh->bv(i)->set_color(COLOR(vlist[i]->r, vlist[i]->g, vlist[i]->b));
      err_adv(debug, "  done\n");
   }

   // Add faces
   err_adv(debug, "  adding faces...");
   for (i = 0; i < nfaces; i++)
      add_face(mesh, flist[i]);
   err_adv(debug, "  done\n");

   //******** Filter the mesh ********

   err_adv(debug, "filtering mesh...");

   // Remove any isolated vertices
   for (i=mesh->nverts()-1; i>=0; i--) {
      if (mesh->bv(i)->degree() == 0) {
         mesh->remove_vertex(mesh->bv(i));
      }
   }
   mesh->changed();

   // Remove duplicate vertices while we're at it
   mesh->remove_duplicate_vertices(false); // don't keep the bastards

   // Check for consistent orientation of normals
   bool is_bad = false;
   for (i=0; i<mesh->nedges(); i++)
      if (!mesh->be(i)->consistent_orientation())
         is_bad = true;
   if (is_bad)
      err_msg("Warning: inconsistently oriented triangles -- can't fix");

   // Optional: recenter mesh
   if (Config::get_var_bool("JOT_RECENTER"))
      mesh->recenter();

   // Optional: print stats
   if (Config::get_var_bool("JOT_PRINT_MESH"))
      mesh->print();

   err_adv(debug, "done\n");

   //******** Write mesh ********

   err_adv(debug, "writing mesh...");
   mesh->write_stream(cout);
   err_adv(debug, "done\n");
}
Пример #16
0
SubdFace *SubdMesh::add_face(int v0, int v1, int v2, int v3)
{
	int index[4] = {v0, v1, v2, v3};
	return add_face(index, 4);
}
Пример #17
0
SubdFace *SubdMesh::add_face(int v0, int v1, int v2)
{
	int index[3] = {v0, v1, v2};
	return add_face(index, 3);
}
Пример #18
0
void OpenSubdMesh::add_face(int v0, int v1, int v2, int v3)
{
	int index[4] = {v0, v1, v2, v3};
	add_face(index, 4);
}
Пример #19
0
/* Add a face with the given vertices */
void add_face(int a, int b, int c) {
	if (faces.len == faces.capacity) {
		/* Double size when we run out... */
		faces.capacity *= 2;
		faces.nodes = (face_t **)realloc(faces.nodes, sizeof(face_t *) * faces.capacity);
	}
	if (vertices.len < a || vertices.len < b || vertices.len < c) {
		/* Frick... */
		fprintf(stderr, "ERROR: Haven't yet collected enough vertices for the face %d %d %d (have %d)!\n", a, b, c, vertices.len);
		exit(1);
	}
	/* Create a new triangle */
	faces.nodes[faces.len] = malloc(sizeof(face_t));
	faces.nodes[faces.len]->a = vertices.nodes[a-1];
	faces.nodes[faces.len]->b = vertices.nodes[b-1];
	faces.nodes[faces.len]->c = vertices.nodes[c-1];
	/* Calculate some normals */
	vertex_t u = {.x = faces.nodes[faces.len]->b->x - faces.nodes[faces.len]->a->x,
				  .y = faces.nodes[faces.len]->b->y - faces.nodes[faces.len]->a->y,
				  .z = faces.nodes[faces.len]->b->z - faces.nodes[faces.len]->a->z};
	vertex_t v = {.x = faces.nodes[faces.len]->c->x - faces.nodes[faces.len]->a->x,
				  .y = faces.nodes[faces.len]->c->y - faces.nodes[faces.len]->a->y,
				  .z = faces.nodes[faces.len]->c->z - faces.nodes[faces.len]->a->z};
	/* Set the face normals */
	faces.nodes[faces.len]->normal.x = ((u.y * v.z) - (u.z * v.y));
	faces.nodes[faces.len]->normal.y = -((u.z * v.x) - (u.x * v.z));
	faces.nodes[faces.len]->normal.z = ((u.x * v.y) - (u.y * v.x));
	faces.len++;
}

void finish_normals() {
	/* Loop through vertices and accumulate normals for them */
	for (uint32_t i = 0; i < faces.len; ++i) {
		/* Vertex a */
		faces.nodes[i]->a->normal.x += faces.nodes[i]->normal.x;
		faces.nodes[i]->a->normal.y += faces.nodes[i]->normal.y;
		faces.nodes[i]->a->normal.z += faces.nodes[i]->normal.z;
		/* Vertex b */
		faces.nodes[i]->b->normal.x += faces.nodes[i]->normal.x;
		faces.nodes[i]->b->normal.y += faces.nodes[i]->normal.y;
		faces.nodes[i]->b->normal.z += faces.nodes[i]->normal.z;
		/* Vertex c */
		faces.nodes[i]->c->normal.x += faces.nodes[i]->normal.x;
		faces.nodes[i]->c->normal.y += faces.nodes[i]->normal.y;
		faces.nodes[i]->c->normal.z += faces.nodes[i]->normal.z;
	}
}

/* Discard the rest of this line */
void toss(FILE * f) {
	while (fgetc(f) != '\n');
}

/* Load a Wavefront Obj model */
void load_wavefront(char * filename) {
	/* Open the file */
	FILE * obj = fopen(filename, "r");
	int collected = 0;
	char d = ' ';
	/* Initialize the lists */
	init_model();
	while (!feof(obj)) {
		/* Scan in a line */
		collected = fscanf(obj, "%c ", &d);
		if (collected == 0) continue;
		switch (d) {
			case 'v':
				{
					/* Vertex */
					float x, y, z;
					collected = fscanf(obj, "%f %f %f\n", &x, &y, &z);
					if (collected < 3) fprintf(stderr, "ERROR: Only collected %d points!\n", collected);
					add_vertex(x, y, z);
				}
				break;
			case 'f':
				{
					/* Face */
					int a, b, c;
					collected = fscanf(obj, "%d %d %d\n", &a, &b, &c);
					if (collected < 3) fprintf(stderr, "ERROR: Only collected %d vertices!\n", collected);
					add_face(a,b,c);
				}
				break;
			default:
				/* Something else that we don't care about */
				toss(obj);
				break;
		}
	}
	/* Finalize the vertex normals */
	finish_normals();
	fclose(obj);
}

/* Vertex, fragment, program */
GLuint v, f, p;

/* Read a file into a buffer and return a pointer to the buffer */
char * readFile(char * filename, int32_t * size) {
	FILE * tex;
	char * texture;
	tex = fopen(filename, "r");
	fseek(tex, 0L, SEEK_END);
	*size = ftell(tex);
	texture = malloc(*size);
	fseek(tex, 0L, SEEK_SET);
	fread(texture, *size, 1, tex);
	fclose(tex);
	return texture;
}
Пример #20
0
// Retriangulate a cavity formed when a constraint edge is inserted, following Shewchuck and Brown.
// The cavity is defined by a counterclockwise list of vertices v[0] to v[m-1] as in Shewchuck and Brown, Figure 5.
static void cavity_delaunay(MutableTriangleTopology& parent_mesh, RawField<const EV,VertexId> X,
                            RawArray<const VertexId> cavity, Random& random) {
  // Since the algorithm generates meshes which may be inconsistent with the outer mesh, and the cavity
  // array may have duplicate vertices, we use a temporary mesh and then copy the triangles over when done.
  // In the temporary, vertices are indexed consecutively from 0 to m-1.
  const int m = cavity.size();
  assert(m >= 3);
  const auto mesh = new_<MutableTriangleTopology>();
  Field<Perturbed2,VertexId> Xc(m,uninit);
  for (const int i : range(m))
    Xc.flat[i] = Perturbed2(cavity[i].id,X[cavity[i]]);
  mesh->add_vertices(m);
  const auto xs = Xc.flat[0],
             xe = Xc.flat[m-1];

  // Set up data structures for prev, next, pi in the paper
  const Field<VertexId,VertexId> prev(m,uninit),
                                 next(m,uninit);
  for (int i=0;i<m-1;i++) {
    next.flat[i] = VertexId(i+1);
    prev.flat[i+1] = VertexId(i);
  }
  const Array<VertexId> pi_(m-2,uninit);
  for (int i=1;i<=m-2;i++)
    pi_[i-1] = VertexId(i);
  #define PI(i) pi_[(i)-1]

  // Randomly shuffle [1,m-2], subject to vertices closer to xs-xe than both their neighbors occurring later
  for (int i=m-2;i>=2;i--) {
    int j;
    for (;;) {
      j = random.uniform<int>(0,i)+1;
      const auto pj = PI(j);
      if (!(   segment_directions_oriented(xe,xs,Xc[pj],Xc[prev[pj]])
            && segment_directions_oriented(xe,xs,Xc[pj],Xc[next[pj]])))
        break;
    }
    swap(PI(i),PI(j));
    // Remove PI(i) from the list
    const auto pi = PI(i);
    next[prev[pi]] = next[pi];
    prev[next[pi]] = prev[pi];
  }

  // Add the first triangle
  mesh->add_face(vec(VertexId(0),PI(1),VertexId(m-1)));

  // Add remaining triangles, flipping to ensure Delaunay
  const Field<bool,VertexId> marked(m);
  Array<HalfedgeId> fan;
  bool used_chew = false;
  for (int i=2;i<m-1;i++) {
    const auto pi = PI(i);
    insert_cavity_vertex(mesh,Xc,marked,pi,next[pi],prev[pi]);
    if (marked[pi]) {
      used_chew = true;
      marked[pi] = false;
      // Retriangulate the fans of triangles that have all three vertices marked
      auto e = mesh->reverse(mesh->halfedge(pi));
      auto v = mesh->src(e);
      bool mv = marked[v];
      marked[v] = false;
      fan.clear();
      do {
        const auto h = mesh->prev(e);
        e = mesh->reverse(mesh->next(e));
        v = mesh->src(e);
        const bool mv2 = marked[v];
        marked[v] = false;
        if (mv) {
          if (mv2)
            fan.append(h);
          if (!mv2 || mesh->is_boundary(e)) {
            chew_fan(mesh,Xc,pi,fan,random);
            fan.clear();
          }
        }
        mv = mv2;
      } while (!mesh->is_boundary(e));
    }
  }
  #undef PI

  // If we ran Chew's algorithm, validate the output.  I haven't tested this
  // case enough to be confident of its correctness.
  if (used_chew)
    assert_delaunay("Failure in extreme special case.  If this triggers, please email [email protected]: ",
                    mesh,Xc,Tuple<>(),false,false);

  // Copy triangles from temporary mesh to real mesh
  for (const auto f : mesh->faces()) {
    const auto v = mesh->vertices(f);
    parent_mesh.add_face(vec(cavity[v.x.id],cavity[v.y.id],cavity[v.z.id]));
  }
}
Пример #21
0
// Delaunay retriangulate a triangle fan
static void chew_fan(MutableTriangleTopology& parent_mesh, RawField<const Perturbed2,VertexId> X,
                     const VertexId u, RawArray<HalfedgeId> fan, Random& random) {
  chew_fan_count_ += 1;
#ifndef NDEBUG
  for (const auto e : fan)
    assert(parent_mesh.opposite(e)==u);
  for (int i=0;i<fan.size()-1;i++)
    GEODE_ASSERT(parent_mesh.src(fan[i])==parent_mesh.dst(fan[i+1]));
#endif
  const int n = fan.size();
  if (n < 2)
    return;
  chew_fan_count_ += 1024*n;

  // Collect vertices
  const Field<VertexId,VertexId> vertices(n+2,uninit);
  vertices.flat[0] = u;
  vertices.flat[1] = parent_mesh.src(fan[n-1]);
  for (int i=0;i<n;i++)
    vertices.flat[i+2] = parent_mesh.dst(fan[n-1-i]);

  // Delete original vertices
  for (const auto e : fan)
    parent_mesh.erase(parent_mesh.face(e));

  // Make the vertices into a doubly linked list
  const Field<VertexId,VertexId> prev(n+2,uninit),
                                 next(n+2,uninit);
  prev.flat[0].id = n+1;
  next.flat[n+1].id = 0;
  for (int i=0;i<n+1;i++) {
    prev.flat[i+1].id = i;
    next.flat[i].id = i+1;
  }

  // Randomly shuffle the vertices, then pulling elements off the linked list in reverse order of our final shuffle.
  const Array<VertexId> pi(n+2,uninit);
  for (int i=0;i<n+2;i++)
    pi[i].id = i;
  random.shuffle(pi);
  for (int i=n+1;i>=0;i--) {
    const auto j = pi[i];
    prev[next[j]] = prev[j];
    next[prev[j]] = next[j];
  }

  // Make a new singleton mesh
  const auto mesh = new_<MutableTriangleTopology>();
  mesh->add_vertices(n+2);
  small_sort(pi[0],pi[1],pi[2]);
  mesh->add_face(vec(pi[0],pi[1],pi[2]));

  // Insert remaining vertices
  Array<HalfedgeId> work;
  for (int i=3;i<n+2;i++) {
    const auto j = pi[i];
    const auto f = mesh->add_face(vec(j,next[j],prev[j]));
    work.append(mesh->reverse(mesh->opposite(f,j)));
    while (work.size()) {
      auto e = work.pop();
      if (   !mesh->is_boundary(e)
          && incircle(X[vertices[mesh->src(e)]],
                      X[vertices[mesh->dst(e)]],
                      X[vertices[mesh->opposite(e)]],
                      X[vertices[mesh->opposite(mesh->reverse(e))]])) {
        work.append(mesh->reverse(mesh->next(e)));
        work.append(mesh->reverse(mesh->prev(e)));
        e = mesh->unsafe_flip_edge(e);
      }
    }
  }

  // Copy triangles back to parent
  for (const auto f : mesh->faces()) {
    const auto vs = mesh->vertices(f);
    parent_mesh.add_face(vec(vertices[vs.x],vertices[vs.y],vertices[vs.z]));
  }
}