# Path: cloudvolume/exceptions.py
# class MeshDecodeError(ValueError):
#   """Unable to decode a mesh object."""
#   pass
# 
# Path: cloudvolume/lib.py
# def yellow(text):
#   return colorize('yellow', text)
# 
# class Vec(np.ndarray):
#     def __new__(cls, *args, **kwargs):
#       if 'dtype' in kwargs:
#         dtype = kwargs['dtype']
#       elif floating(args):
#         dtype = float
#       else:
#         dtype = int
# 
#       return super(Vec, cls).__new__(cls, shape=(len(args),), buffer=np.array(args).astype(dtype), dtype=dtype)
# 
#     @classmethod
#     def clamp(cls, val, minvec, maxvec):
#       x = np.minimum.reduce([
#         np.maximum.reduce([val,minvec]), 
#         maxvec
#       ])
#       return Vec(*x)
# 
#     def clone(self):
#       return Vec(*self[:], dtype=self.dtype)
# 
#     def null(self):
#         return self.length() <= 10 * np.finfo(np.float32).eps
# 
#     def dot(self, vec):
#       return sum(self * vec)
# 
#     def length2(self):
#         return self.dot(self)
# 
#     def length(self):
#         return math.sqrt(self.dot(self))
# 
#     def rectVolume(self):
#         return reduce(operator.mul, self)
# 
#     def __hash__(self):
#       return int(''.join(map(str, self)))
# 
#     def __repr__(self):
#       values = ",".join([ str(x) for x in self ])
#       return f"Vec({values}, dtype={self.dtype})"


import copy
import re
import struct
import numpy as np
    import DracoPy
      import vtk
from .exceptions import MeshDecodeError
from .lib import yellow, Vec
  def deduplicate_vertices(self, is_chunk_aligned):
    faces = self.faces
    verts = self.vertices
    # find all vertices that have exactly 2 duplicates
    unique_vertices, unique_inverse, counts = np.unique(
      verts, return_inverse=True, return_counts=True, axis=0
    )
    
    only_double = np.where(counts == 2)[0]
    is_doubled = np.isin(unique_inverse, only_double)
    # this stores whether each vertex should be merged or not
    do_merge = np.array(is_doubled & is_chunk_aligned)

    # setup an artificial 4th coordinate for vertex positions
    # which will be unique in general, 
    # but then repeated for those that are merged
    new_vertices = np.hstack((verts, np.arange(verts.shape[0])[:, np.newaxis]))
    new_vertices[do_merge, 3] = -1
  
    faces = faces.flatten()

    # use unique to make the artificial vertex list unique and reindex faces
    vertices, newfaces = np.unique(new_vertices[faces], return_inverse=True, axis=0)
    newfaces = newfaces.astype(np.uint32).reshape( (len(newfaces) // 3, 3) )

    return Mesh(vertices[:,0:3], newfaces, None, segid=self.segid, 
      encoding_type=self.encoding_type, encoding_options=self.encoding_options
    )

  def deduplicate_chunk_boundaries(self, chunk_size, is_draco=False, draco_grid_size=None, offset=(0,0,0)):

# Path: cloudvolume/exceptions.py
# class MeshDecodeError(ValueError):
#   """Unable to decode a mesh object."""
#   pass
# 
# Path: cloudvolume/lib.py
# def yellow(text):
#   return colorize('yellow', text)
# 
# class Vec(np.ndarray):
#     def __new__(cls, *args, **kwargs):
#       if 'dtype' in kwargs:
#         dtype = kwargs['dtype']
#       elif floating(args):
#         dtype = float
#       else:
#         dtype = int
# 
#       return super(Vec, cls).__new__(cls, shape=(len(args),), buffer=np.array(args).astype(dtype), dtype=dtype)
# 
#     @classmethod
#     def clamp(cls, val, minvec, maxvec):
#       x = np.minimum.reduce([
#         np.maximum.reduce([val,minvec]), 
#         maxvec
#       ])
#       return Vec(*x)
# 
#     def clone(self):
#       return Vec(*self[:], dtype=self.dtype)
# 
#     def null(self):
#         return self.length() <= 10 * np.finfo(np.float32).eps
# 
#     def dot(self, vec):
#       return sum(self * vec)
# 
#     def length2(self):
#         return self.dot(self)
# 
#     def length(self):
#         return math.sqrt(self.dot(self))
# 
#     def rectVolume(self):
#         return reduce(operator.mul, self)
# 
#     def __hash__(self):
#       return int(''.join(map(str, self)))
# 
#     def __repr__(self):
#       values = ",".join([ str(x) for x in self ])
#       return f"Vec({values}, dtype={self.dtype})"


# Path: cloudvolume/mesh.py
import copy
import re
import struct
import numpy as np
    import DracoPy
      import vtk
from .exceptions import MeshDecodeError
from .lib import yellow, Vec

  def deduplicate_vertices(self, is_chunk_aligned):
    faces = self.faces
    verts = self.vertices
    # find all vertices that have exactly 2 duplicates
    unique_vertices, unique_inverse, counts = np.unique(
      verts, return_inverse=True, return_counts=True, axis=0
    )
    
    only_double = np.where(counts == 2)[0]
    is_doubled = np.isin(unique_inverse, only_double)
    # this stores whether each vertex should be merged or not
    do_merge = np.array(is_doubled & is_chunk_aligned)

    # setup an artificial 4th coordinate for vertex positions
    # which will be unique in general, 
    # but then repeated for those that are merged
    new_vertices = np.hstack((verts, np.arange(verts.shape[0])[:, np.newaxis]))
    new_vertices[do_merge, 3] = -1
  
    faces = faces.flatten()

    # use unique to make the artificial vertex list unique and reindex faces
    vertices, newfaces = np.unique(new_vertices[faces], return_inverse=True, axis=0)
    newfaces = newfaces.astype(np.uint32).reshape( (len(newfaces) // 3, 3) )

    return Mesh(vertices[:,0:3], newfaces, None, segid=self.segid, 
      encoding_type=self.encoding_type, encoding_options=self.encoding_options
    )

  def deduplicate_chunk_boundaries(self, chunk_size, is_draco=False, draco_grid_size=None, offset=(0,0,0)):

    offset = Vec(*offset)