def test_lines(view_type, options_class): proj = steno3d.Project() lines = steno3d.Line( project=proj, title='my elem', description='my desc', mesh=steno3d.Mesh1D( vertices=[[i / 3., i / 4, i / 5] for i in range(10)], segments=[[i, i + 1] for i in range(9)], opts={'view_type': view_type}, ), opts={ 'color': 'red', 'opacity': 0.5, }, ) proj.validate() view = convert.steno3d_to_view(proj) view.validate() assert len(view.contents) == 3 lineset = find(view, spatial.ElementLineSet) defaults = lineset.defaults.serialize() assert defaults['__class__'] == options_class assert defaults['color']['value'] == '#FF0000' assert defaults['opacity']['value'] == 0.5 assert lineset.name == 'my elem' assert lineset.description == 'my desc'
def test_texture(): try: dirname, _ = os.path.split(os.path.abspath(__file__)) png_file = os.path.sep.join(dirname.split(os.path.sep) + ['temp.png']) s = ['110010010011', '101011010100', '110010110101', '100010010011'] s = [[int(v) for v in val] for val in s] f = open(png_file, 'wb') w = png.Writer(len(s[0]), len(s), greyscale=True, bitdepth=16) w.write(f, s) f.close() proj = steno3d.Project() surf = steno3d.Surface( project=proj, mesh=steno3d.Mesh2DGrid( h1=[1., 1., 1., 1., 1.], h2=[1., 1., 1., 1., 1.], ), textures=[ steno3d.Texture2DImage( O=[0., 0, 0], U=[5., 0, 0], V=[0., 5, 0], image=png_file, ) ], ) proj.validate() view = convert.steno3d_to_view(proj) view.validate() assert len(view.contents) == 3 finally: os.remove(png_file)
def plot3D_steno(self, block, project, plot=True, **kwargs): import steno3d import numpy as np steno3d.login() description = kwargs.get('description', 'Nothing') proj = steno3d.Project( title=project, description=description, public=True, ) mesh = steno3d.Mesh3DGrid(h1=np.ones(self._data.resolution[0]) * (self._data.extent[0] - self._data.extent[1]) / (self._data.resolution[0] - 1), h2=np.ones(self._data.resolution[1]) * (self._data.extent[2] - self._data.extent[3]) / (self._data.resolution[1] - 1), h3=np.ones(self._data.resolution[2]) * (self._data.extent[4] - self._data.extent[5]) / (self._data.resolution[2] - 1)) data = steno3d.DataArray( title='Lithologies', array=block) vol = steno3d.Volume(project=proj, mesh=mesh, data=[dict(location='CC', data=data)]) vol.upload() if plot: return vol.plot()
def test_volume_grid(): proj = steno3d.Project() vol = steno3d.Volume( project=proj, title='my elem', description='my desc', mesh=steno3d.Mesh3DGrid( h1=[1., 1., 1., 1., 1.], h2=[1., 1., 1., 1., 1.], h3=[1., 1., 1., 1., 1.], opts={'wireframe': True}, ), opts={ 'color': 'red', 'opacity': 0.5, }, ) proj.validate() view = convert.steno3d_to_view(proj) view.validate() assert len(view.contents) == 1 vol = find(view, spatial.ElementVolumeGrid) defaults = vol.defaults.serialize() assert defaults['__class__'] == 'OptionsBlockModel' assert defaults['color']['value'] == '#FF0000' assert defaults['opacity']['value'] == 0.5 assert defaults['wireframe']['active'] assert vol.name == 'my elem' assert vol.description == 'my desc'
def test_project(): proj = steno3d.Project( title='my proj', description='my desc', ) proj.validate() view = convert.steno3d_to_view(proj) view.validate() assert view.name == 'my proj' assert view.description == 'my desc' assert view.contents == []
def test_surface_mesh2dgrid(self): myh1 = [5., 4., 3., 2., 1., 1., 1., 1., 2., 3., 4., 5.] myh2 = [1., 1., 1., 1., 2., 3., 4., 5.] # def f(): P = steno3d.Project() S = steno3d.Surface( P, mesh=dict(h1=myh1, h2=myh2, opts={"wireframe": True}), opts={ "opacity": 0.3, "color": "red" }, ) # self.assertRaises(KeyError, f) # This should be ok P = steno3d.Project() M = steno3d.Mesh2DGrid(h1=myh1, h2=myh2, opts={"wireframe": True}) S = steno3d.Surface(P, mesh=M, opts={"opacity": 0.3, "color": "red"}) S.validate() myZ = np.random.rand(10) S.mesh.Z = myZ self.assertRaises(ValueError, lambda: S.validate()) myZ = np.random.rand((len(myh1) + 1) * (len(myh2) + 1)) S.mesh.Z = myZ S.validate() self.assertRaises(ValueError, setattr(S, 'x0', [[0., 0., 0.], [1., 1., 1.]])) assert len(S._dirty) == 3 S._mark_clean() assert len(S._dirty) == 0
def test_teapot(self): teapot = Teapot.fetch_data(filename='teapot.json', verbose=False, directory='.') with open(teapot) as f: data = json.loads(f.read()) P = steno3d.Project() S = steno3d.Surface( project=P, mesh=dict(vertices=data['vertices'], triangles=data['triangles'], opts={"wireframe": True}), opts={ "opacity": 0.3, "color": "red" }, ) S.validate()
def test_surfaces(): proj = steno3d.Project() surf_0 = steno3d.Surface( project=proj, title='my elem', description='my desc', mesh=steno3d.Mesh2D( vertices=[[i / 3., i / 4, i / 5] for i in range(10)], triangles=[[i, i + 1, i + 2] for i in range(8)], opts={'wireframe': True}, ), opts={ 'color': 'red', 'opacity': 0.5, }, ) surf_1 = steno3d.Surface( project=proj, title='my elem', description='my desc', mesh=steno3d.Mesh2DGrid( h1=[1., 1., 1., 1., 1.], h2=[1., 1., 1., 1., 1.], Z=[1.] * 36, opts={'wireframe': True}, ), opts={ 'color': 'red', 'opacity': 0.5, }, ) proj.validate() view = convert.steno3d_to_view(proj) view.validate() assert len(view.contents) == 5 for cls in (spatial.ElementSurface, spatial.ElementSurfaceGrid): surface = find(view, cls) defaults = surface.defaults.serialize() assert defaults['__class__'] == 'OptionsSurface' assert defaults['color']['value'] == '#FF0000' assert defaults['opacity']['value'] == 0.5 assert defaults['wireframe']['active'] assert surface.name == 'my elem' assert surface.description == 'my desc'
def test_proj_resource_link(self): t = np.array([[0, 1, 2], [1, 2, 3], [0, 1, 4], [0, 2, 4], [1, 3, 4], [2, 3, 4]]) v = np.array([[0, 0, 0], [0, 0, -1], [0, 1, 0], [0, 1, 1], [1, .5, .5]]) m = steno3d.Mesh2D(triangles=t, vertices=v) p0 = steno3d.Project() p1 = steno3d.Project() p2 = steno3d.Project() s0 = steno3d.Surface(p0, mesh=m) assert len(s0.project) == 1 assert len(p0.resources) == 1 s0.project = [p0, p0, p2, p1, p2] assert len(s0.project) == 3, '{}'.format(s0.project) assert s0.project[0] is p0 assert s0.project[1] is p2 assert s0.project[2] is p1 assert len(p0.resources) == 1 assert len(p1.resources) == 1 assert len(p2.resources) == 1 s1 = steno3d.Surface(p1, mesh=m) assert len(s1.project) == 1 assert len(p1.resources) == 2 s2 = steno3d.Surface(p1, mesh=m) assert len(p1.resources) == 3 s2.project += [p1] assert len(p1.resources) == 3 assert len(s2.project) == 1 p2.resources += [s0, s1, s1, s1] assert len(p2.resources) == 2 assert p2.resources[0] is s0 assert p2.resources[1] is s1 assert len(s0.project) == 3 assert len(s1.project) == 2 p0.validate() p1.validate() p2.validate() p3 = steno3d.Project() p4 = steno3d.Project() p5 = steno3d.Project() p3.resources = [s0, s1, s2] p4.resources = p4.resources + p3.resources p5.resources += p3.resources p3.validate() p4.validate() p5.validate()
def test_ascii(self): ascii_file = path.sep.join(self.assets + ['ascii.stl']) parser = steno3d.parsers.stl(ascii_file) projs = parser.parse() assert len(projs) == 1 proj, = projs assert len(proj.resources) == 1 assert isinstance(proj.resources[0], steno3d.Surface) assert proj.resources[0].mesh.nN == 4 assert proj.resources[0].mesh.nC == 4 parser = steno3d.parsers.AllParsers(ascii_file) projs = parser.parse() assert len(projs) == 1 proj, = projs assert len(proj.resources) == 1 assert isinstance(proj.resources[0], steno3d.Surface) assert proj.resources[0].mesh.nN == 4 assert proj.resources[0].mesh.nC == 4 proj = steno3d.Project() parser.parse(proj) assert len(proj.resources) == 1
def to_steno3d(self, project=None, as_topo=True): """Create a project from GridInfo Optional input: project: Preexisting Steno3D project to add .grd file components to. If not provided, a new project will be created. verbose: Print messages and warnings during file parsing. (default: True) as_topo: If True, add data from grid file as topography. Otherwise only add the data as color on a flat surface. (default: True) Output: Steno3D Project with GridInfo components """ self.validate() if project is None: project = steno3d.Project(description='From surfer grid file') elif not isinstance(project, steno3d.Project): raise steno3d.parsers.ParseError('project must be Steno3D Project') surf = steno3d.Surface( project=project, mesh=steno3d.Mesh2DGrid(O=[self.xll, self.yll, 0], h1=np.ones(self.ncol - 1) * self.xsize, h2=np.ones(self.nrow - 1) * self.ysize), data=[ dict(location='N', data=steno3d.DataArray(array=self.data.flatten())) ]) if as_topo: surf.mesh.Z = self.data.flatten() return project
def test_points(): proj = steno3d.Project() pts = steno3d.Point( project=proj, title='my elem', description='my desc', mesh=steno3d.Mesh0D(vertices=[[i / 3., i / 4, i / 5] for i in range(10)], ), opts={ 'color': 'red', 'opacity': 0.5, }, ) proj.validate() view = convert.steno3d_to_view(proj) view.validate() assert len(view.contents) == 2 pointset = find(view, spatial.ElementPointSet) defaults = pointset.defaults.serialize() assert defaults['__class__'] == 'OptionsPoints' assert defaults['color']['value'] == '#FF0000' assert defaults['opacity']['value'] == 0.5 assert pointset.name == 'my elem' assert pointset.description == 'my desc'
def parse(self, project=None, verbose=True): """function parse Parses the .stl file (binary or ASCII) provided at parser instantiation into a Steno3D project. Optional input: project: Preexisting Steno3D project to add .stl file components to. If not provided, a new project will be created. verbose: Print messages and warnings during file parsing. (default: True) Output: tuple containing one Steno3D project with components parsed from the .stl file """ warnings = set() if project is None: project = steno3d.Project( description='Project imported from ' + self.file_name ) elif not isinstance(project, steno3d.Project): raise steno3d.parsers.ParseError('Only allowed input for parse is ' 'optional Steno3D project') f = open(self.file_name, 'rb') first_line = f.readline().split() f.close() if len(first_line) > 0 and first_line[0] == b'solid': (tris, verts, title) = self._parse_ascii(verbose, warnings) else: (tris, verts) = self._parse_binary(verbose, warnings) title = '' if len(tris) == 0 or len(verts) == 0: raise steno3d.parsers.ParseError( 'Invalid file. No triangles or vertices extracted.' ) if np.min(tris) < 0 or np.max(tris) >= len(verts): raise steno3d.parsers.ParseError( 'Invalid surface geometry encountered in parsed file. ' 'Triangle indices must be between 0 and ' '{}'.format(len(verts)-1) ) steno3d.Surface( project=project, title=title, mesh=steno3d.Mesh2D( vertices=verts, triangles=tris ) ) if verbose and len(warnings) > 0: print(' If you are interested in contributing to unsupported ' 'features, please visit\n' ' https://github.com/seequent/steno3d-stl') return (project,)
def test_data(): arr = [float(val) for val in range(25)] arr_int = [int(val % 4) for val in range(25)] proj = steno3d.Project(title='Mappings proj') surf = steno3d.Surface( project=proj, mesh=steno3d.Mesh2DGrid( h1=[1., 1., 1., 1., 1.], h2=[1., 1., 1., 1., 1.], ), data=[ { 'location': 'CC', 'data': steno3d.DataArray( array=arr, colormap=[ 'red', 'blue', 'black', 'orange', 'black', 'yellow', ], ) }, { 'location': 'CC', 'data': steno3d.DataDiscrete(array=arr, colormap=['red', 'blue', 'green'], end_values=[10., 15]) }, { 'location': 'CC', 'data': steno3d.DataCategory(array=arr_int, colormap=[ 'yellow', 'black', 'brown', 'green', ], categories=[ 'yellow!', 'black!!', 'brown!!!', 'green!!!!', ]) }, ], ) proj.validate() view = convert.steno3d_to_view(proj) view.validate() assert len(view.contents) == 12
def test_surface(self): P = steno3d.Project() myVerts = np.array([[0., 0, 0], [0, 1, 0], [1, 0, 0]]) myTriangles = np.array([[0, 1, 2]]) S = steno3d.Surface( project=P, mesh=dict(vertices=myVerts, triangles=myTriangles, opts={"wireframe": True}), opts={ "opacity": 0.3, "color": "red" }, ) S.validate() # Make sure getattr/setattr working ok assert S.mesh is S.mesh assert S.mesh.vertices is S.mesh.vertices assert S.mesh.triangles is S.mesh.triangles # Make sure the shortcuts are working # assert S.vertices is S.mesh.vertices # assert S.triangles is S.mesh.triangles # assert S.vertices is not myVerts # assert S.triangles is not myTriangles assert np.array_equal(S.mesh.vertices, myVerts) assert np.array_equal(S.mesh.triangles, myTriangles) # Test options # ----Surface # --------Color assert S.opts.color == (255, 0, 0) S.opts.color = 'darkred' assert S.opts.opacity == 0.3 S.opts.opacity = .1 assert S.opts.opacity == .1 S.opts = {"opacity": 0.5} assert S.opts.opacity == .5 S.opts = {"color": '#FFF'} assert S.opts.opacity == 1 assert S.mesh.opts.wireframe self.assertRaises(ValueError, lambda: setattr(S.mesh.opts, 'wireframe', 'Wires')) self.assertRaises(ValueError, lambda: setattr(S.mesh.opts, 'wireframe', 1)) S.mesh.opts = {"wireframe": False} assert not S.mesh.opts.wireframe S.mesh.opts.wireframe = True assert S.mesh.opts.wireframe # Test triangles S.triangles = [[0, 0, 0], [1, 1, 1]] assert isinstance(S.mesh.triangles, np.ndarray) self.assertRaises(ValueError, lambda: setattr(S.mesh, 'triangles', -1)) self.assertRaises(ValueError, lambda: setattr(S.mesh, 'triangles', [[0, 0]])) self.assertRaises( ValueError, lambda: setattr(S.mesh, 'triangles', [[0, 0, .5], [1, 1, 1]])) self.assertRaises( ValueError, lambda: setattr(S.mesh, 'triangles', [[0], [0], [0]])) self.assertRaises( ValueError, lambda: setattr(S.mesh, 'triangles', 'Three isosceles, please!')) self.assertRaises( ValueError, lambda: setattr(S.mesh, 'triangles', [[0, 0, 1, 1], [0, 0, 0, 0]])) S.mesh.triangles = [[0, 0, -100], [1, 1, 1]] self.assertRaises(ValueError, lambda: S.validate()) S.mesh._p_triangles = -1 # try to fake things out. # ensure we also call the validator self.assertRaises(ValueError, lambda: S.validate()) myNewTriangles = np.array([[0, 1, 2], [1, 2, 3], [0, 1, 4], [0, 2, 4], [1, 3, 4], [2, 3, 4]]) S.mesh.triangles = myNewTriangles self.assertRaises(ValueError, lambda: S.validate()) assert np.array_equal(S.mesh.triangles, myNewTriangles) # Test vertices myNewVerts = np.array([[0, 0, 0], [0, 0, -1], [0, 1, 0], [0, 1, 1], [1, .5, .5]]) S.mesh.vertices = myNewVerts self.assertRaises(ValueError, lambda: setattr(S.mesh, 'vertices', -1)) self.assertRaises(ValueError, lambda: setattr(S.mesh, 'vertices', [[0, 0]])) self.assertRaises(ValueError, lambda: setattr(S.mesh, 'vertices', [[0], [0], [0]])) self.assertRaises( ValueError, lambda: setattr(S.mesh, 'vertices', 'Just a few random points.')) self.assertRaises( ValueError, lambda: setattr(S.mesh, 'vertices', [[0, 0, 1, 1], [0, 0, 0, 0]])) # Other Constructor tests S = steno3d.Surface(P) self.assertRaises(ValueError, lambda: S.validate()) S.mesh = S.mesh self.assertRaises(ValueError, lambda: S.validate()) S.mesh.vertices = myVerts S.mesh.triangles = myTriangles S.validate() MOpts = dict(wireframe=True) M = steno3d.Mesh2D(vertices=myNewVerts, triangles=myNewTriangles, opts=MOpts) SOpts = dict(opacity=.5, color=[100, 100, 100]) S = steno3d.Surface(P, mesh=M, opts=SOpts) assert S.mesh.vertices is not myNewVerts assert S.mesh.triangles is not myNewTriangles assert np.array_equal(S.mesh.vertices, myNewVerts) assert np.array_equal(S.mesh.triangles, myNewTriangles) assert S.opts.opacity == .5 assert S.opts.color == (100, 100, 100) assert S.mesh.opts.wireframe # Test Data # This needs to be updated once data is updated S.mesh.triangles = myTriangles S.mesh.vertices = myVerts S.data = {'data': [0], 'location': 'face'} assert type(S.data) is list S.data = [{'data': [0], 'location': 'face'}] assert type(S.data) is list d0 = S.data[0] # copying the list and appending to it (not the same list!) S.data.append({'data': [2], 'location': 'vertex'}) assert len(S.data) == 1 # iadd is resetting the list and doing validation S.data += [{'data': [2], 'location': 'vertex'}] assert S.data[0] is d0 from steno3d.surface import _SurfaceBinder assert isinstance(S.data[1], _SurfaceBinder) # surface.data[1] is the incorrect length self.assertRaises(Exception, lambda: S.validate()) S.data[1].data = [0, 1, 2] assert S.data[1].data.title == '' assert S.data[1].data.description == '' assert np.all(S.data[1].data.array == [0, 1, 2]) S.validate()
def parse(self, project=None, verbose=True): """function parse Parses the .obj file provided at parser instantiation into a Steno3D project. Optional input: project: Preexisting Steno3D project to add .obj file components to. If not provided, a new project will be created. verbose: Print messages and warnings during file parsing. (default: True) Output: tuple containing one Steno3D project with components parsed from the .obj file """ if project is None: project = steno3d.Project(description='Project imported from ' + self.file_name) elif not isinstance(project, steno3d.Project): raise steno3d.parsers.ParseError('Only allowed input for parse is ' 'optional Steno3D project') warnings = set() vertices = [] points = [] triangles = [] segments = [] with open(self.file_name, 'r') as f: if verbose: print('Parsing file: {}'.format(self.file_name)) ext_line = [] for line in f: try: line = ext_line + line.split() # Check for comment if len(line) == 0 or line[0] == '#': continue # Check for line continuation if line[-1] == '\\': ext_line = line[:-1] continue else: ext_line = [] # Vertex if line[0] == 'v': if len(line) == 5: self._warn('Unsupported feature: Vertex Weight', warnings, verbose) vertices.append([float(v) for v in line[1:4]]) continue # Surface if line[0] == 'f': tri = [int(v.split('/')[0]) for v in line[1:]] tri = [ v - 1 if v > 0 else v + len(vertices) for v in tri ] for i in range(len(tri) - 2): triangles.append([tri[0], tri[i + 1], tri[i + 2]]) continue # Line if line[0] == 'l': seg = [int(v.split('/')[0]) for v in line[1:]] seg = [ v - 1 if v > 0 else v + len(vertices) for v in seg ] for i in range(len(seg) - 1): segments.append([seg[i], seg[i + 1]]) continue # Point if line[0] == 'p': pnt = [int(v) for v in line[1:]] pnt = [ v - 1 if v > 0 else v + len(vertices) for v in pnt ] for p in pnt: points.append(vertices[p]) continue # Unsupported features for feat in self.unsupported: if line[0] in self.unsupported[feat]: self._warn('Unsupported feature: {}'.format(feat), warnings, verbose) break else: self._warn( 'Skipping unknown keyword: {}'.format(line[0]), warnings, verbose) except: raise steno3d.parsers.ParseError( 'Bad file line encountered while ' 'parsing {}:\n{}'.format(self.file_name, ' '.join(line))) if len(points) == 0 and len(segments) == 0 and len(triangles) == 0: raise steno3d.parsers.ParseError( 'No valid geometry extracted while parsing {}.\nPlease ' 'ensure this file has vertices (v) and points (p), lines (l), ' 'or faces (f).\nOther features are currently unsupported. If ' 'you would like to contribute, visit\n' 'https://github.com/seequent/steno3d-obj'.format( self.file_name)) if len(segments) > 0: if np.min(segments) < 0 or np.max(segments) >= len(vertices): raise steno3d.parsers.ParseError( 'Invalid line geometry encountered in parsed file. ' 'Segment indices must be between 0 and ' '{}'.format(len(vertices) - 1)) steno3d.Line(project=project, mesh=steno3d.Mesh1D(segments=segments, vertices=vertices)) if len(triangles) > 0: if np.min(triangles) < 0 or np.max(triangles) >= len(vertices): raise steno3d.parsers.ParseError( 'Invalid surface geometry encountered in parsed file. ' 'Triangle indices must be between 0 and ' '{}'.format(len(vertices) - 1)) steno3d.Surface(project=project, mesh=steno3d.Mesh2D(triangles=triangles, vertices=vertices)) if len(points) > 0: steno3d.Point(project=project, mesh=steno3d.Mesh0D(vertices=points)) if verbose and len(warnings) > 0: print(' If you are interested in contributing to unsupported ' 'features, please visit\n' ' https://github.com/seequent/steno3d-obj') return (project, )