Skip to content

Commit

Permalink
Added tests, reimplemented interface, support for writing ctm files
Browse files Browse the repository at this point in the history
  • Loading branch information
lejafar committed Aug 12, 2018
1 parent 04db758 commit 4f76882
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 45 deletions.
17 changes: 7 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Python-OpenCTM
==============
[![Build Status](https://travis-ci.org/lejafar/Python-OpenCTM.svg?branch=master)](https://travis-ci.org/lejafar/Python-OpenCTM)
### Python Interface for the Open-CTM Library
### Python Interface for the Open-CTM File Format

Python-OpenCTM is an (unofficial) Python interface for the [OpenCTM](https://github.com/Danny02/OpenCTM) file format. A format that allows a geometry to be compressed to a fraction of comparable file formats (3DS, STL, COLLADA...).

Expand All @@ -16,15 +16,12 @@ pip install python-openctm
```python
import openctm

with openctm.open('foo.ctm') as ctm_file:
vertices = ctm_file.get_vertices()
faces = ctm_file.get_faces()
# read
mesh = openctm.import_mesh('foo.ctm')

result = {'vertices': vertices,
'faces': faces}
print(mesh.vertices.shape)
# (124, 3)

# get face normals if available
face_normals = ctm_file.get_face_normals()
if face_normals:
result['face_normals'] = face_normals
# write
openctm.export_mesh(mesh, 'bar.ctm')
```
2 changes: 1 addition & 1 deletion openctm/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
from .io import open, CtmFile
from .io import import_mesh, export_mesh
90 changes: 61 additions & 29 deletions openctm/io.py
Original file line number Diff line number Diff line change
@@ -1,48 +1,80 @@
from .openctm import *
import numpy as np
from contextlib import contextmanager

@contextmanager
def open(filename):
ctm_context = ctmNewContext(CTM_IMPORT)
yield CtmFile(ctm_context, filename)
ctmFreeContext(ctm_context)

class CtmFile:
class CTM:

def __init__(self, _vertices, _faces, _normals=None):
self.vertices = _vertices
self.faces = _faces
self.normals = _normals

def __eq__(self, other):
return (self.vertices == other.vertices).all() and (self.faces == other.faces).all()


def __init__(self, ctm_context, filename):
self.ctm = ctm_context
ctmLoad(self.ctm, str(filename).encode('utf-8'))
err = ctmGetError(self.ctm)
def import_mesh(_filename):
ctm_context = ctmNewContext(CTM_IMPORT)
try:
ctmLoad(ctm_context, str(_filename).encode('utf-8'))
err = ctmGetError(ctm_context)
if err != CTM_NONE:
raise IOError("Error loading file: " + str(ctmErrorString(err)))
raise IOError("Error loading file: %s" % str(ctmErrorString(err)))

# read vertices
vertex_count = ctmGetInteger(ctm_context, CTM_VERTEX_COUNT)
vertex_ctm = ctmGetFloatArray(ctm_context, CTM_VERTICES)

def get_vertices(self):
# get vertices
vertex_count = ctmGetInteger(self.ctm, CTM_VERTEX_COUNT)
vertex_ctm = ctmGetFloatArray(self.ctm, CTM_VERTICES)
# use fromiter to avoid loop
vertices = np.fromiter(vertex_ctm,
dtype=np.float,
count=vertex_count * 3).reshape((-1, 3))
return vertices

def get_faces(self):
# get faces
face_count = ctmGetInteger(self.ctm, CTM_TRIANGLE_COUNT)
face_ctm = ctmGetIntegerArray(self.ctm, CTM_INDICES)
# read faces
face_count = ctmGetInteger(ctm_context, CTM_TRIANGLE_COUNT)
face_ctm = ctmGetIntegerArray(ctm_context, CTM_INDICES)
faces = np.fromiter(face_ctm,
dtype=np.int,
count=face_count * 3).reshape((-1, 3))
return faces

def get_face_normals(self):
if ctmGetInteger(self.ctm, CTM_HAS_NORMALS) == CTM_TRUE:
normals_ctm = ctmGetFloatArray(self.ctm, CTM_NORMALS)
# read face normals
normals = None
if ctmGetInteger(ctm_context, CTM_HAS_NORMALS) == CTM_TRUE:
normals_ctm = ctmGetFloatArray(ctm_context, CTM_NORMALS)
normals = np.fromiter(normals_ctm,
dtype=np.float,
count=face_count * 3).reshape((-1, 3))
return normals
# if not available return None
finally:
ctmFreeContext(ctm_context)

return CTM(vertices, faces, normals)


def export_mesh(_ctm, _filename):
ctm_context = ctmNewContext(CTM_EXPORT)

try:
vertex_count = len(_ctm.vertices)
vertices = _ctm.vertices.reshape((-1, 1))
p_vertices = ctypes.cast((CTMfloat * vertex_count * 3)(), ctypes.POINTER(CTMfloat))
for i in range(vertex_count * 3):
p_vertices[i] = CTMfloat(vertices[i])

face_count = len(_ctm.faces)
faces = _ctm.faces.reshape((-1, 1))
p_faces = ctypes.cast((CTMuint * face_count * 3)(), ctypes.POINTER(CTMuint))
for i in range(face_count * 3):
p_faces[i] = CTMuint(faces[i])

if _ctm.normals:
normal_count = len(_ctm.normals)
normals = _ctm.normals.reshape((-1, 1))
p_normals = ctypes.cast((CTMfloat * normal_count * 3)(), ctypes.POINTER(CTMfloat))
for i in range(normal_count * 3):
p_normals[i] = CTMfloat(normals[i])
else:
return None
p_normals = None

ctmDefineMesh(ctm_context, p_vertices, CTMuint(vertex_count), p_faces, CTMuint(face_count), p_normals)
ctmSave(ctm_context, ctypes.c_char_p(_filename.encode('utf-8')))
finally:
ctmFreeContext(ctm_context)
10 changes: 5 additions & 5 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
from setuptools import setup, Distribution, Extension
import sys
from setuptools import setup, Extension

setup(
name='python-openctm',
version='1.0.2',
description='Python Interface for the OpenCTM Library',
long_description='Python Interface for the OpenCTM Library',
version='1.0.3',
description='Python Interface for the OpenCTM File Format',
long_description='Python Interface for the OpenCTM File Format',
url='https://github.com/lejafar/python-openctm',
author='Rafael Hautekiet',
author_email='[email protected]',
Expand All @@ -24,6 +23,7 @@
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Natural Language :: English",
"Topic :: Scientific/Engineering",
"License :: OSI Approved :: zlib/libpng License",
Expand Down
Empty file added tests/__init__.py
Empty file.
File renamed without changes.
Binary file added tests/test-data/vierkanten_test_exported.ctm
Binary file not shown.
31 changes: 31 additions & 0 deletions tests/test_basic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import unittest
import os

from openctm import import_mesh, export_mesh


class BasicTestSuiteFunctions(unittest.TestCase):

def setUp(self):
self.file_dir = "tests/test-data/"

def testImportVertices(self):
mesh = import_mesh("%s/squares.ctm" % self.file_dir)
assert len(mesh.vertices) == 124

def testImportFaces(self):
mesh = import_mesh("%s/squares.ctm" % self.file_dir)
assert len(mesh.faces) == 284

def testExportImport(self):
original_mesh = import_mesh("%s/squares.ctm" % self.file_dir)
export_mesh(original_mesh, "%s/squares_exported.ctm" % self.file_dir)

exported_mesh = import_mesh("%s/squares_exported.ctm" % self.file_dir)

assert original_mesh == exported_mesh
os.remove("%s/squares_exported.ctm" % self.file_dir)


if __name__ == '__main__':
unittest.main()

0 comments on commit 4f76882

Please sign in to comment.